Minor fixes for XHTML compliance
[pfb-moodle.git] / lib / weblib.php
blobcef75867dc3e4d786674bd2d143510b0c259df35
1 <?php // $Id$
3 ///////////////////////////////////////////////////////////////////////////
4 // //
5 // NOTICE OF COPYRIGHT //
6 // //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
9 // //
10 // Copyright (C) 2001-2003 Martin Dougiamas http://dougiamas.com //
11 // //
12 // This program is free software; you can redistribute it and/or modify //
13 // it under the terms of the GNU General Public License as published by //
14 // the Free Software Foundation; either version 2 of the License, or //
15 // (at your option) any later version. //
16 // //
17 // This program is distributed in the hope that it will be useful, //
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
20 // GNU General Public License for more details: //
21 // //
22 // http://www.gnu.org/copyleft/gpl.html //
23 // //
24 ///////////////////////////////////////////////////////////////////////////
26 /**
27 * Library of functions for web output
29 * Library of all general-purpose Moodle PHP functions and constants
30 * that produce HTML output
32 * Other main libraries:
33 * - datalib.php - functions that access the database.
34 * - moodlelib.php - general-purpose Moodle functions.
35 * @author Martin Dougiamas
36 * @version $Id$
37 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
38 * @package moodlecore
41 /// We are going to uses filterlib functions here
42 require_once("$CFG->libdir/filterlib.php");
44 require_once("$CFG->libdir/ajax/ajaxlib.php");
46 /// Constants
48 /// Define text formatting types ... eventually we can add Wiki, BBcode etc
50 /**
51 * Does all sorts of transformations and filtering
53 define('FORMAT_MOODLE', '0'); // Does all sorts of transformations and filtering
55 /**
56 * Plain HTML (with some tags stripped)
58 define('FORMAT_HTML', '1'); // Plain HTML (with some tags stripped)
60 /**
61 * Plain text (even tags are printed in full)
63 define('FORMAT_PLAIN', '2'); // Plain text (even tags are printed in full)
65 /**
66 * Wiki-formatted text
67 * Deprecated: left here just to note that '3' is not used (at the moment)
68 * and to catch any latent wiki-like text (which generates an error)
70 define('FORMAT_WIKI', '3'); // Wiki-formatted text
72 /**
73 * Markdown-formatted text http://daringfireball.net/projects/markdown/
75 define('FORMAT_MARKDOWN', '4'); // Markdown-formatted text http://daringfireball.net/projects/markdown/
77 /**
78 * TRUSTTEXT marker - if present in text, text cleaning should be bypassed
80 define('TRUSTTEXT', '#####TRUSTTEXT#####');
83 /**
84 * Allowed tags - string of html tags that can be tested against for safe html tags
85 * @global string $ALLOWED_TAGS
87 $ALLOWED_TAGS =
88 '<p><br><b><i><u><font><table><tbody><span><div><tr><td><th><ol><ul><dl><li><dt><dd><h1><h2><h3><h4><h5><h6><hr><img><a><strong><emphasis><em><sup><sub><address><cite><blockquote><pre><strike><param><acronym><nolink><lang><tex><algebra><math><mi><mn><mo><mtext><mspace><ms><mrow><mfrac><msqrt><mroot><mstyle><merror><mpadded><mphantom><mfenced><msub><msup><msubsup><munder><mover><munderover><mmultiscripts><mtable><mtr><mtd><maligngroup><malignmark><maction><cn><ci><apply><reln><fn><interval><inverse><sep><condition><declare><lambda><compose><ident><quotient><exp><factorial><divide><max><min><minus><plus><power><rem><times><root><gcd><and><or><xor><not><implies><forall><exists><abs><conjugate><eq><neq><gt><lt><geq><leq><ln><log><int><diff><partialdiff><lowlimit><uplimit><bvar><degree><set><list><union><intersect><in><notin><subset><prsubset><notsubset><notprsubset><setdiff><sum><product><limit><tendsto><mean><sdev><variance><median><mode><moment><vector><matrix><matrixrow><determinant><transpose><selector><annotation><semantics><annotation-xml><tt><code>';
90 /**
91 * Allowed protocols - array of protocols that are safe to use in links and so on
92 * @global string $ALLOWED_PROTOCOLS
94 $ALLOWED_PROTOCOLS = array('http', 'https', 'ftp', 'news', 'mailto', 'rtsp', 'teamspeak', 'gopher', 'mms',
95 'color', 'callto', 'cursor', 'text-align', 'font-size', 'font-weight', 'font-style',
96 'border', 'margin', 'padding', 'background'); // CSS as well to get through kses
99 /// Functions
102 * Add quotes to HTML characters
104 * Returns $var with HTML characters (like "<", ">", etc.) properly quoted.
105 * This function is very similar to {@link p()}
107 * @param string $var the string potentially containing HTML characters
108 * @param boolean $strip to decide if we want to strip slashes or no. Default to false.
109 * true should be used to print data from forms and false for data from DB.
110 * @return string
112 function s($var, $strip=false) {
114 if ($var == '0') { // for integer 0, boolean false, string '0'
115 return '0';
118 if ($strip) {
119 return preg_replace("/&amp;(#\d+);/i", "&$1;", htmlspecialchars(stripslashes_safe($var)));
120 } else {
121 return preg_replace("/&amp;(#\d+);/i", "&$1;", htmlspecialchars($var));
126 * Add quotes to HTML characters
128 * Prints $var with HTML characters (like "<", ">", etc.) properly quoted.
129 * This function is very similar to {@link s()}
131 * @param string $var the string potentially containing HTML characters
132 * @param boolean $strip to decide if we want to strip slashes or no. Default to false.
133 * true should be used to print data from forms and false for data from DB.
134 * @return string
136 function p($var, $strip=false) {
137 echo s($var, $strip);
141 * Does proper javascript quoting.
142 * Do not use addslashes anymore, because it does not work when magic_quotes_sybase is enabled.
144 * @since 1.8 - 22/02/2007
145 * @param mixed value
146 * @return mixed quoted result
148 function addslashes_js($var) {
149 if (is_string($var)) {
150 $var = str_replace('\\', '\\\\', $var);
151 $var = str_replace(array('\'', '"', "\n", "\r", "\0"), array('\\\'', '\\"', '\\n', '\\r', '\\0'), $var);
152 $var = str_replace('</', '<\/', $var); // XHTML compliance
153 } else if (is_array($var)) {
154 $var = array_map('addslashes_js', $var);
155 } else if (is_object($var)) {
156 $a = get_object_vars($var);
157 foreach ($a as $key=>$value) {
158 $a[$key] = addslashes_js($value);
160 $var = (object)$a;
162 return $var;
166 * Remove query string from url
168 * Takes in a URL and returns it without the querystring portion
170 * @param string $url the url which may have a query string attached
171 * @return string
173 function strip_querystring($url) {
175 if ($commapos = strpos($url, '?')) {
176 return substr($url, 0, $commapos);
177 } else {
178 return $url;
183 * Returns the URL of the HTTP_REFERER, less the querystring portion if required
184 * @return string
186 function get_referer($stripquery=true) {
187 if (isset($_SERVER['HTTP_REFERER'])) {
188 if ($stripquery) {
189 return strip_querystring($_SERVER['HTTP_REFERER']);
190 } else {
191 return $_SERVER['HTTP_REFERER'];
193 } else {
194 return '';
200 * Returns the name of the current script, WITH the querystring portion.
201 * this function is necessary because PHP_SELF and REQUEST_URI and SCRIPT_NAME
202 * return different things depending on a lot of things like your OS, Web
203 * server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.)
204 * <b>NOTE:</b> This function returns false if the global variables needed are not set.
206 * @return string
208 function me() {
210 if (!empty($_SERVER['REQUEST_URI'])) {
211 return $_SERVER['REQUEST_URI'];
213 } else if (!empty($_SERVER['PHP_SELF'])) {
214 if (!empty($_SERVER['QUERY_STRING'])) {
215 return $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
217 return $_SERVER['PHP_SELF'];
219 } else if (!empty($_SERVER['SCRIPT_NAME'])) {
220 if (!empty($_SERVER['QUERY_STRING'])) {
221 return $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
223 return $_SERVER['SCRIPT_NAME'];
225 } else if (!empty($_SERVER['URL'])) { // May help IIS (not well tested)
226 if (!empty($_SERVER['QUERY_STRING'])) {
227 return $_SERVER['URL'] .'?'. $_SERVER['QUERY_STRING'];
229 return $_SERVER['URL'];
231 } else {
232 notify('Warning: Could not find any of these web server variables: $REQUEST_URI, $PHP_SELF, $SCRIPT_NAME or $URL');
233 return false;
238 * Like {@link me()} but returns a full URL
239 * @see me()
240 * @return string
242 function qualified_me() {
244 global $CFG;
246 if (!empty($CFG->wwwroot)) {
247 $url = parse_url($CFG->wwwroot);
250 if (!empty($url['host'])) {
251 $hostname = $url['host'];
252 } else if (!empty($_SERVER['SERVER_NAME'])) {
253 $hostname = $_SERVER['SERVER_NAME'];
254 } else if (!empty($_ENV['SERVER_NAME'])) {
255 $hostname = $_ENV['SERVER_NAME'];
256 } else if (!empty($_SERVER['HTTP_HOST'])) {
257 $hostname = $_SERVER['HTTP_HOST'];
258 } else if (!empty($_ENV['HTTP_HOST'])) {
259 $hostname = $_ENV['HTTP_HOST'];
260 } else {
261 notify('Warning: could not find the name of this server!');
262 return false;
265 if (!empty($url['port'])) {
266 $hostname .= ':'.$url['port'];
267 } else if (!empty($_SERVER['SERVER_PORT'])) {
268 if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
269 $hostname .= ':'.$_SERVER['SERVER_PORT'];
273 if (isset($_SERVER['HTTPS'])) {
274 $protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
275 } else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS']
276 $protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://';
277 } else {
278 $protocol = 'http://';
281 $url_prefix = $protocol.$hostname;
282 return $url_prefix . me();
286 * Determine if there is data waiting to be processed from a form
288 * Used on most forms in Moodle to check for data
289 * Returns the data as an object, if it's found.
290 * This object can be used in foreach loops without
291 * casting because it's cast to (array) automatically
293 * Checks that submitted POST data exists and returns it as object.
295 * @param string $url not used anymore
296 * @return mixed false or object
298 function data_submitted($url='') {
300 if (empty($_POST)) {
301 return false;
302 } else {
303 return (object)$_POST;
308 * Moodle replacement for php stripslashes() function,
309 * works also for objects and arrays.
311 * The standard php stripslashes() removes ALL backslashes
312 * even from strings - so C:\temp becomes C:temp - this isn't good.
313 * This function should work as a fairly safe replacement
314 * to be called on quoted AND unquoted strings (to be sure)
316 * @param mixed something to remove unsafe slashes from
317 * @return mixed
319 function stripslashes_safe($mixed) {
320 // there is no need to remove slashes from int, float and bool types
321 if (empty($mixed)) {
322 //nothing to do...
323 } else if (is_string($mixed)) {
324 if (ini_get_bool('magic_quotes_sybase')) { //only unescape single quotes
325 $mixed = str_replace("''", "'", $mixed);
326 } else { //the rest, simple and double quotes and backslashes
327 $mixed = str_replace("\\'", "'", $mixed);
328 $mixed = str_replace('\\"', '"', $mixed);
329 $mixed = str_replace('\\\\', '\\', $mixed);
331 } else if (is_array($mixed)) {
332 foreach ($mixed as $key => $value) {
333 $mixed[$key] = stripslashes_safe($value);
335 } else if (is_object($mixed)) {
336 $vars = get_object_vars($mixed);
337 foreach ($vars as $key => $value) {
338 $mixed->$key = stripslashes_safe($value);
342 return $mixed;
346 * Recursive implementation of stripslashes()
348 * This function will allow you to strip the slashes from a variable.
349 * If the variable is an array or object, slashes will be stripped
350 * from the items (or properties) it contains, even if they are arrays
351 * or objects themselves.
353 * @param mixed the variable to remove slashes from
354 * @return mixed
356 function stripslashes_recursive($var) {
357 if(is_object($var)) {
358 $properties = get_object_vars($var);
359 foreach($properties as $property => $value) {
360 $var->$property = stripslashes_recursive($value);
363 else if(is_array($var)) {
364 foreach($var as $property => $value) {
365 $var[$property] = stripslashes_recursive($value);
368 else if(is_string($var)) {
369 $var = stripslashes($var);
371 return $var;
375 * Recursive implementation of addslashes()
377 * This function will allow you to add the slashes from a variable.
378 * If the variable is an array or object, slashes will be added
379 * to the items (or properties) it contains, even if they are arrays
380 * or objects themselves.
382 * @param mixed the variable to add slashes from
383 * @return mixed
385 function addslashes_recursive($var) {
386 if(is_object($var)) {
387 $properties = get_object_vars($var);
388 foreach($properties as $property => $value) {
389 $var->$property = addslashes_recursive($value);
392 else if(is_array($var)) {
393 foreach($var as $property => $value) {
394 $var[$property] = addslashes_recursive($value);
397 else if(is_string($var)) {
398 $var = addslashes($var);
400 return $var;
404 * Given some normal text this function will break up any
405 * long words to a given size by inserting the given character
407 * It's multibyte savvy and doesn't change anything inside html tags.
409 * @param string $string the string to be modified
410 * @param int $maxsize maximum length of the string to be returned
411 * @param string $cutchar the string used to represent word breaks
412 * @return string
414 function break_up_long_words($string, $maxsize=20, $cutchar=' ') {
416 /// Loading the textlib singleton instance. We are going to need it.
417 $textlib = textlib_get_instance();
419 /// First of all, save all the tags inside the text to skip them
420 $tags = array();
421 filter_save_tags($string,$tags);
423 /// Process the string adding the cut when necessary
424 $output = '';
425 $length = $textlib->strlen($string);
426 $wordlength = 0;
428 for ($i=0; $i<$length; $i++) {
429 $char = $textlib->substr($string, $i, 1);
430 if ($char == ' ' or $char == "\t" or $char == "\n" or $char == "\r" or $char == "<" or $char == ">") {
431 $wordlength = 0;
432 } else {
433 $wordlength++;
434 if ($wordlength > $maxsize) {
435 $output .= $cutchar;
436 $wordlength = 0;
439 $output .= $char;
442 /// Finally load the tags back again
443 if (!empty($tags)) {
444 $output = str_replace(array_keys($tags), $tags, $output);
447 return $output;
451 * This does a search and replace, ignoring case
452 * This function is only used for versions of PHP older than version 5
453 * which do not have a native version of this function.
454 * Taken from the PHP manual, by bradhuizenga @ softhome.net
456 * @param string $find the string to search for
457 * @param string $replace the string to replace $find with
458 * @param string $string the string to search through
459 * return string
461 if (!function_exists('str_ireplace')) { /// Only exists in PHP 5
462 function str_ireplace($find, $replace, $string) {
464 if (!is_array($find)) {
465 $find = array($find);
468 if(!is_array($replace)) {
469 if (!is_array($find)) {
470 $replace = array($replace);
471 } else {
472 // this will duplicate the string into an array the size of $find
473 $c = count($find);
474 $rString = $replace;
475 unset($replace);
476 for ($i = 0; $i < $c; $i++) {
477 $replace[$i] = $rString;
482 foreach ($find as $fKey => $fItem) {
483 $between = explode(strtolower($fItem),strtolower($string));
484 $pos = 0;
485 foreach($between as $bKey => $bItem) {
486 $between[$bKey] = substr($string,$pos,strlen($bItem));
487 $pos += strlen($bItem) + strlen($fItem);
489 $string = implode($replace[$fKey],$between);
491 return ($string);
496 * Locate the position of a string in another string
498 * This function is only used for versions of PHP older than version 5
499 * which do not have a native version of this function.
500 * Taken from the PHP manual, by dmarsh @ spscc.ctc.edu
502 * @param string $haystack The string to be searched
503 * @param string $needle The string to search for
504 * @param int $offset The position in $haystack where the search should begin.
506 if (!function_exists('stripos')) { /// Only exists in PHP 5
507 function stripos($haystack, $needle, $offset=0) {
509 return strpos(strtoupper($haystack), strtoupper($needle), $offset);
514 * This function will create a HTML link that will work on both
515 * Javascript and non-javascript browsers.
516 * Relies on the Javascript function openpopup in javascript.php
518 * $url must be relative to home page eg /mod/survey/stuff.php
519 * @param string $url Web link relative to home page
520 * @param string $name Name to be assigned to the popup window
521 * @param string $linkname Text to be displayed as web link
522 * @param int $height Height to assign to popup window
523 * @param int $width Height to assign to popup window
524 * @param string $title Text to be displayed as popup page title
525 * @param string $options List of additional options for popup window
526 * @todo Add code examples and list of some options that might be used.
527 * @param boolean $return Should the link to the popup window be returned as a string (true) or printed immediately (false)?
528 * @return string
529 * @uses $CFG
531 function link_to_popup_window ($url, $name='popup', $linkname='click here',
532 $height=400, $width=500, $title='Popup window',
533 $options='none', $return=false) {
535 global $CFG;
537 if ($options == 'none') {
538 $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
540 $fullscreen = 0;
542 if (!(strpos($url,$CFG->wwwroot) === false)) { // some log url entries contain _SERVER[HTTP_REFERRER] in which case wwwroot is already there.
543 $url = substr($url, strlen($CFG->wwwroot));
546 $link = '<a title="'. s($title) .'" href="'. $CFG->wwwroot . $url .'" '.
547 "onclick=\"this.target='$name'; return openpopup('$url', '$name', '$options', $fullscreen);\">$linkname</a>";
548 if ($return) {
549 return $link;
550 } else {
551 echo $link;
556 * This function will print a button submit form element
557 * that will work on both Javascript and non-javascript browsers.
558 * Relies on the Javascript function openpopup in javascript.php
560 * $url must be relative to home page eg /mod/survey/stuff.php
561 * @param string $url Web link relative to home page
562 * @param string $name Name to be assigned to the popup window
563 * @param string $linkname Text to be displayed as web link
564 * @param int $height Height to assign to popup window
565 * @param int $width Height to assign to popup window
566 * @param string $title Text to be displayed as popup page title
567 * @param string $options List of additional options for popup window
568 * @param string $return If true, return as a string, otherwise print
569 * @return string
570 * @uses $CFG
572 function button_to_popup_window ($url, $name='popup', $linkname='click here',
573 $height=400, $width=500, $title='Popup window', $options='none', $return=false,
574 $id='', $class='') {
576 global $CFG;
578 if ($options == 'none') {
579 $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
582 if ($id) {
583 $id = ' id="'.$id.'" ';
585 if ($class) {
586 $class = ' class="'.$class.'" ';
588 $fullscreen = 0;
590 $button = '<input type="button" name="'.$name.'" title="'. $title .'" value="'. $linkname .' ..." '.$id.$class.
591 "onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\" />\n";
592 if ($return) {
593 return $button;
594 } else {
595 echo $button;
601 * Prints a simple button to close a window
603 function close_window_button($name='closewindow', $return=false) {
604 global $CFG;
606 $output = '';
608 $output .= '<div class="closewindow">' . "\n";
609 $output .= '<form action="'.$CFG->wwwroot.'"><div>'; // We don't use this
610 $output .= '<input type="button" onclick="self.close();" value="'.get_string($name).'" />';
611 $output .= '</div></form>';
612 $output .= '</div>' . "\n";
614 if ($return) {
615 return $output;
616 } else {
617 echo $output;
622 * Try and close the current window immediately using Javascript
624 function close_window($delay=0) {
626 <script type="text/javascript">
627 //<![CDATA[
628 function close_this_window() {
629 self.close();
631 setTimeout("close_this_window()", <?php echo $delay * 1000 ?>);
632 //]]>
633 </script>
634 <noscript><center>
635 <?php print_string('pleaseclose') ?>
636 </center></noscript>
637 <?php
638 die;
643 * Given an array of value, creates a popup menu to be part of a form
644 * $options["value"]["label"]
646 * @param type description
647 * @todo Finish documenting this function
649 function choose_from_menu ($options, $name, $selected='', $nothing='choose', $script='',
650 $nothingvalue='0', $return=false, $disabled=false, $tabindex=0, $id='') {
652 if ($nothing == 'choose') {
653 $nothing = get_string('choose') .'...';
656 $attributes = ($script) ? 'onchange="'. $script .'"' : '';
657 if ($disabled) {
658 $attributes .= ' disabled="disabled"';
661 if ($tabindex) {
662 $attributes .= ' tabindex="'.$tabindex.'"';
665 if ($id ==='') {
666 $id = 'menu'.$name;
667 // name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
668 $id = str_replace('[', '', $id);
669 $id = str_replace(']', '', $id);
672 $output = '<select id="'.$id.'" name="'. $name .'" '. $attributes .'>' . "\n";
673 if ($nothing) {
674 $output .= ' <option value="'. s($nothingvalue) .'"'. "\n";
675 if ($nothingvalue === $selected) {
676 $output .= ' selected="selected"';
678 $output .= '>'. $nothing .'</option>' . "\n";
680 if (!empty($options)) {
681 foreach ($options as $value => $label) {
682 $output .= ' <option value="'. s($value) .'"';
683 if ((string)$value == (string)$selected) {
684 $output .= ' selected="selected"';
686 if ($label === '') {
687 $output .= '>'. $value .'</option>' . "\n";
688 } else {
689 $output .= '>'. $label .'</option>' . "\n";
693 $output .= '</select>' . "\n";
695 if ($return) {
696 return $output;
697 } else {
698 echo $output;
703 * Choose value 0 or 1 from a menu with options 'No' and 'Yes'.
704 * Other options like choose_from_menu.
706 function choose_from_menu_yesno($name, $selected, $script = '',
707 $return = false, $disabled = false, $tabindex = 0) {
708 return choose_from_menu(array(get_string('no'), get_string('yes')), $name,
709 $selected, '', $script, '0', $return, $disabled, $tabindex);
713 * Just like choose_from_menu, but takes a nested array (2 levels) and makes a dropdown menu
714 * including option headings with the first level.
716 function choose_from_menu_nested($options,$name,$selected='',$nothing='choose',$script = '',
717 $nothingvalue=0,$return=false,$disabled=false,$tabindex=0) {
719 if ($nothing == 'choose') {
720 $nothing = get_string('choose') .'...';
723 $attributes = ($script) ? 'onchange="'. $script .'"' : '';
724 if ($disabled) {
725 $attributes .= ' disabled="disabled"';
728 if ($tabindex) {
729 $attributes .= ' tabindex="'.$tabindex.'"';
732 $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
733 if ($nothing) {
734 $output .= ' <option value="'. $nothingvalue .'"'. "\n";
735 if ($nothingvalue === $selected) {
736 $output .= ' selected="selected"';
738 $output .= '>'. $nothing .'</option>' . "\n";
740 if (!empty($options)) {
741 foreach ($options as $section => $values) {
743 $output .= ' <optgroup label="'. s(format_string($section)) .'">'."\n";
744 foreach ($values as $value => $label) {
745 $output .= ' <option value="'. format_string($value) .'"';
746 if ((string)$value == (string)$selected) {
747 $output .= ' selected="selected"';
749 if ($label === '') {
750 $output .= '>'. $value .'</option>' . "\n";
751 } else {
752 $output .= '>'. $label .'</option>' . "\n";
755 $output .= ' </optgroup>'."\n";
758 $output .= '</select>' . "\n";
760 if ($return) {
761 return $output;
762 } else {
763 echo $output;
769 * Given an array of values, creates a group of radio buttons to be part of a form
771 * @param array $options An array of value-label pairs for the radio group (values as keys)
772 * @param string $name Name of the radiogroup (unique in the form)
773 * @param string $checked The value that is already checked
775 function choose_from_radio ($options, $name, $checked='', $return=false) {
777 static $idcounter = 0;
779 if (!$name) {
780 $name = 'unnamed';
783 $output = '<span class="radiogroup '.$name."\">\n";
785 if (!empty($options)) {
786 $currentradio = 0;
787 foreach ($options as $value => $label) {
788 $htmlid = 'auto-rb'.sprintf('%04d', ++$idcounter);
789 $output .= ' <span class="radioelement '.$name.' rb'.$currentradio."\">";
790 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="radio" value="'.$value.'"';
791 if ($value == $checked) {
792 $output .= ' checked="checked"';
794 if ($label === '') {
795 $output .= ' /> <label for="'.$htmlid.'">'. $value .'</label></span>' . "\n";
796 } else {
797 $output .= ' /> <label for="'.$htmlid.'">'. $label .'</label></span>' . "\n";
799 $currentradio = ($currentradio + 1) % 2;
803 $output .= '</span>' . "\n";
805 if ($return) {
806 return $output;
807 } else {
808 echo $output;
812 /** Display an standard html checkbox with an optional label
814 * @param string $name The name of the checkbox
815 * @param string $value The valus that the checkbox will pass when checked
816 * @param boolean $checked The flag to tell the checkbox initial state
817 * @param string $label The label to be showed near the checkbox
818 * @param string $alt The info to be inserted in the alt tag
820 function print_checkbox ($name, $value, $checked = true, $label = '', $alt = '', $script='',$return=false) {
822 static $idcounter = 0;
824 if (!$name) {
825 $name = 'unnamed';
828 if ($alt) {
829 $alt = strip_tags($alt);
830 } else {
831 $alt = 'checkbox';
834 if ($checked) {
835 $strchecked = ' checked="checked"';
836 } else {
837 $strchecked = '';
840 $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
841 $output = '<span class="checkbox '.$name."\">";
842 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="checkbox" value="'.$value.'" alt="'.$alt.'"'.$strchecked.' '.((!empty($script)) ? ' onclick="'.$script.'" ' : '').' />';
843 if(!empty($label)) {
844 $output .= ' <label for="'.$htmlid.'">'.$label.'</label>';
846 $output .= '</span>'."\n";
848 if (empty($return)) {
849 echo $output;
850 } else {
851 return $output;
856 /** Display an standard html text field with an optional label
858 * @param string $name The name of the text field
859 * @param string $value The value of the text field
860 * @param string $label The label to be showed near the text field
861 * @param string $alt The info to be inserted in the alt tag
863 function print_textfield ($name, $value, $alt = '',$size=50,$maxlength=0, $return=false) {
865 static $idcounter = 0;
867 if (empty($name)) {
868 $name = 'unnamed';
871 if (empty($alt)) {
872 $alt = 'textfield';
875 if (!empty($maxlength)) {
876 $maxlength = ' maxlength="'.$maxlength.'" ';
879 $htmlid = 'auto-tf'.sprintf('%04d', ++$idcounter);
880 $output = '<span class="textfield '.$name."\">";
881 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="text" value="'.$value.'" size="'.$size.'" '.$maxlength.' alt="'.$alt.'" />';
883 $output .= '</span>'."\n";
885 if (empty($return)) {
886 echo $output;
887 } else {
888 return $output;
895 * Implements a complete little popup form
897 * @uses $CFG
898 * @param string $common The URL up to the point of the variable that changes
899 * @param array $options Alist of value-label pairs for the popup list
900 * @param string $formid Id must be unique on the page (originaly $formname)
901 * @param string $selected The option that is already selected
902 * @param string $nothing The label for the "no choice" option
903 * @param string $help The name of a help page if help is required
904 * @param string $helptext The name of the label for the help button
905 * @param boolean $return Indicates whether the function should return the text
906 * as a string or echo it directly to the page being rendered
907 * @param string $targetwindow The name of the target page to open the linked page in.
908 * @return string If $return is true then the entire form is returned as a string.
909 * @todo Finish documenting this function<br>
911 function popup_form($common, $options, $formid, $selected='', $nothing='choose', $help='', $helptext='', $return=false, $targetwindow='self', $selectlabel='') {
913 global $CFG;
914 static $go, $choose; /// Locally cached, in case there's lots on a page
916 if (empty($options)) {
917 return '';
920 if (!isset($go)) {
921 $go = get_string('go');
924 if ($nothing == 'choose') {
925 if (!isset($choose)) {
926 $choose = get_string('choose');
928 $nothing = $choose.'...';
931 // changed reference to document.getElementById('id_abc') instead of document.abc
932 // MDL-7861
933 $output = '<form action="'.$CFG->wwwroot.'/course/jumpto.php"'.
934 ' method="get" '.
935 $CFG->frametarget.
936 ' id="'.$formid.'"'.
937 ' class="popupform">';
938 if ($help) {
939 $button = helpbutton($help, $helptext, 'moodle', true, false, '', true);
940 } else {
941 $button = '';
944 if ($selectlabel) {
945 $selectlabel = '<label for="'.$formid.'_jump">'.$selectlabel.'</label>';
948 $output .= '<div>'.$selectlabel.$button.'<select id="'.$formid.'_jump" name="jump" onchange="'.$targetwindow.'.location=document.getElementById(\''.$formid.'\').jump.options[document.getElementById(\''.$formid.'\').jump.selectedIndex].value;">'."\n";
950 if ($nothing != '') {
951 $output .= " <option value=\"javascript:void(0)\">$nothing</option>\n";
954 $inoptgroup = false;
956 foreach ($options as $value => $label) {
958 if ($label == '--') { /// we are ending previous optgroup
959 /// Check to see if we already have a valid open optgroup
960 /// XHTML demands that there be at least 1 option within an optgroup
961 if ($inoptgroup and (count($optgr) > 1) ) {
962 $output .= implode('', $optgr);
963 $output .= ' </optgroup>';
965 $optgr = array();
966 $inoptgroup = false;
967 continue;
968 } else if (substr($label,0,2) == '--') { /// we are starting a new optgroup
970 /// Check to see if we already have a valid open optgroup
971 /// XHTML demands that there be at least 1 option within an optgroup
972 if ($inoptgroup and (count($optgr) > 1) ) {
973 $output .= implode('', $optgr);
974 $output .= ' </optgroup>';
977 unset($optgr);
978 $optgr = array();
980 $optgr[] = ' <optgroup label="'. s(format_string(substr($label,2))) .'">'; // Plain labels
982 $inoptgroup = true; /// everything following will be in an optgroup
983 continue;
985 } else {
986 if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()]))
988 $url=sid_process_url( $common . $value );
989 } else
991 $url=$common . $value;
993 $optstr = ' <option value="' . $url . '"';
995 if ($value == $selected) {
996 $optstr .= ' selected="selected"';
999 if ($label) {
1000 $optstr .= '>'. $label .'</option>' . "\n";
1001 } else {
1002 $optstr .= '>'. $value .'</option>' . "\n";
1005 if ($inoptgroup) {
1006 $optgr[] = $optstr;
1007 } else {
1008 $output .= $optstr;
1014 /// catch the final group if not closed
1015 if ($inoptgroup and count($optgr) > 1) {
1016 $output .= implode('', $optgr);
1017 $output .= ' </optgroup>';
1020 $output .= '</select>';
1021 $output .= '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
1022 $output .= '<div id="noscript'.$formid.'" style="display: inline;">';
1023 $output .= '<input type="submit" value="'.$go.'" /></div>';
1024 $output .= '<script type="text/javascript">'.
1025 "\n//<![CDATA[\n".
1026 'document.getElementById("noscript'.$formid.'").style.display = "none";'.
1027 "\n//]]>\n".'</script>';
1028 $output .= '</div>';
1029 $output .= '</form>';
1031 if ($return) {
1032 return $output;
1033 } else {
1034 echo $output;
1040 * Prints some red text
1042 * @param string $error The text to be displayed in red
1044 function formerr($error) {
1046 if (!empty($error)) {
1047 echo '<span class="error">'. $error .'</span>';
1052 * Validates an email to make sure it makes sense.
1054 * @param string $address The email address to validate.
1055 * @return boolean
1057 function validate_email($address) {
1059 return (ereg('^[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+'.
1060 '(\.[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+)*'.
1061 '@'.
1062 '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
1063 '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$',
1064 $address));
1068 * Extracts file argument either from file parameter or PATH_INFO
1070 * @param string $scriptname name of the calling script
1071 * @return string file path (only safe characters)
1073 function get_file_argument($scriptname) {
1074 global $_SERVER;
1076 $relativepath = FALSE;
1078 // first try normal parameter (compatible method == no relative links!)
1079 $relativepath = optional_param('file', FALSE, PARAM_PATH);
1080 if ($relativepath === '/testslasharguments') {
1081 echo 'test -1 : Incorrect use - try "file.php/testslasharguments" instead'; //indicate fopen/fread works for health center
1082 die;
1085 // then try extract file from PATH_INFO (slasharguments method)
1086 if (!$relativepath and !empty($_SERVER['PATH_INFO'])) {
1087 $path_info = $_SERVER['PATH_INFO'];
1088 // check that PATH_INFO works == must not contain the script name
1089 if (!strpos($path_info, $scriptname)) {
1090 $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1091 if ($relativepath === '/testslasharguments') {
1092 echo 'test 1 : Slasharguments test passed. Server confguration is compatible with file.php/1/pic.jpg slashargument setting.'; //indicate ok for health center
1093 die;
1098 // now if both fail try the old way
1099 // (for compatibility with misconfigured or older buggy php implementations)
1100 if (!$relativepath) {
1101 $arr = explode($scriptname, me());
1102 if (!empty($arr[1])) {
1103 $path_info = strip_querystring($arr[1]);
1104 $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1105 if ($relativepath === '/testslasharguments') {
1106 echo 'test 2 : Slasharguments test passed (compatibility hack). Server confguration may be compatible with file.php/1/pic.jpg slashargument setting'; //indicate ok for health center
1107 die;
1112 return $relativepath;
1116 * Searches the current environment variables for some slash arguments
1118 * @param string $file ?
1119 * @todo Finish documenting this function
1121 function get_slash_arguments($file='file.php') {
1123 if (!$string = me()) {
1124 return false;
1127 $pathinfo = explode($file, $string);
1129 if (!empty($pathinfo[1])) {
1130 return addslashes($pathinfo[1]);
1131 } else {
1132 return false;
1137 * Extracts arguments from "/foo/bar/something"
1138 * eg http://mysite.com/script.php/foo/bar/something
1140 * @param string $string ?
1141 * @param int $i ?
1142 * @return array|string
1143 * @todo Finish documenting this function
1145 function parse_slash_arguments($string, $i=0) {
1147 if (detect_munged_arguments($string)) {
1148 return false;
1150 $args = explode('/', $string);
1152 if ($i) { // return just the required argument
1153 return $args[$i];
1155 } else { // return the whole array
1156 array_shift($args); // get rid of the empty first one
1157 return $args;
1162 * Just returns an array of text formats suitable for a popup menu
1164 * @uses FORMAT_MOODLE
1165 * @uses FORMAT_HTML
1166 * @uses FORMAT_PLAIN
1167 * @uses FORMAT_MARKDOWN
1168 * @return array
1170 function format_text_menu() {
1172 return array (FORMAT_MOODLE => get_string('formattext'),
1173 FORMAT_HTML => get_string('formathtml'),
1174 FORMAT_PLAIN => get_string('formatplain'),
1175 FORMAT_MARKDOWN => get_string('formatmarkdown'));
1179 * Given text in a variety of format codings, this function returns
1180 * the text as safe HTML.
1182 * @uses $CFG
1183 * @uses FORMAT_MOODLE
1184 * @uses FORMAT_HTML
1185 * @uses FORMAT_PLAIN
1186 * @uses FORMAT_WIKI
1187 * @uses FORMAT_MARKDOWN
1188 * @param string $text The text to be formatted. This is raw text originally from user input.
1189 * @param int $format Identifier of the text format to be used
1190 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1191 * @param array $options ?
1192 * @param int $courseid ?
1193 * @return string
1194 * @todo Finish documenting this function
1196 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) {
1198 global $CFG, $COURSE;
1200 if ($text === '') {
1201 return ''; // no need to do any filters and cleaning
1204 if (!isset($options->trusttext)) {
1205 $options->trusttext = false;
1208 if (!isset($options->noclean)) {
1209 $options->noclean=false;
1211 if (!isset($options->nocache)) {
1212 $options->nocache=false;
1214 if (!isset($options->smiley)) {
1215 $options->smiley=true;
1217 if (!isset($options->filter)) {
1218 $options->filter=true;
1220 if (!isset($options->para)) {
1221 $options->para=true;
1223 if (!isset($options->newlines)) {
1224 $options->newlines=true;
1227 if (empty($courseid)) {
1228 $courseid = $COURSE->id;
1231 if (!empty($CFG->cachetext) and empty($options->nocache)) {
1232 $time = time() - $CFG->cachetext;
1233 $md5key = md5($text.'-'.(int)$courseid.'-'.current_language().'-'.(int)$format.(int)$options->trusttext.(int)$options->noclean.(int)$options->smiley.(int)$options->filter.(int)$options->para.(int)$options->newlines);
1234 if ($oldcacheitem = get_record_sql('SELECT * FROM '.$CFG->prefix.'cache_text WHERE md5key = \''.$md5key.'\'', true)) {
1235 if ($oldcacheitem->timemodified >= $time) {
1236 return $oldcacheitem->formattedtext;
1241 // trusttext overrides the noclean option!
1242 if ($options->trusttext) {
1243 if (trusttext_present($text)) {
1244 $text = trusttext_strip($text);
1245 if (!empty($CFG->enabletrusttext)) {
1246 $options->noclean = true;
1247 } else {
1248 $options->noclean = false;
1250 } else {
1251 $options->noclean = false;
1253 } else if (!debugging('', DEBUG_DEVELOPER)) {
1254 // strip any forgotten trusttext in non-developer mode
1255 // do not forget to disable text cache when debugging trusttext!!
1256 $text = trusttext_strip($text);
1259 $CFG->currenttextiscacheable = true; // Default status - can be changed by any filter
1261 switch ($format) {
1262 case FORMAT_HTML:
1263 if ($options->smiley) {
1264 replace_smilies($text);
1266 if (!$options->noclean) {
1267 $text = clean_text($text, FORMAT_HTML);
1269 if ($options->filter) {
1270 $text = filter_text($text, $courseid);
1272 break;
1274 case FORMAT_PLAIN:
1275 $text = s($text); // cleans dangerous JS
1276 $text = rebuildnolinktag($text);
1277 $text = str_replace(' ', '&nbsp; ', $text);
1278 $text = nl2br($text);
1279 break;
1281 case FORMAT_WIKI:
1282 // this format is deprecated
1283 $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle. You should not be seeing
1284 this message as all texts should have been converted to Markdown format instead.
1285 Please post a bug report to http://moodle.org/bugs with information about where you
1286 saw this message.</p>'.s($text);
1287 break;
1289 case FORMAT_MARKDOWN:
1290 $text = markdown_to_html($text);
1291 if ($options->smiley) {
1292 replace_smilies($text);
1294 if (!$options->noclean) {
1295 $text = clean_text($text, FORMAT_HTML);
1298 if ($options->filter) {
1299 $text = filter_text($text, $courseid);
1301 break;
1303 default: // FORMAT_MOODLE or anything else
1304 $text = text_to_html($text, $options->smiley, $options->para, $options->newlines);
1305 if (!$options->noclean) {
1306 $text = clean_text($text, FORMAT_HTML);
1309 if ($options->filter) {
1310 $text = filter_text($text, $courseid);
1312 break;
1315 if (empty($options->nocache) and !empty($CFG->cachetext) and $CFG->currenttextiscacheable) {
1316 $newcacheitem = new object();
1317 $newcacheitem->md5key = $md5key;
1318 $newcacheitem->formattedtext = addslashes($text);
1319 $newcacheitem->timemodified = time();
1320 if ($oldcacheitem) { // See bug 4677 for discussion
1321 $newcacheitem->id = $oldcacheitem->id;
1322 @update_record('cache_text', $newcacheitem); // Update existing record in the cache table
1323 // It's unlikely that the cron cache cleaner could have
1324 // deleted this entry in the meantime, as it allows
1325 // some extra time to cover these cases.
1326 } else {
1327 @insert_record('cache_text', $newcacheitem); // Insert a new record in the cache table
1328 // Again, it's possible that another user has caused this
1329 // record to be created already in the time that it took
1330 // to traverse this function. That's OK too, as the
1331 // call above handles duplicate entries, and eventually
1332 // the cron cleaner will delete them.
1336 return $text;
1339 /** Converts the text format from the value to the 'internal'
1340 * name or vice versa. $key can either be the value or the name
1341 * and you get the other back.
1343 * @param mixed int 0-4 or string one of 'moodle','html','plain','markdown'
1344 * @return mixed as above but the other way around!
1346 function text_format_name( $key ) {
1347 $lookup = array();
1348 $lookup[FORMAT_MOODLE] = 'moodle';
1349 $lookup[FORMAT_HTML] = 'html';
1350 $lookup[FORMAT_PLAIN] = 'plain';
1351 $lookup[FORMAT_MARKDOWN] = 'markdown';
1352 $value = "error";
1353 if (!is_numeric($key)) {
1354 $key = strtolower( $key );
1355 $value = array_search( $key, $lookup );
1357 else {
1358 if (isset( $lookup[$key] )) {
1359 $value = $lookup[ $key ];
1362 return $value;
1366 /** Given a simple string, this function returns the string
1367 * processed by enabled filters if $CFG->filterall is enabled
1369 * @param string $string The string to be filtered.
1370 * @param boolean $striplinks To strip any link in the result text (Moodle 1.8 default changed from false to true! MDL-8713)
1371 * @param int $courseid Current course as filters can, potentially, use it
1372 * @return string
1374 function format_string ($string, $striplinks=true, $courseid=NULL ) {
1376 global $CFG, $COURSE;
1378 //We'll use a in-memory cache here to speed up repeated strings
1379 static $strcache = false;
1381 if ($strcache === false or count($strcache) > 2000 ) { // this number might need some tuning to limit memory usage in cron
1382 $strcache = array();
1385 //init course id
1386 if (empty($courseid)) {
1387 $courseid = $COURSE->id;
1390 //Calculate md5
1391 $md5 = md5($string.'<+>'.$striplinks.'<+>'.$courseid.'<+>'.current_language());
1393 //Fetch from cache if possible
1394 if (isset($strcache[$md5])) {
1395 return $strcache[$md5];
1398 // First replace all ampersands not followed by html entity code
1399 $string = preg_replace("/\&(?![a-z0-9#]{1,8};)/", "&amp;", $string);
1401 if (!empty($CFG->filterall)) {
1402 $string = filter_string($string, $courseid);
1405 // If the site requires it, strip ALL tags from this string
1406 if (!empty($CFG->formatstringstriptags)) {
1407 $string = strip_tags($string);
1409 // Otherwise strip just links if that is required (default)
1410 } else if ($striplinks) { //strip links in string
1411 $string = preg_replace('/(<a[^>]+?>)(.+?)(<\/a>)/is','$2',$string);
1414 //Store to cache
1415 $strcache[$md5] = $string;
1417 return $string;
1421 * Given text in a variety of format codings, this function returns
1422 * the text as plain text suitable for plain email.
1424 * @uses FORMAT_MOODLE
1425 * @uses FORMAT_HTML
1426 * @uses FORMAT_PLAIN
1427 * @uses FORMAT_WIKI
1428 * @uses FORMAT_MARKDOWN
1429 * @param string $text The text to be formatted. This is raw text originally from user input.
1430 * @param int $format Identifier of the text format to be used
1431 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1432 * @return string
1434 function format_text_email($text, $format) {
1436 switch ($format) {
1438 case FORMAT_PLAIN:
1439 return $text;
1440 break;
1442 case FORMAT_WIKI:
1443 $text = wiki_to_html($text);
1444 /// This expression turns links into something nice in a text format. (Russell Jungwirth)
1445 /// From: http://php.net/manual/en/function.eregi-replace.php and simplified
1446 $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1447 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1448 break;
1450 case FORMAT_HTML:
1451 return html_to_text($text);
1452 break;
1454 case FORMAT_MOODLE:
1455 case FORMAT_MARKDOWN:
1456 default:
1457 $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1458 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1459 break;
1464 * Given some text in HTML format, this function will pass it
1465 * through any filters that have been defined in $CFG->textfilterx
1466 * The variable defines a filepath to a file containing the
1467 * filter function. The file must contain a variable called
1468 * $textfilter_function which contains the name of the function
1469 * with $courseid and $text parameters
1471 * @param string $text The text to be passed through format filters
1472 * @param int $courseid ?
1473 * @return string
1474 * @todo Finish documenting this function
1476 function filter_text($text, $courseid=NULL) {
1477 global $CFG, $COURSE;
1479 if (empty($courseid)) {
1480 $courseid = $COURSE->id; // (copied from format_text)
1483 if (!empty($CFG->textfilters)) {
1484 require_once($CFG->libdir.'/filterlib.php');
1485 $textfilters = explode(',', $CFG->textfilters);
1486 foreach ($textfilters as $textfilter) {
1487 if (is_readable($CFG->dirroot .'/'. $textfilter .'/filter.php')) {
1488 include_once($CFG->dirroot .'/'. $textfilter .'/filter.php');
1489 $functionname = basename($textfilter).'_filter';
1490 if (function_exists($functionname)) {
1491 $text = $functionname($courseid, $text);
1497 /// <nolink> tags removed for XHTML compatibility
1498 $text = str_replace('<nolink>', '', $text);
1499 $text = str_replace('</nolink>', '', $text);
1501 return $text;
1506 * Given a string (short text) in HTML format, this function will pass it
1507 * through any filters that have been defined in $CFG->stringfilters
1508 * The variable defines a filepath to a file containing the
1509 * filter function. The file must contain a variable called
1510 * $textfilter_function which contains the name of the function
1511 * with $courseid and $text parameters
1513 * @param string $string The text to be passed through format filters
1514 * @param int $courseid The id of a course
1515 * @return string
1517 function filter_string($string, $courseid=NULL) {
1518 global $CFG, $COURSE;
1520 if (empty($CFG->textfilters)) { // All filters are disabled anyway so quit
1521 return $string;
1524 if (empty($courseid)) {
1525 $courseid = $COURSE->id;
1528 require_once($CFG->libdir.'/filterlib.php');
1530 if (isset($CFG->stringfilters)) { // We have a predefined list to use, great!
1531 if (empty($CFG->stringfilters)) { // but it's blank, so finish now
1532 return $string;
1534 $stringfilters = explode(',', $CFG->stringfilters); // ..use the list we have
1536 } else { // Otherwise try to derive a list from textfilters
1537 if (strpos($CFG->textfilters, 'filter/multilang') !== false) { // Multilang is here
1538 $stringfilters = array('filter/multilang'); // Let's use just that
1539 $CFG->stringfilters = 'filter/multilang'; // Save it for next time through
1540 } else {
1541 $CFG->stringfilters = ''; // Save the result and return
1542 return $string;
1547 foreach ($stringfilters as $stringfilter) {
1548 if (is_readable($CFG->dirroot .'/'. $stringfilter .'/filter.php')) {
1549 include_once($CFG->dirroot .'/'. $stringfilter .'/filter.php');
1550 $functionname = basename($stringfilter).'_filter';
1551 if (function_exists($functionname)) {
1552 $string = $functionname($courseid, $string);
1557 /// <nolink> tags removed for XHTML compatibility
1558 $string = str_replace('<nolink>', '', $string);
1559 $string = str_replace('</nolink>', '', $string);
1561 return $string;
1565 * Is the text marked as trusted?
1567 * @param string $text text to be searched for TRUSTTEXT marker
1568 * @return boolean
1570 function trusttext_present($text) {
1571 if (strpos($text, TRUSTTEXT) !== FALSE) {
1572 return true;
1573 } else {
1574 return false;
1579 * This funtion MUST be called before the cleaning or any other
1580 * function that modifies the data! We do not know the origin of trusttext
1581 * in database, if it gets there in tweaked form we must not convert it
1582 * to supported form!!!
1584 * Please be carefull not to use stripslashes on data from database
1585 * or twice stripslashes when processing data recieved from user.
1587 * @param string $text text that may contain TRUSTTEXT marker
1588 * @return text without any TRUSTTEXT marker
1590 function trusttext_strip($text) {
1591 global $CFG;
1593 while (true) { //removing nested TRUSTTEXT
1594 $orig = $text;
1595 $text = str_replace(TRUSTTEXT, '', $text);
1596 if (strcmp($orig, $text) === 0) {
1597 return $text;
1603 * Mark text as trusted, such text may contain any HTML tags because the
1604 * normal text cleaning will be bypassed.
1605 * Please make sure that the text comes from trusted user before storing
1606 * it into database!
1608 function trusttext_mark($text) {
1609 global $CFG;
1610 if (!empty($CFG->enabletrusttext) and (strpos($text, TRUSTTEXT) === FALSE)) {
1611 return TRUSTTEXT.$text;
1612 } else {
1613 return $text;
1616 function trusttext_after_edit(&$text, $context) {
1617 if (has_capability('moodle/site:trustcontent', $context)) {
1618 $text = trusttext_strip($text);
1619 $text = trusttext_mark($text);
1620 } else {
1621 $text = trusttext_strip($text);
1625 function trusttext_prepare_edit(&$text, &$format, $usehtmleditor, $context) {
1626 global $CFG;
1628 $options = new object();
1629 $options->smiley = false;
1630 $options->filter = false;
1631 if (!empty($CFG->enabletrusttext)
1632 and has_capability('moodle/site:trustcontent', $context)
1633 and trusttext_present($text)) {
1634 $options->noclean = true;
1635 } else {
1636 $options->noclean = false;
1638 $text = trusttext_strip($text);
1639 if ($usehtmleditor) {
1640 $text = format_text($text, $format, $options);
1641 $format = FORMAT_HTML;
1642 } else if (!$options->noclean){
1643 $text = clean_text($text, $format);
1648 * Given raw text (eg typed in by a user), this function cleans it up
1649 * and removes any nasty tags that could mess up Moodle pages.
1651 * @uses FORMAT_MOODLE
1652 * @uses FORMAT_PLAIN
1653 * @uses ALLOWED_TAGS
1654 * @param string $text The text to be cleaned
1655 * @param int $format Identifier of the text format to be used
1656 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1657 * @return string The cleaned up text
1659 function clean_text($text, $format=FORMAT_MOODLE) {
1661 global $ALLOWED_TAGS;
1663 switch ($format) {
1664 case FORMAT_PLAIN:
1665 return $text;
1667 default:
1669 /// Fix non standard entity notations
1670 $text = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $text);
1671 $text = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $text);
1673 /// Remove tags that are not allowed
1674 $text = strip_tags($text, $ALLOWED_TAGS);
1676 /// Clean up embedded scripts and , using kses
1677 $text = cleanAttributes($text);
1679 /// Remove script events
1680 $text = eregi_replace("([^a-z])language([[:space:]]*)=", "\\1Xlanguage=", $text);
1681 $text = eregi_replace("([^a-z])on([a-z]+)([[:space:]]*)=", "\\1Xon\\2=", $text);
1683 return $text;
1688 * This function takes a string and examines it for HTML tags.
1689 * If tags are detected it passes the string to a helper function {@link cleanAttributes2()}
1690 * which checks for attributes and filters them for malicious content
1691 * 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie
1693 * @param string $str The string to be examined for html tags
1694 * @return string
1696 function cleanAttributes($str){
1697 $result = preg_replace_callback(
1698 '%(<[^>]*(>|$)|>)%m', #search for html tags
1699 "cleanAttributes2",
1700 $str
1702 return $result;
1706 * This function takes a string with an html tag and strips out any unallowed
1707 * protocols e.g. javascript:
1708 * It calls ancillary functions in kses which are prefixed by kses
1709 * 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie
1711 * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st
1712 * element the html to be cleared
1713 * @return string
1715 function cleanAttributes2($htmlArray){
1717 global $CFG, $ALLOWED_PROTOCOLS;
1718 require_once($CFG->libdir .'/kses.php');
1720 $htmlTag = $htmlArray[1];
1721 if (substr($htmlTag, 0, 1) != '<') {
1722 return '&gt;'; //a single character ">" detected
1724 if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) {
1725 return ''; // It's seriously malformed
1727 $slash = trim($matches[1]); //trailing xhtml slash
1728 $elem = $matches[2]; //the element name
1729 $attrlist = $matches[3]; // the list of attributes as a string
1731 $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
1733 $attStr = '';
1734 foreach ($attrArray as $arreach) {
1735 $arreach['name'] = strtolower($arreach['name']);
1736 if ($arreach['name'] == 'style') {
1737 $value = $arreach['value'];
1738 while (true) {
1739 $prevvalue = $value;
1740 $value = kses_no_null($value);
1741 $value = preg_replace("/\/\*.*\*\//Us", '', $value);
1742 $value = kses_decode_entities($value);
1743 $value = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $value);
1744 $value = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $value);
1745 if ($value === $prevvalue) {
1746 $arreach['value'] = $value;
1747 break;
1750 $arreach['value'] = preg_replace("/j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t/i", "Xjavascript", $arreach['value']);
1751 $arreach['value'] = preg_replace("/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n/i", "Xexpression", $arreach['value']);
1752 } else if ($arreach['name'] == 'href') {
1753 //Adobe Acrobat Reader XSS protection
1754 $arreach['value'] = preg_replace('/(\.(pdf|fdf|xfdf|xdp|xfd))[^a-z0-9_\.\-].*$/i', '$1', $arreach['value']);
1756 $attStr .= ' '.$arreach['name'].'="'.$arreach['value'].'"';
1759 $xhtml_slash = '';
1760 if (preg_match('%/\s*$%', $attrlist)) {
1761 $xhtml_slash = ' /';
1763 return '<'. $slash . $elem . $attStr . $xhtml_slash .'>';
1767 * Replaces all known smileys in the text with image equivalents
1769 * @uses $CFG
1770 * @param string $text Passed by reference. The string to search for smily strings.
1771 * @return string
1773 function replace_smilies(&$text) {
1775 global $CFG;
1777 /// this builds the mapping array only once
1778 static $runonce = false;
1779 static $e = array();
1780 static $img = array();
1781 static $emoticons = array(
1782 ':-)' => 'smiley',
1783 ':)' => 'smiley',
1784 ':-D' => 'biggrin',
1785 ';-)' => 'wink',
1786 ':-/' => 'mixed',
1787 'V-.' => 'thoughtful',
1788 ':-P' => 'tongueout',
1789 'B-)' => 'cool',
1790 '^-)' => 'approve',
1791 '8-)' => 'wideeyes',
1792 ':o)' => 'clown',
1793 ':-(' => 'sad',
1794 ':(' => 'sad',
1795 '8-.' => 'shy',
1796 ':-I' => 'blush',
1797 ':-X' => 'kiss',
1798 '8-o' => 'surprise',
1799 'P-|' => 'blackeye',
1800 '8-[' => 'angry',
1801 'xx-P' => 'dead',
1802 '|-.' => 'sleepy',
1803 '}-]' => 'evil',
1804 '(h)' => 'heart',
1805 '(heart)' => 'heart',
1806 '(y)' => 'yes',
1807 '(n)' => 'no',
1808 '(martin)' => 'martin',
1809 '( )' => 'egg'
1812 if ($runonce == false) { /// After the first time this is not run again
1813 foreach ($emoticons as $emoticon => $image){
1814 $alttext = get_string($image, 'pix');
1816 $e[] = $emoticon;
1817 $img[] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $CFG->pixpath .'/s/'. $image .'.gif" />';
1819 $runonce = true;
1822 // Exclude from transformations all the code inside <script> tags
1823 // Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
1824 // Based on code from glossary fiter by Williams Castillo.
1825 // - Eloy
1827 // Detect all the <script> zones to take out
1828 $excludes = array();
1829 preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
1831 // Take out all the <script> zones from text
1832 foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
1833 $excludes['<+'.$key.'+>'] = $value;
1835 if ($excludes) {
1836 $text = str_replace($excludes,array_keys($excludes),$text);
1839 /// this is the meat of the code - this is run every time
1840 $text = str_replace($e, $img, $text);
1842 // Recover all the <script> zones to text
1843 if ($excludes) {
1844 $text = str_replace(array_keys($excludes),$excludes,$text);
1849 * Given plain text, makes it into HTML as nicely as possible.
1850 * May contain HTML tags already
1852 * @uses $CFG
1853 * @param string $text The string to convert.
1854 * @param boolean $smiley Convert any smiley characters to smiley images?
1855 * @param boolean $para If true then the returned string will be wrapped in paragraph tags
1856 * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
1857 * @return string
1860 function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
1863 global $CFG;
1865 /// Remove any whitespace that may be between HTML tags
1866 $text = eregi_replace(">([[:space:]]+)<", "><", $text);
1868 /// Remove any returns that precede or follow HTML tags
1869 $text = eregi_replace("([\n\r])<", " <", $text);
1870 $text = eregi_replace(">([\n\r])", "> ", $text);
1872 convert_urls_into_links($text);
1874 /// Make returns into HTML newlines.
1875 if ($newlines) {
1876 $text = nl2br($text);
1879 /// Turn smileys into images.
1880 if ($smiley) {
1881 replace_smilies($text);
1884 /// Wrap the whole thing in a paragraph tag if required
1885 if ($para) {
1886 return '<p>'.$text.'</p>';
1887 } else {
1888 return $text;
1893 * Given Markdown formatted text, make it into XHTML using external function
1895 * @uses $CFG
1896 * @param string $text The markdown formatted text to be converted.
1897 * @return string Converted text
1899 function markdown_to_html($text) {
1900 global $CFG;
1902 require_once($CFG->libdir .'/markdown.php');
1904 return Markdown($text);
1908 * Given HTML text, make it into plain text using external function
1910 * @uses $CFG
1911 * @param string $html The text to be converted.
1912 * @return string
1914 function html_to_text($html) {
1916 global $CFG;
1918 require_once($CFG->libdir .'/html2text.php');
1920 return html2text($html);
1924 * Given some text this function converts any URLs it finds into HTML links
1926 * @param string $text Passed in by reference. The string to be searched for urls.
1928 function convert_urls_into_links(&$text) {
1929 /// Make lone URLs into links. eg http://moodle.com/
1930 $text = eregi_replace("([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])",
1931 "\\1<a href=\"\\2://\\3\\4\" target=\"_blank\">\\2://\\3\\4</a>", $text);
1933 /// eg www.moodle.com
1934 $text = eregi_replace("([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])",
1935 "\\1<a href=\"http://www.\\2\\3\" target=\"_blank\">www.\\2\\3</a>", $text);
1939 * This function will highlight search words in a given string
1940 * It cares about HTML and will not ruin links. It's best to use
1941 * this function after performing any conversions to HTML.
1942 * Function found here: http://forums.devshed.com/t67822/scdaa2d1c3d4bacb4671d075ad41f0854.html
1944 * @param string $needle The string to search for
1945 * @param string $haystack The string to search for $needle in
1946 * @param int $case ?
1947 * @return string
1948 * @todo Finish documenting this function
1950 function highlight($needle, $haystack, $case=0,
1951 $left_string='<span class="highlight">', $right_string='</span>') {
1952 if (empty($needle)) {
1953 return $haystack;
1956 //$list_of_words = eregi_replace("[^-a-zA-Z0-9&.']", " ", $needle); // bug 3101
1957 $list_of_words = $needle;
1958 $list_array = explode(' ', $list_of_words);
1959 for ($i=0; $i<sizeof($list_array); $i++) {
1960 if (strlen($list_array[$i]) == 1) {
1961 $list_array[$i] = '';
1964 $list_of_words = implode(' ', $list_array);
1965 $list_of_words_cp = $list_of_words;
1966 $final = array();
1967 preg_match_all('/<(.+?)>/is',$haystack,$list_of_words);
1969 foreach (array_unique($list_of_words[0]) as $key=>$value) {
1970 $final['<|'.$key.'|>'] = $value;
1973 $haystack = str_replace($final,array_keys($final),$haystack);
1974 $list_of_words_cp = eregi_replace(' +', '|', $list_of_words_cp);
1976 if ($list_of_words_cp{0}=='|') {
1977 $list_of_words_cp{0} = '';
1979 if ($list_of_words_cp{strlen($list_of_words_cp)-1}=='|') {
1980 $list_of_words_cp{strlen($list_of_words_cp)-1}='';
1983 $list_of_words_cp = trim($list_of_words_cp);
1985 if ($list_of_words_cp) {
1987 $list_of_words_cp = "(". $list_of_words_cp .")";
1989 if (!$case){
1990 $haystack = eregi_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1991 } else {
1992 $haystack = ereg_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1995 $haystack = str_replace(array_keys($final),$final,$haystack);
1997 return $haystack;
2001 * This function will highlight instances of $needle in $haystack
2002 * It's faster that the above function and doesn't care about
2003 * HTML or anything.
2005 * @param string $needle The string to search for
2006 * @param string $haystack The string to search for $needle in
2007 * @return string
2009 function highlightfast($needle, $haystack) {
2011 $parts = explode(moodle_strtolower($needle), moodle_strtolower($haystack));
2013 $pos = 0;
2015 foreach ($parts as $key => $part) {
2016 $parts[$key] = substr($haystack, $pos, strlen($part));
2017 $pos += strlen($part);
2019 $parts[$key] .= '<span class="highlight">'.substr($haystack, $pos, strlen($needle)).'</span>';
2020 $pos += strlen($needle);
2023 return (join('', $parts));
2027 * Return a string containing 'lang', xml:lang and optionally 'dir' HTML attributes.
2028 * Internationalisation, for print_header and backup/restorelib.
2029 * @param $dir Default false.
2030 * @return string Attributes.
2032 function get_html_lang($dir = false) {
2033 $direction = '';
2034 if ($dir) {
2035 if (get_string('thisdirection') == 'rtl') {
2036 $direction = ' dir="rtl"';
2037 } else {
2038 $direction = ' dir="ltr"';
2041 //Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag.
2042 $language = str_replace('_', '-', str_replace('_utf8', '', current_language()));
2043 @header('Content-Language: '.$language);
2044 return ($direction.' lang="'.$language.'" xml:lang="'.$language.'"');
2048 /// STANDARD WEB PAGE PARTS ///////////////////////////////////////////////////
2051 * Print a standard header
2053 * @uses $USER
2054 * @uses $CFG
2055 * @uses $SESSION
2056 * @param string $title Appears at the top of the window
2057 * @param string $heading Appears at the top of the page
2058 * @param string $navigation Premade navigation string (for use as breadcrumbs links)
2059 * @param string $focus Indicates form element to get cursor focus on load eg inputform.password
2060 * @param string $meta Meta tags to be added to the header
2061 * @param boolean $cache Should this page be cacheable?
2062 * @param string $button HTML code for a button (usually for module editing)
2063 * @param string $menu HTML code for a popup menu
2064 * @param boolean $usexml use XML for this page
2065 * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
2066 * @param bool $return If true, return the visible elements of the header instead of echoing them.
2068 function print_header ($title='', $heading='', $navigation='', $focus='',
2069 $meta='', $cache=true, $button='&nbsp;', $menu='',
2070 $usexml=false, $bodytags='', $return=false) {
2072 global $USER, $CFG, $THEME, $SESSION, $ME, $SITE, $COURSE;
2074 $heading = format_string($heading); // Fix for MDL-8582
2076 /// This makes sure that the header is never repeated twice on a page
2077 if (defined('HEADER_PRINTED')) {
2078 debugging('print_header() was called more than once - this should not happen. Please check the code for this page closely. Note: error() and redirect() are now safe to call after print_header().');
2079 return;
2081 define('HEADER_PRINTED', 'true');
2084 /// Add the required stylesheets
2085 $stylesheetshtml = '';
2086 foreach ($CFG->stylesheets as $stylesheet) {
2087 $stylesheetshtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
2089 $meta = $stylesheetshtml.$meta;
2092 /// Add the meta page from the themes if any were requested
2094 $metapage = '';
2096 if (!isset($THEME->standardmetainclude) || $THEME->standardmetainclude) {
2097 ob_start();
2098 include_once($CFG->dirroot.'/theme/standard/meta.php');
2099 $metapage .= ob_get_contents();
2100 ob_end_clean();
2103 if ($THEME->parent && (!isset($THEME->parentmetainclude) || $THEME->parentmetainclude)) {
2104 if (file_exists($CFG->dirroot.'/theme/'.$THEME->parent.'/meta.php')) {
2105 ob_start();
2106 include_once($CFG->dirroot.'/theme/'.$THEME->parent.'/meta.php');
2107 $metapage .= ob_get_contents();
2108 ob_end_clean();
2112 if (!isset($THEME->metainclude) || $THEME->metainclude) {
2113 if (file_exists($CFG->dirroot.'/theme/'.current_theme().'/meta.php')) {
2114 ob_start();
2115 include_once($CFG->dirroot.'/theme/'.current_theme().'/meta.php');
2116 $metapage .= ob_get_contents();
2117 ob_end_clean();
2121 $meta = $meta."\n".$metapage;
2124 /// Add the required JavaScript Libraries
2125 $meta .= "\n".require_js();
2128 if ($navigation == 'home') {
2129 $home = true;
2130 $navigation = '';
2131 } else {
2132 $home = false;
2135 /// This is another ugly hack to make navigation elements available to print_footer later
2136 $THEME->title = $title;
2137 $THEME->heading = $heading;
2138 $THEME->navigation = $navigation;
2139 $THEME->button = $button;
2140 $THEME->menu = $menu;
2141 $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
2143 if ($button == '') {
2144 $button = '&nbsp;';
2147 if (!$menu and $navigation) {
2148 if (empty($CFG->loginhttps)) {
2149 $wwwroot = $CFG->wwwroot;
2150 } else {
2151 $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
2153 $menu = user_login_string($COURSE);
2156 if (isset($SESSION->justloggedin)) {
2157 unset($SESSION->justloggedin);
2158 if (!empty($CFG->displayloginfailures)) {
2159 if (!empty($USER->username) and $USER->username != 'guest') {
2160 if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
2161 $menu .= '&nbsp;<font size="1">';
2162 if (empty($count->accounts)) {
2163 $menu .= get_string('failedloginattempts', '', $count);
2164 } else {
2165 $menu .= get_string('failedloginattemptsall', '', $count);
2167 if (has_capability('moodle/site:viewreports', get_context_instance(CONTEXT_SYSTEM, SITEID))) {
2168 $menu .= ' (<a href="'.$CFG->wwwroot.'/course/report/log/index.php'.
2169 '?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
2171 $menu .= '</font>';
2178 $meta = '<meta http-equiv="content-type" content="text/html; charset=utf-8" />'. "\n". $meta ."\n";
2179 if (!$usexml) {
2180 @header('Content-type: text/html; charset=utf-8');
2183 //Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag.
2184 $direction = get_html_lang($dir=true);
2186 if ($cache) { // Allow caching on "back" (but not on normal clicks)
2187 @header('Cache-Control: private, pre-check=0, post-check=0, max-age=0');
2188 @header('Pragma: no-cache');
2189 @header('Expires: ');
2190 } else { // Do everything we can to always prevent clients and proxies caching
2191 @header('Cache-Control: no-store, no-cache, must-revalidate');
2192 @header('Cache-Control: post-check=0, pre-check=0', false);
2193 @header('Pragma: no-cache');
2194 @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
2195 @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
2197 $meta .= "\n<meta http-equiv=\"pragma\" content=\"no-cache\" />";
2198 $meta .= "\n<meta http-equiv=\"expires\" content=\"0\" />";
2200 @header('Accept-Ranges: none');
2202 if (empty($usexml)) {
2203 $direction = ' xmlns="http://www.w3.org/1999/xhtml"'. $direction; // See debug_header
2204 } else {
2205 $currentlanguage = current_language();
2206 $mathplayer = preg_match("/MathPlayer/i", $_SERVER['HTTP_USER_AGENT']);
2207 if(!$mathplayer) {
2208 header('Content-Type: application/xhtml+xml');
2210 echo '<?xml version="1.0" ?>'."\n";
2211 if (!empty($CFG->xml_stylesheets)) {
2212 $stylesheets = explode(';', $CFG->xml_stylesheets);
2213 foreach ($stylesheets as $stylesheet) {
2214 echo '<?xml-stylesheet type="text/xsl" href="'. $CFG->wwwroot .'/'. $stylesheet .'" ?>' . "\n";
2217 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1';
2218 if (!empty($CFG->xml_doctype_extra)) {
2219 echo ' plus '. $CFG->xml_doctype_extra;
2221 echo '//' . strtoupper($currentlanguage) . '" "'. $CFG->xml_dtd .'">'."\n";
2222 $direction = " xmlns=\"http://www.w3.org/1999/xhtml\"
2223 xmlns:math=\"http://www.w3.org/1998/Math/MathML\"
2224 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
2225 $direction";
2226 if($mathplayer) {
2227 $meta .= '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n";
2228 $meta .= '<!--comment required to prevent this becoming an empty tag-->'."\n";
2229 $meta .= '</object>'."\n";
2230 $meta .= '<?import namespace="math" implementation="#mathplayer" ?>' . "\n";
2234 // Clean up the title
2236 $title = strip_tags(format_string($title)); // fix for MDL-8582
2237 $title = str_replace('"', '&quot;', $title);
2239 // Create class and id for this page
2241 page_id_and_class($pageid, $pageclass);
2243 $pageclass .= ' course-'.$COURSE->id;
2245 if (($pageid != 'site-index') && ($pageid != 'course-view') &&
2246 (strstr($pageid, 'admin') === FALSE)) {
2247 $pageclass .= ' nocoursepage';
2250 if (!isloggedin()) {
2251 $pageclass .= ' notloggedin';
2254 if (!empty($USER->editing)) {
2255 $pageclass .= ' editing';
2258 if (!empty($CFG->blocksdrag)) {
2259 $pageclass .= ' drag';
2262 $bodytags .= ' class="'.$pageclass.'" id="'.$pageid.'"';
2264 ob_start();
2265 include($CFG->header);
2266 $output = ob_get_contents();
2267 ob_end_clean();
2269 $output = force_strict_header($output);
2271 if (!empty($CFG->messaging)) {
2272 $output .= message_popup_window();
2275 if ($return) {
2276 return $output;
2277 } else {
2278 echo $output;
2283 * Debugging aid: serve page as 'application/xhtml+xml' where possible,
2284 * and substitute the XHTML strict document type.
2285 * Note, requires the 'xmlns' fix in function print_header above.
2286 * See: http://tracker.moodle.org/browse/MDL-7883
2287 * TODO:
2289 function force_strict_header($output) {
2290 global $CFG;
2291 $strict = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
2292 $xsl = '/lib/xhtml.xsl';
2294 if (!headers_sent() && debugging(NULL, DEBUG_DEVELOPER)) { // In developer debugging, the browser will barf
2295 $ctype = 'Content-Type: ';
2296 $prolog= "<?xml version='1.0' encoding='utf-8'?>\n";
2298 if (isset($_SERVER['HTTP_ACCEPT'])
2299 && false !== strpos($_SERVER['HTTP_ACCEPT'], 'application/xhtml+xml')) {
2300 //|| false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') //Safari "Entity 'copy' not defined".
2301 // Firefox et al.
2302 $ctype .= 'application/xhtml+xml';
2303 $prolog .= "<!--\n DEBUG: $ctype \n-->\n";
2305 } else if (file_exists($CFG->dirroot.$xsl)
2306 && preg_match('/MSIE.*Windows NT/', $_SERVER['HTTP_USER_AGENT'])) {
2307 // XSL hack for IE 5+ on Windows.
2308 //$www_xsl = preg_replace('/(http:\/\/.+?\/).*/', '', $CFG->wwwroot) .$xsl;
2309 $www_xsl = $CFG->wwwroot .$xsl;
2310 $ctype .= 'application/xml';
2311 $prolog .= "<?xml-stylesheet type='text/xsl' href='$www_xsl'?>\n";
2312 $prolog .= "<!--\n DEBUG: $ctype \n-->\n";
2314 } else {
2315 //ELSE: Mac/IE, old/non-XML browsers.
2316 $ctype .= 'text/html';
2317 $prolog = '';
2319 @header($ctype.'; charset=utf-8');
2320 $output = $prolog . $output;
2322 // Test parser error-handling.
2323 if (isset($_GET['error'])) {
2324 $output .= "__ TEST: XML well-formed error < __\n";
2328 $output = preg_replace('/(<!DOCTYPE.+?>)/s', $strict, $output); // Always change the DOCTYPE to Strict 1.0
2330 return $output;
2336 * This version of print_header is simpler because the course name does not have to be
2337 * provided explicitly in the strings. It can be used on the site page as in courses
2338 * Eventually all print_header could be replaced by print_header_simple
2340 * @param string $title Appears at the top of the window
2341 * @param string $heading Appears at the top of the page
2342 * @param string $navigation Premade navigation string (for use as breadcrumbs links)
2343 * @param string $focus Indicates form element to get cursor focus on load eg inputform.password
2344 * @param string $meta Meta tags to be added to the header
2345 * @param boolean $cache Should this page be cacheable?
2346 * @param string $button HTML code for a button (usually for module editing)
2347 * @param string $menu HTML code for a popup menu
2348 * @param boolean $usexml use XML for this page
2349 * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
2350 * @param bool $return If true, return the visible elements of the header instead of echoing them.
2352 function print_header_simple($title='', $heading='', $navigation='', $focus='', $meta='',
2353 $cache=true, $button='&nbsp;', $menu='', $usexml=false, $bodytags='', $return=false) {
2355 global $COURSE, $CFG;
2357 $shortname ='';
2358 if ($COURSE->id != SITEID) {
2359 $shortname = '<a href="'.$CFG->wwwroot.'/course/view.php?id='. $COURSE->id .'">'. $COURSE->shortname .'</a> ->';
2362 $output = print_header($COURSE->shortname .': '. $title, $COURSE->fullname .' '. $heading, $shortname.' '. $navigation, $focus, $meta,
2363 $cache, $button, $menu, $usexml, $bodytags, true);
2365 if ($return) {
2366 return $output;
2367 } else {
2368 echo $output;
2374 * Can provide a course object to make the footer contain a link to
2375 * to the course home page, otherwise the link will go to the site home
2377 * @uses $CFG
2378 * @uses $USER
2379 * @param course $course {@link $COURSE} object containing course information
2380 * @param ? $usercourse ?
2381 * @todo Finish documenting this function
2383 function print_footer($course=NULL, $usercourse=NULL, $return=false) {
2384 global $USER, $CFG, $THEME, $COURSE;
2386 /// Course links
2387 if ($course) {
2388 if (is_string($course) && $course == 'none') { // Don't print any links etc
2389 $homelink = '';
2390 $loggedinas = '';
2391 $home = false;
2392 } else if (is_string($course) && $course == 'home') { // special case for site home page - please do not remove
2393 $course = get_site();
2394 $homelink = '<div class="sitelink">'.
2395 '<a title="moodle '. $CFG->release .' ('. $CFG->version .')" href="http://moodle.org/">'.
2396 '<br /><img style="width:100px;height:30px" src="pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
2397 $home = true;
2398 } else {
2399 $homelink = '<div class="homelink"><a '.$CFG->frametarget.' href="'.$CFG->wwwroot.
2400 '/course/view.php?id='.$course->id.'">'.format_string($course->shortname).'</a></div>';
2401 $home = false;
2403 } else {
2404 $course = get_site(); // Set course as site course by default
2405 $homelink = '<div class="homelink"><a '.$CFG->frametarget.' href="'.$CFG->wwwroot.'/">'.get_string('home').'</a></div>';
2406 $home = false;
2409 /// Set up some other navigation links (passed from print_header by ugly hack)
2410 $menu = isset($THEME->menu) ? str_replace('navmenu', 'navmenufooter', $THEME->menu) : '';
2411 $title = isset($THEME->title) ? $THEME->title : '';
2412 $button = isset($THEME->button) ? $THEME->button : '';
2413 $heading = isset($THEME->heading) ? $THEME->heading : '';
2414 $navigation = isset($THEME->navigation) ? $THEME->navigation : '';
2415 $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
2418 /// Set the user link if necessary
2419 if (!$usercourse and is_object($course)) {
2420 $usercourse = $course;
2423 if (!isset($loggedinas)) {
2424 $loggedinas = user_login_string($usercourse, $USER);
2427 if ($loggedinas == $menu) {
2428 $menu = '';
2431 /// Provide some performance info if required
2432 $performanceinfo = '';
2433 if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
2434 $perf = get_performance_info();
2435 if (defined('MDL_PERFTOLOG')) {
2436 error_log("PERF: " . $perf['txt']);
2438 if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) {
2439 $performanceinfo = $perf['html'];
2444 /// Include the actual footer file
2446 ob_start();
2447 include($CFG->footer);
2448 $output = ob_get_contents();
2449 ob_end_clean();
2451 if ($return) {
2452 return $output;
2453 } else {
2454 echo $output;
2459 * Returns the name of the current theme
2461 * @uses $CFG
2462 * @param $USER
2463 * @param $SESSION
2464 * @return string
2466 function current_theme() {
2467 global $CFG, $USER, $SESSION, $COURSE;
2469 if (!empty($CFG->pagetheme)) { // Page theme is for special page-only themes set by code
2470 return $CFG->pagetheme;
2472 } else if (!empty($CFG->allowcoursethemes) and !empty($COURSE->theme)) { // Course themes override others
2473 return $COURSE->theme;
2475 } else if (!empty($SESSION->theme)) { // Session theme can override other settings
2476 return $SESSION->theme;
2478 } else if (!empty($CFG->allowuserthemes) and !empty($USER->theme)) { // User theme can override site theme
2479 return $USER->theme;
2481 } else {
2482 return $CFG->theme;
2488 * This function is called by stylesheets to set up the header
2489 * approriately as well as the current path
2491 * @uses $CFG
2492 * @param int $lastmodified ?
2493 * @param int $lifetime ?
2494 * @param string $thename ?
2496 function style_sheet_setup($lastmodified=0, $lifetime=300, $themename='', $forceconfig='', $lang='') {
2498 global $CFG, $THEME;
2500 // Fix for IE6 caching - we don't want the filemtime('styles.php'), instead use now.
2501 $lastmodified = time();
2503 header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmodified) . ' GMT');
2504 header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
2505 header('Cache-Control: max-age='. $lifetime);
2506 header('Pragma: ');
2507 header('Content-type: text/css'); // Correct MIME type
2509 $DEFAULT_SHEET_LIST = array('styles_layout', 'styles_fonts', 'styles_color');
2511 if (empty($themename)) {
2512 $themename = current_theme(); // So we have something. Normally not needed.
2513 } else {
2514 $themename = clean_param($themename, PARAM_SAFEDIR);
2517 if (!empty($forceconfig)) { // Page wants to use the config from this theme instead
2518 unset($THEME);
2519 include($CFG->themedir.'/'.$forceconfig.'/'.'config.php');
2522 /// If this is the standard theme calling us, then find out what sheets we need
2524 if ($themename == 'standard') {
2525 if (!isset($THEME->standardsheets) or $THEME->standardsheets === true) { // Use all the sheets we have
2526 $THEME->sheets = $DEFAULT_SHEET_LIST;
2527 } else if (empty($THEME->standardsheets)) { // We can stop right now!
2528 echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2529 exit;
2530 } else { // Use the provided subset only
2531 $THEME->sheets = $THEME->standardsheets;
2534 /// If we are a parent theme, then check for parent definitions
2536 } else if (!empty($THEME->parent) && $themename == $THEME->parent) {
2537 if (!isset($THEME->parentsheets) or $THEME->parentsheets === true) { // Use all the sheets we have
2538 $THEME->sheets = $DEFAULT_SHEET_LIST;
2539 } else if (empty($THEME->parentsheets)) { // We can stop right now!
2540 echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2541 exit;
2542 } else { // Use the provided subset only
2543 $THEME->sheets = $THEME->parentsheets;
2547 /// Work out the last modified date for this theme
2549 foreach ($THEME->sheets as $sheet) {
2550 if (file_exists($CFG->themedir.'/'.$themename.'/'.$sheet.'.css')) {
2551 $sheetmodified = filemtime($CFG->themedir.'/'.$themename.'/'.$sheet.'.css');
2552 if ($sheetmodified > $lastmodified) {
2553 $lastmodified = $sheetmodified;
2559 /// Get a list of all the files we want to include
2560 $files = array();
2562 foreach ($THEME->sheets as $sheet) {
2563 $files[] = array($CFG->themedir, $themename.'/'.$sheet.'.css');
2566 if ($themename == 'standard') { // Add any standard styles included in any modules
2567 if (!empty($THEME->modsheets)) { // Search for styles.php within activity modules
2568 if ($mods = get_list_of_plugins('mod')) {
2569 foreach ($mods as $mod) {
2570 if (file_exists($CFG->dirroot.'/mod/'.$mod.'/styles.php')) {
2571 $files[] = array($CFG->dirroot, '/mod/'.$mod.'/styles.php');
2577 if (!empty($THEME->blocksheets)) { // Search for styles.php within block modules
2578 if ($mods = get_list_of_plugins('blocks')) {
2579 foreach ($mods as $mod) {
2580 if (file_exists($CFG->dirroot.'/blocks/'.$mod.'/styles.php')) {
2581 $files[] = array($CFG->dirroot, '/blocks/'.$mod.'/styles.php');
2587 if (!isset($THEME->courseformatsheets) || $THEME->courseformatsheets) { // Search for styles.php in course formats
2588 if ($mods = get_list_of_plugins('format','',$CFG->dirroot.'/course')) {
2589 foreach ($mods as $mod) {
2590 if (file_exists($CFG->dirroot.'/course/format/'.$mod.'/styles.php')) {
2591 $files[] = array($CFG->dirroot, '/course/format/'.$mod.'/styles.php');
2597 if (!empty($THEME->langsheets)) { // Search for styles.php within the current language
2598 if (file_exists($CFG->dirroot.'/lang/'.$lang.'/styles.php')) {
2599 $files[] = array($CFG->dirroot, '/lang/'.$lang.'/styles.php');
2605 if ($files) {
2606 /// Produce a list of all the files first
2607 echo '/**************************************'."\n";
2608 echo ' * THEME NAME: '.$themename."\n *\n";
2609 echo ' * Files included in this sheet:'."\n *\n";
2610 foreach ($files as $file) {
2611 echo ' * '.$file[1]."\n";
2613 echo ' **************************************/'."\n\n";
2616 /// check if csscobstants is set
2617 if (!empty($THEME->cssconstants)) {
2618 require_once("$CFG->libdir/cssconstants.php");
2619 /// Actually collect all the files in order.
2620 $css = '';
2621 foreach ($files as $file) {
2622 $css .= '/***** '.$file[1].' start *****/'."\n\n";
2623 $css .= file_get_contents($file[0].'/'.$file[1]);
2624 $ccs .= '/***** '.$file[1].' end *****/'."\n\n";
2626 /// replace css_constants with their values
2627 echo replace_cssconstants($css);
2628 } else {
2629 /// Actually output all the files in order.
2630 foreach ($files as $file) {
2631 echo '/***** '.$file[1].' start *****/'."\n\n";
2632 @include_once($file[0].'/'.$file[1]);
2633 echo '/***** '.$file[1].' end *****/'."\n\n";
2638 return $CFG->themewww.'/'.$themename; // Only to help old themes (1.4 and earlier)
2642 function theme_setup($theme = '', $params=NULL) {
2643 /// Sets up global variables related to themes
2645 global $CFG, $THEME, $SESSION, $USER, $HTTPSPAGEREQUIRED;
2647 if (empty($theme)) {
2648 $theme = current_theme();
2651 /// If the theme doesn't exist for some reason then revert to standardwhite
2652 if (!file_exists($CFG->themedir .'/'. $theme .'/config.php')) {
2653 $CFG->theme = $theme = 'standardwhite';
2656 /// Load up the theme config
2657 $THEME = NULL; // Just to be sure
2658 include($CFG->themedir .'/'. $theme .'/config.php'); // Main config for current theme
2660 /// Put together the parameters
2661 if (!$params) {
2662 $params = array();
2664 if ($theme != $CFG->theme) {
2665 $params[] = 'forceconfig='.$theme;
2668 /// Force language too if required
2669 if (!empty($THEME->langsheets)) {
2670 $params[] = 'lang='.current_language();
2673 /// Convert params to string
2674 if ($params) {
2675 $paramstring = '?'.implode('&', $params);
2676 } else {
2677 $paramstring = '';
2680 /// Set up image paths
2681 if(isset($CFG->smartpix) && $CFG->smartpix==1) {
2682 if($CFG->slasharguments) { // Use this method if possible for better caching
2683 $extra='';
2684 } else {
2685 $extra='?file=';
2688 $CFG->pixpath = $CFG->wwwroot. '/pix/smartpix.php'.$extra.'/'.$theme;
2689 $CFG->modpixpath = $CFG->wwwroot .'/pix/smartpix.php'.$extra.'/'.$theme.'/mod';
2690 } else if (empty($THEME->custompix)) { // Could be set in the above file
2691 $CFG->pixpath = $CFG->wwwroot .'/pix';
2692 $CFG->modpixpath = $CFG->wwwroot .'/mod';
2693 } else {
2694 $CFG->pixpath = $CFG->themewww .'/'. $theme .'/pix';
2695 $CFG->modpixpath = $CFG->themewww .'/'. $theme .'/pix/mod';
2698 /// Header and footer paths
2699 $CFG->header = $CFG->themedir .'/'. $theme .'/header.html';
2700 $CFG->footer = $CFG->themedir .'/'. $theme .'/footer.html';
2702 /// Define stylesheet loading order
2703 $CFG->stylesheets = array();
2704 if ($theme != 'standard') { /// The standard sheet is always loaded first
2705 $CFG->stylesheets[] = $CFG->themewww.'/standard/styles.php'.$paramstring;
2707 if (!empty($THEME->parent)) { /// Parent stylesheets are loaded next
2708 $CFG->stylesheets[] = $CFG->themewww.'/'.$THEME->parent.'/styles.php'.$paramstring;
2710 $CFG->stylesheets[] = $CFG->themewww.'/'.$theme.'/styles.php'.$paramstring;
2712 /// We have to change some URLs in styles if we are in a $HTTPSPAGEREQUIRED page
2713 if (!empty($HTTPSPAGEREQUIRED)) {
2714 $CFG->themewww = str_replace('http:', 'https:', $CFG->themewww);
2715 $CFG->pixpath = str_replace('http:', 'https:', $CFG->pixpath);
2716 $CFG->modpixpath = str_replace('http:', 'https:', $CFG->modpixpath);
2717 foreach ($CFG->stylesheets as $key => $stylesheet) {
2718 $CFG->stylesheets[$key] = str_replace('http:', 'https:', $stylesheet);
2726 * Returns text to be displayed to the user which reflects their login status
2728 * @uses $CFG
2729 * @uses $USER
2730 * @param course $course {@link $COURSE} object containing course information
2731 * @param user $user {@link $USER} object containing user information
2732 * @return string
2734 function user_login_string($course=NULL, $user=NULL) {
2735 global $USER, $CFG, $SITE;
2737 if (empty($user) and !empty($USER->id)) {
2738 $user = $USER;
2741 if (empty($course)) {
2742 $course = $SITE;
2745 if (isset($user->realuser)) {
2746 if ($realuser = get_record('user', 'id', $user->realuser)) {
2747 $fullname = fullname($realuser, true);
2748 $realuserinfo = " [<a $CFG->frametarget
2749 href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&amp;return=1\">$fullname</a>] ";
2751 } else {
2752 $realuserinfo = '';
2755 if (empty($CFG->loginhttps)) {
2756 $wwwroot = $CFG->wwwroot;
2757 } else {
2758 $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
2761 if (empty($course->id)) {
2762 // $course->id is not defined during installation
2763 return '';
2764 } else if (isset($user->id) and $user->id) {
2765 $context = get_context_instance(CONTEXT_COURSE, $course->id);
2767 $fullname = fullname($user, true);
2768 $username = "<a $CFG->frametarget href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>";
2769 if (is_mnet_remote_user($user) and $idprovider = get_record('mnet_host', 'id', $user->mnethostid)) {
2770 $username .= " from <a $CFG->frametarget href=\"{$idprovider->wwwroot}\">{$idprovider->name}</a>";
2772 if (isset($user->username) && $user->username == 'guest') {
2773 $loggedinas = $realuserinfo.get_string('loggedinasguest').
2774 " (<a $CFG->frametarget href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
2775 } else if (!empty($user->switchrole[$context->id])) {
2776 $rolename = '';
2777 if ($role = get_record('role', 'id', $user->switchrole[$context->id])) {
2778 $rolename = ': '.format_string($role->name);
2780 $loggedinas = get_string('loggedinas', 'moodle', $username).$rolename.
2781 " (<a $CFG->frametarget
2782 href=\"$CFG->wwwroot/course/view.php?id=$course->id&amp;switchrole=0&amp;sesskey=".sesskey()."\">".get_string('switchrolereturn').'</a>)';
2783 } else {
2784 $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '.
2785 " (<a $CFG->frametarget href=\"$CFG->wwwroot/login/logout.php?sesskey=".sesskey()."\">".get_string('logout').'</a>)';
2787 } else {
2788 $loggedinas = get_string('loggedinnot', 'moodle').
2789 " (<a $CFG->frametarget href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
2791 return '<div class="logininfo">'.$loggedinas.'</div>';
2795 * Tests whether $THEME->rarrow, $THEME->larrow have been set (theme/-/config.php).
2796 * If not it applies sensible defaults.
2798 * Accessibility: right and left arrow Unicode characters for breadcrumb, calendar,
2799 * search forum block, etc. Important: these are 'silent' in a screen-reader
2800 * (unlike &gt; &raquo;), and must be accompanied by text.
2801 * @uses $THEME
2803 function check_theme_arrows() {
2804 global $THEME;
2806 if (!isset($THEME->rarrow) and !isset($THEME->larrow)) {
2807 // Default, looks good in Win XP/IE 6, Win/Firefox 1.5, Win/Netscape 8...
2808 // Also OK in Win 9x/2K/IE 5.x
2809 $THEME->rarrow = '&#x25BA;';
2810 $THEME->larrow = '&#x25C4;';
2811 $uagent = $_SERVER['HTTP_USER_AGENT'];
2812 if (false !== strpos($uagent, 'Opera')
2813 || false !== strpos($uagent, 'Mac')) {
2814 // Looks good in Win XP/Mac/Opera 8/9, Mac/Firefox 2, Camino, Safari.
2815 // Not broken in Mac/IE 5, Mac/Netscape 7 (?).
2816 $THEME->rarrow = '&#x25B6;';
2817 $THEME->larrow = '&#x25C0;';
2819 elseif (false !== strpos($uagent, 'Konqueror')) {
2820 $THEME->rarrow = '&rarr;';
2821 $THEME->larrow = '&larr;';
2823 elseif (isset($_SERVER['HTTP_ACCEPT_CHARSET'])
2824 && false === stripos($_SERVER['HTTP_ACCEPT_CHARSET'], 'utf-8')) {
2825 // (Win/IE 5 doesn't set ACCEPT_CHARSET, but handles Unicode.)
2826 // To be safe, non-Unicode browsers!
2827 $THEME->rarrow = '&gt;';
2828 $THEME->larrow = '&lt;';
2834 * Prints breadcrumb trail of links, called in theme/-/header.html
2836 * @uses $CFG
2837 * @param string $navigation The breadcrumb navigation string to be printed
2838 * @param string $separator The breadcrumb trail separator. The default 0 leads to the use
2839 * of $THEME->rarrow, themes could use '&rarr;', '/', or '' for a style-sheet solution.
2840 * @param boolean $return False to echo the breadcrumb string (default), true to return it.
2842 function print_navigation ($navigation, $separator=0, $return=false) {
2843 global $CFG, $THEME;
2844 $output = '';
2846 check_theme_arrows();
2847 if (0 === $separator) {
2848 $separator = $THEME->rarrow;
2850 if (!empty($separator)) {
2851 $separator = '<span class="sep">'. $separator .'</span>';
2854 if ($navigation) {
2856 if (!is_array($navigation)) {
2857 $ar = explode('->', $navigation);
2858 $navigation = array();
2860 foreach ($ar as $a) {
2861 if (strpos($a, '</a>') === false) {
2862 $navigation[] = array('title' => trim(format_string($a)), 'url' => '');
2863 } else {
2864 if (preg_match('/<a.*href="([^"]*)">(.*)<\/a>/', $a, $matches)) {
2865 $navigation[] = array('title' => trim(format_string($matches[2])), 'url' => $matches[1]);
2871 if (! $site = get_site()) {
2872 $site = new object();
2873 $site->shortname = get_string('home');
2876 //Accessibility: breadcrumb links now in a list, &raquo; replaced with a 'silent' character.
2877 $nav_text = get_string('youarehere','access');
2878 $output .= '<h2 class="accesshide">'.$nav_text."</h2><ul>\n";
2880 $output .= '<li class="first"><a '.$CFG->frametarget.' onclick="this.target=\''.$CFG->framename.'\'" href="'
2881 .$CFG->wwwroot.((!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM, SITEID))
2882 && !empty($USER->id) && !empty($CFG->mymoodleredirect) && !isguest())
2883 ? '/my' : '') .'/">'. format_string($site->shortname) ."</a></li>\n";
2886 foreach ($navigation as $navitem) {
2887 extract($navitem);
2888 $title = strip_tags(format_string($title));
2889 if (empty($url)) {
2890 $output .= '<li class="first">'."$separator $title</li>\n";
2891 } else {
2892 $output .= '<li class="first">' . $separator . ' <a '.$CFG->frametarget.' onclick="this.target=\''.$CFG->framename.'\'" href="'
2893 .$url.'">'."$title</a></li>\n";
2897 $output .= "</ul>\n";
2900 if ($return) {
2901 return $output;
2902 } else {
2903 echo $output;
2908 * Prints a string in a specified size (retained for backward compatibility)
2910 * @param string $text The text to be displayed
2911 * @param int $size The size to set the font for text display.
2913 function print_headline($text, $size=2, $return=false) {
2914 $output = print_heading($text, '', $size, true);
2915 if ($return) {
2916 return $output;
2917 } else {
2918 echo $output;
2923 * Prints text in a format for use in headings.
2925 * @param string $text The text to be displayed
2926 * @param string $align The alignment of the printed paragraph of text
2927 * @param int $size The size to set the font for text display.
2929 function print_heading($text, $align='', $size=2, $class='main', $return=false) {
2930 if ($align) {
2931 $align = ' style="text-align:'.$align.';"';
2933 if ($class) {
2934 $class = ' class="'.$class.'"';
2936 $output = "<h$size $align $class>".stripslashes_safe($text)."</h$size>";
2938 if ($return) {
2939 return $output;
2940 } else {
2941 echo $output;
2946 * Centered heading with attached help button (same title text)
2947 * and optional icon attached
2949 * @param string $text The text to be displayed
2950 * @param string $helppage The help page to link to
2951 * @param string $module The module whose help should be linked to
2952 * @param string $icon Image to display if needed
2954 function print_heading_with_help($text, $helppage, $module='moodle', $icon='', $return=false) {
2955 $output = '';
2956 $output .= '<h2 class="main help">'.$icon.stripslashes_safe($text);
2957 $output .= helpbutton($helppage, $text, $module, true, false, '', true);
2958 $output .= '</h2>';
2960 if ($return) {
2961 return $output;
2962 } else {
2963 echo $output;
2968 function print_heading_block($heading, $class='', $return=false) {
2969 //Accessibility: 'headingblock' is now H1, see theme/standard/styles_*.css: ??
2970 $output = '<h2 class="headingblock header '.$class.'">'.stripslashes($heading).'</h2>';
2972 if ($return) {
2973 return $output;
2974 } else {
2975 echo $output;
2981 * Print a link to continue on to another page.
2983 * @uses $CFG
2984 * @param string $link The url to create a link to.
2986 function print_continue($link, $return=false) {
2988 global $CFG;
2990 // in case we are logging upgrade in admin/index.php stop it
2991 if (function_exists('upgrade_log_finish')) {
2992 upgrade_log_finish();
2995 $output = '';
2997 if ($link == '') {
2998 if (!empty($_SERVER['HTTP_REFERER'])) {
2999 $link = $_SERVER['HTTP_REFERER'];
3000 $link = str_replace('&', '&amp;', $link); // make it valid XHTML
3001 } else {
3002 $link = $CFG->wwwroot .'/';
3006 $output .= '<div class="continuebutton">';
3008 $output .= print_single_button($link, NULL, get_string('continue'), 'post', $CFG->framename, true);
3009 $output .= '</div>'."\n";
3011 if ($return) {
3012 return $output;
3013 } else {
3014 echo $output;
3020 * Print a message in a standard themed box.
3021 * Replaces print_simple_box (see deprecatedlib.php)
3023 * @param string $message, the content of the box
3024 * @param string $classes, space-separated class names.
3025 * @param string $ids, space-separated id names.
3026 * @param boolean $return, return as string or just print it
3028 function print_box($message, $classes='generalbox', $ids='', $return=false) {
3030 $output = print_box_start($classes, $ids, true);
3031 $output .= stripslashes_safe($message);
3032 $output .= print_box_end(true);
3034 if ($return) {
3035 return $output;
3036 } else {
3037 echo $output;
3042 * Starts a box using divs
3043 * Replaces print_simple_box_start (see deprecatedlib.php)
3045 * @param string $classes, space-separated class names.
3046 * @param string $ids, space-separated id names.
3047 * @param boolean $return, return as string or just print it
3049 function print_box_start($classes='generalbox', $ids='', $return=false) {
3050 $output = '';
3052 if ($ids) {
3053 $ids = ' id="'.$ids.'"';
3056 $output .= '<div'.$ids.' class="box '.$classes.'">';
3058 if ($return) {
3059 return $output;
3060 } else {
3061 echo $output;
3067 * Simple function to end a box (see above)
3068 * Replaces print_simple_box_end (see deprecatedlib.php)
3070 * @param boolean $return, return as string or just print it
3072 function print_box_end($return=false) {
3073 $output = '</div>';
3074 if ($return) {
3075 return $output;
3076 } else {
3077 echo $output;
3083 * Print a self contained form with a single submit button.
3085 * @param string $link ?
3086 * @param array $options ?
3087 * @param string $label ?
3088 * @param string $method ?
3089 * @todo Finish documenting this function
3091 function print_single_button($link, $options, $label='OK', $method='get', $target='_self', $return=false) {
3092 $output = '';
3093 $link = str_replace('"', '&quot;', $link); //basic XSS protection
3094 $output .= '<div class="singlebutton">';
3095 // taking target out, will need to add later target="'.$target.'"
3096 $output .= '<form action="'. $link .'" method="'. $method .'">';
3097 $output .= '<div>';
3098 if ($options) {
3099 foreach ($options as $name => $value) {
3100 $output .= '<input type="hidden" name="'. $name .'" value="'. s($value) .'" />';
3103 $output .= '<input type="submit" value="'. s($label) .'" /></div></form></div>';
3105 if ($return) {
3106 return $output;
3107 } else {
3108 echo $output;
3114 * Print a spacer image with the option of including a line break.
3116 * @param int $height ?
3117 * @param int $width ?
3118 * @param boolean $br ?
3119 * @todo Finish documenting this function
3121 function print_spacer($height=1, $width=1, $br=true, $return=false) {
3122 global $CFG;
3123 $output = '';
3125 $output .= '<img class="spacer" height="'. $height .'" width="'. $width .'" src="'. $CFG->wwwroot .'/pix/spacer.gif" alt="" />';
3126 if ($br) {
3127 $output .= '<br />'."\n";
3130 if ($return) {
3131 return $output;
3132 } else {
3133 echo $output;
3138 * Given the path to a picture file in a course, or a URL,
3139 * this function includes the picture in the page.
3141 * @param string $path ?
3142 * @param int $courseid ?
3143 * @param int $height ?
3144 * @param int $width ?
3145 * @param string $link ?
3146 * @todo Finish documenting this function
3148 function print_file_picture($path, $courseid=0, $height='', $width='', $link='', $return=false) {
3149 global $CFG;
3150 $output = '';
3152 if ($height) {
3153 $height = 'height="'. $height .'"';
3155 if ($width) {
3156 $width = 'width="'. $width .'"';
3158 if ($link) {
3159 $output .= '<a href="'. $link .'">';
3161 if (substr(strtolower($path), 0, 7) == 'http://') {
3162 $output .= '<img style="height:'.$height.'px;width:'.$width.'px;" src="'. $path .'" />';
3164 } else if ($courseid) {
3165 $output .= '<img style="height:'.$height.'px;width:'.$width.'px;" src="';
3166 if ($CFG->slasharguments) { // Use this method if possible for better caching
3167 $output .= $CFG->wwwroot .'/file.php/'. $courseid .'/'. $path;
3168 } else {
3169 $output .= $CFG->wwwroot .'/file.php?file=/'. $courseid .'/'. $path;
3171 $output .= '" />';
3172 } else {
3173 $output .= 'Error: must pass URL or course';
3175 if ($link) {
3176 $output .= '</a>';
3179 if ($return) {
3180 return $output;
3181 } else {
3182 echo $output;
3187 * Print the specified user's avatar.
3189 * @param int $userid ?
3190 * @param int $courseid ?
3191 * @param boolean $picture Print the user picture?
3192 * @param int $size Size in pixels. Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatability
3193 * @param boolean $return If false print picture to current page, otherwise return the output as string
3194 * @param boolean $link Enclose printed image in a link to view specified course?
3195 * @param string $target link target attribute
3196 * @param boolean $alttext use username or userspecified text in image alt attribute
3197 * return string
3198 * @todo Finish documenting this function
3200 function print_user_picture($userid, $courseid, $picture, $size=0, $return=false, $link=true, $target='', $alttext=true) {
3201 global $CFG;
3203 if ($link) {
3204 if ($target) {
3205 $target=' target="_blank"';
3207 $output = '<a '.$target.' href="'. $CFG->wwwroot .'/user/view.php?id='. $userid .'&amp;course='. $courseid .'">';
3208 } else {
3209 $output = '';
3211 if (empty($size)) {
3212 $file = 'f2';
3213 $size = 35;
3214 } else if ($size === true or $size == 1) {
3215 $file = 'f1';
3216 $size = 100;
3217 } else if ($size >= 50) {
3218 $file = 'f1';
3219 } else {
3220 $file = 'f2';
3222 $class = "userpicture";
3223 if ($picture) { // Print custom user picture
3224 if ($CFG->slasharguments) { // Use this method if possible for better caching
3225 $src = $CFG->wwwroot .'/user/pix.php/'. $userid .'/'. $file .'.jpg';
3226 } else {
3227 $src = $CFG->wwwroot .'/user/pix.php?file=/'. $userid .'/'. $file .'.jpg';
3229 } else { // Print default user pictures (use theme version if available)
3230 $class .= " defaultuserpic";
3231 $src = "$CFG->pixpath/u/$file.png";
3233 $imagealt = '';
3234 if ($alttext and $user = get_record('user','id',$userid)) {
3235 if (!empty($user->imagealt)) {
3236 $imagealt = $user->imagealt;
3237 } else {
3238 $imagealt = get_string('pictureof','',fullname($user));
3242 $output .= '<img class="'.$class.'" src="'.$src.'" alt="'.s($imagealt).'" />';
3243 if ($link) {
3244 $output .= '</a>';
3247 if ($return) {
3248 return $output;
3249 } else {
3250 echo $output;
3255 * Prints a summary of a user in a nice little box.
3257 * @uses $CFG
3258 * @uses $USER
3259 * @param user $user A {@link $USER} object representing a user
3260 * @param course $course A {@link $COURSE} object representing a course
3262 function print_user($user, $course, $messageselect=false, $return=false) {
3264 global $CFG, $USER;
3266 $output = '';
3268 static $string;
3269 static $datestring;
3270 static $countries;
3272 $context = get_context_instance(CONTEXT_COURSE, $course->id);
3274 if (empty($string)) { // Cache all the strings for the rest of the page
3276 $string->email = get_string('email');
3277 $string->location = get_string('location');
3278 $string->lastaccess = get_string('lastaccess');
3279 $string->activity = get_string('activity');
3280 $string->unenrol = get_string('unenrol');
3281 $string->loginas = get_string('loginas');
3282 $string->fullprofile = get_string('fullprofile');
3283 $string->role = get_string('role');
3284 $string->name = get_string('name');
3285 $string->never = get_string('never');
3287 $datestring->day = get_string('day');
3288 $datestring->days = get_string('days');
3289 $datestring->hour = get_string('hour');
3290 $datestring->hours = get_string('hours');
3291 $datestring->min = get_string('min');
3292 $datestring->mins = get_string('mins');
3293 $datestring->sec = get_string('sec');
3294 $datestring->secs = get_string('secs');
3296 $countries = get_list_of_countries();
3299 /// Get the hidden field list
3300 if (has_capability('moodle/course:viewhiddenuserfields', $context)) {
3301 $hiddenfields = array();
3302 } else {
3303 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
3306 $output .= '<table class="userinfobox">';
3307 $output .= '<tr>';
3308 $output .= '<td class="left side">';
3309 $output .= print_user_picture($user->id, $course->id, $user->picture, true, true);
3310 $output .= '</td>';
3311 $output .= '<td class="content">';
3312 $output .= '<div class="username">'.fullname($user, has_capability('moodle/site:viewfullnames', $context)).'</div>';
3313 $output .= '<div class="info">';
3314 if (!empty($user->role) and ($user->role <> $course->teacher)) {
3315 $output .= $string->role .': '. $user->role .'<br />';
3317 if ($user->maildisplay == 1 or ($user->maildisplay == 2 and ($course->id != SITEID) and !isguest()) or
3318 has_capability('moodle/course:viewhiddenuserfields', $context)) {
3319 $output .= $string->email .': <a href="mailto:'. $user->email .'">'. $user->email .'</a><br />';
3321 if (($user->city or $user->country) and (!isset($hiddenfields['city']) or !isset($hiddenfields['country']))) {
3322 $output .= $string->location .': ';
3323 if ($user->city && !isset($hiddenfields['city'])) {
3324 $output .= $user->city;
3326 if (!empty($countries[$user->country]) && !isset($hiddenfields['country'])) {
3327 if ($user->city && !isset($hiddenfields['city'])) {
3328 $output .= ', ';
3330 $output .= $countries[$user->country];
3332 $output .= '<br />';
3335 if (!isset($hiddenfields['lastaccess'])) {
3336 if ($user->lastaccess) {
3337 $output .= $string->lastaccess .': '. userdate($user->lastaccess);
3338 $output .= '&nbsp; ('. format_time(time() - $user->lastaccess, $datestring) .')';
3339 } else {
3340 $output .= $string->lastaccess .': '. $string->never;
3343 $output .= '</div></td><td class="links">';
3344 //link to blogs
3345 if ($CFG->bloglevel > 0) {
3346 $output .= '<a href="'.$CFG->wwwroot.'/blog/index.php?userid='.$user->id.'">'.get_string('blogs','blog').'</a><br />';
3349 if (has_capability('moodle/site:viewreports', $context)) {
3350 $timemidnight = usergetmidnight(time());
3351 $output .= '<a href="'. $CFG->wwwroot .'/course/user.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->activity .'</a><br />';
3353 if (has_capability('moodle/role:assign', $context, NULL)) { // Includes admins
3354 $output .= '<a href="'. $CFG->wwwroot .'/course/unenrol.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->unenrol .'</a><br />';
3356 if ($USER->id != $user->id && has_capability('moodle/user:loginas', $context) &&
3357 ! has_capability('moodle/site:doanything', $context, $user->id, false)) {
3358 $output .= '<a href="'. $CFG->wwwroot .'/course/loginas.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->loginas .'</a><br />';
3360 $output .= '<a href="'. $CFG->wwwroot .'/user/view.php?id='. $user->id .'&amp;course='. $course->id .'">'. $string->fullprofile .'...</a>';
3362 if (!empty($messageselect)) {
3363 $output .= '<br /><input type="checkbox" name="user'.$user->id.'" /> ';
3366 $output .= '</td></tr></table>';
3368 if ($return) {
3369 return $output;
3370 } else {
3371 echo $output;
3376 * Print a specified group's avatar.
3378 * @param group $group A {@link group} object representing a group or array of groups
3379 * @param int $courseid ?
3380 * @param boolean $large ?
3381 * @param boolean $return ?
3382 * @param boolean $link ?
3383 * @return string
3384 * @todo Finish documenting this function
3386 function print_group_picture($group, $courseid, $large=false, $return=false, $link=true) {
3387 global $CFG;
3389 if (is_array($group)) {
3390 $output = '';
3391 foreach($group as $g) {
3392 $output .= print_group_picture($g, $courseid, $large, true, $link);
3394 if ($return) {
3395 return $output;
3396 } else {
3397 echo $output;
3398 return;
3402 $context = get_context_instance(CONTEXT_COURSE, $courseid);
3404 if ($group->hidepicture and !has_capability('moodle/course:managegroups', $context)) {
3405 return '';
3408 if ($link or has_capability('moodle/site:accessallgroups', $context)) {
3409 $output = '<a href="'. $CFG->wwwroot .'/user/index.php?id='. $courseid .'&amp;group='. $group->id .'">';
3410 } else {
3411 $output = '';
3413 if ($large) {
3414 $file = 'f1';
3415 $size = 100;
3416 } else {
3417 $file = 'f2';
3418 $size = 35;
3420 if ($group->picture) { // Print custom group picture
3421 if ($CFG->slasharguments) { // Use this method if possible for better caching
3422 $output .= '<img class="grouppicture" src="'.$CFG->wwwroot.'/user/pixgroup.php/'.$group->id.'/'.$file.'.jpg"'.
3423 ' style="width:'.$size.'px;height:'.$size.'px;" alt="'.s(get_string('group').' '.$group->name).'" title="'.s($group->name).'"/>';
3424 } else {
3425 $output .= '<img class="grouppicture" src="'.$CFG->wwwroot.'/user/pixgroup.php?file=/'.$group->id.'/'.$file.'.jpg"'.
3426 ' style="width:'.$size.'px;height:'.$size.'px;" alt="'.s(get_string('group').' '.$group->name).'" title="'.s($group->name).'"/>';
3429 if ($link or has_capability('moodle/site:accessallgroups', $context)) {
3430 $output .= '</a>';
3433 if ($return) {
3434 return $output;
3435 } else {
3436 echo $output;
3441 * Print a png image.
3443 * @param string $url ?
3444 * @param int $sizex ?
3445 * @param int $sizey ?
3446 * @param boolean $return ?
3447 * @param string $parameters ?
3448 * @todo Finish documenting this function
3450 function print_png($url, $sizex, $sizey, $return, $parameters='alt=""') {
3451 global $CFG;
3452 static $recentIE;
3454 if (!isset($recentIE)) {
3455 $recentIE = check_browser_version('MSIE', '5.0');
3458 if ($recentIE) { // work around the HORRIBLE bug IE has with alpha transparencies
3459 $output .= '<img src="'. $CFG->pixpath .'/spacer.gif" width="'. $sizex .'" height="'. $sizey .'"'.
3460 ' class="png" style="width: '. $sizex .'px; height: '. $sizey .'px; '.
3461 ' filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='.
3462 "'$url', sizingMethod='scale') ".
3463 ' '. $parameters .' />';
3464 } else {
3465 $output .= '<img src="'. $url .'" style="width: '. $sizex .'px; height: '. $sizey .'px; '. $parameters .' />';
3468 if ($return) {
3469 return $output;
3470 } else {
3471 echo $output;
3476 * Print a nicely formatted table.
3478 * @param array $table is an object with several properties.
3479 * <ul<li>$table->head - An array of heading names.
3480 * <li>$table->align - An array of column alignments
3481 * <li>$table->size - An array of column sizes
3482 * <li>$table->wrap - An array of "nowrap"s or nothing
3483 * <li>$table->data[] - An array of arrays containing the data.
3484 * <li>$table->width - A percentage of the page
3485 * <li>$table->tablealign - Align the whole table
3486 * <li>$table->cellpadding - Padding on each cell
3487 * <li>$table->cellspacing - Spacing between cells
3488 * </ul>
3489 * @param bool $return whether to return an output string or echo now
3490 * @return boolean or $string
3491 * @todo Finish documenting this function
3493 function print_table($table, $return=false) {
3494 $output = '';
3496 if (isset($table->align)) {
3497 foreach ($table->align as $key => $aa) {
3498 if ($aa) {
3499 $align[$key] = ' text-align:'. $aa.';';
3500 } else {
3501 $align[$key] = '';
3505 if (isset($table->size)) {
3506 foreach ($table->size as $key => $ss) {
3507 if ($ss) {
3508 $size[$key] = ' width:'. $ss .';';
3509 } else {
3510 $size[$key] = '';
3514 if (isset($table->wrap)) {
3515 foreach ($table->wrap as $key => $ww) {
3516 if ($ww) {
3517 $wrap[$key] = ' white-space:nowrap;';
3518 } else {
3519 $wrap[$key] = '';
3524 if (empty($table->width)) {
3525 $table->width = '80%';
3528 if (empty($table->tablealign)) {
3529 $table->tablealign = 'center';
3532 if (empty($table->cellpadding)) {
3533 $table->cellpadding = '5';
3536 if (empty($table->cellspacing)) {
3537 $table->cellspacing = '1';
3540 if (empty($table->class)) {
3541 $table->class = 'generaltable';
3544 $tableid = empty($table->id) ? '' : 'id="'.$table->id.'"';
3546 $output .= '<table width="'.$table->width.'" ';
3547 $output .= " cellpadding=\"$table->cellpadding\" cellspacing=\"$table->cellspacing\" class=\"$table->class boxalign$table->tablealign\" $tableid>\n";
3549 $countcols = 0;
3551 if (!empty($table->head)) {
3552 $countcols = count($table->head);
3553 $output .= '<tr>';
3554 foreach ($table->head as $key => $heading) {
3556 if (!isset($size[$key])) {
3557 $size[$key] = '';
3559 if (!isset($align[$key])) {
3560 $align[$key] = '';
3563 $output .= '<th class="header c'.$key.'" scope="col">'. $heading .'</th>';
3564 // commenting the following code out as <th style does not validate MDL-7861
3565 //$output .= '<th sytle="vertical-align:top;'. $align[$key].$size[$key] .';white-space:nowrap;" class="header c'.$key.'" scope="col">'. $heading .'</th>';
3567 $output .= '</tr>'."\n";
3570 if (!empty($table->data)) {
3571 $oddeven = 1;
3572 foreach ($table->data as $key => $row) {
3573 $oddeven = $oddeven ? 0 : 1;
3574 $output .= '<tr class="r'.$oddeven.'">'."\n";
3575 if ($row == 'hr' and $countcols) {
3576 $output .= '<td colspan="'. $countcols .'"><div class="tabledivider"></div></td>';
3577 } else { /// it's a normal row of data
3578 foreach ($row as $key => $item) {
3579 if (!isset($size[$key])) {
3580 $size[$key] = '';
3582 if (!isset($align[$key])) {
3583 $align[$key] = '';
3585 if (!isset($wrap[$key])) {
3586 $wrap[$key] = '';
3588 $output .= '<td style="'. $align[$key].$size[$key].$wrap[$key] .'" class="cell c'.$key.'">'. $item .'</td>';
3591 $output .= '</tr>'."\n";
3594 $output .= '</table>'."\n";
3596 if ($return) {
3597 return $output;
3600 echo $output;
3601 return true;
3605 * Creates a nicely formatted table and returns it.
3607 * @param array $table is an object with several properties.
3608 * <ul<li>$table->head - An array of heading names.
3609 * <li>$table->align - An array of column alignments
3610 * <li>$table->size - An array of column sizes
3611 * <li>$table->wrap - An array of "nowrap"s or nothing
3612 * <li>$table->data[] - An array of arrays containing the data.
3613 * <li>$table->class - A css class name
3614 * <li>$table->fontsize - Is the size of all the text
3615 * <li>$table->tablealign - Align the whole table
3616 * <li>$table->width - A percentage of the page
3617 * <li>$table->cellpadding - Padding on each cell
3618 * <li>$table->cellspacing - Spacing between cells
3619 * </ul>
3620 * @return string
3621 * @todo Finish documenting this function
3623 function make_table($table) {
3625 if (isset($table->align)) {
3626 foreach ($table->align as $key => $aa) {
3627 if ($aa) {
3628 $align[$key] = ' align="'. $aa .'"';
3629 } else {
3630 $align[$key] = '';
3634 if (isset($table->size)) {
3635 foreach ($table->size as $key => $ss) {
3636 if ($ss) {
3637 $size[$key] = ' width="'. $ss .'"';
3638 } else {
3639 $size[$key] = '';
3643 if (isset($table->wrap)) {
3644 foreach ($table->wrap as $key => $ww) {
3645 if ($ww) {
3646 $wrap[$key] = ' style="white-space:nowrap;" ';
3647 } else {
3648 $wrap[$key] = '';
3653 if (empty($table->width)) {
3654 $table->width = '80%';
3657 if (empty($table->tablealign)) {
3658 $table->tablealign = 'center';
3661 if (empty($table->cellpadding)) {
3662 $table->cellpadding = '5';
3665 if (empty($table->cellspacing)) {
3666 $table->cellspacing = '1';
3669 if (empty($table->class)) {
3670 $table->class = 'generaltable';
3673 if (empty($table->fontsize)) {
3674 $fontsize = '';
3675 } else {
3676 $fontsize = '<font size="'. $table->fontsize .'">';
3679 $output = '<table width="'. $table->width .'" align="'. $table->tablealign .'" ';
3680 $output .= ' cellpadding="'. $table->cellpadding .'" cellspacing="'. $table->cellspacing .'" class="'. $table->class .'">'."\n";
3682 if (!empty($table->head)) {
3683 $output .= '<tr valign="top">';
3684 foreach ($table->head as $key => $heading) {
3685 if (!isset($size[$key])) {
3686 $size[$key] = '';
3688 if (!isset($align[$key])) {
3689 $align[$key] = '';
3691 $output .= '<th valign="top" '. $align[$key].$size[$key] .' style="white-space:nowrap;" class="'. $table->class .'header" scope="col">'.$fontsize.$heading.'</th>';
3693 $output .= '</tr>'."\n";
3696 foreach ($table->data as $row) {
3697 $output .= '<tr valign="top">';
3698 foreach ($row as $key => $item) {
3699 if (!isset($size[$key])) {
3700 $size[$key] = '';
3702 if (!isset($align[$key])) {
3703 $align[$key] = '';
3705 if (!isset($wrap[$key])) {
3706 $wrap[$key] = '';
3708 $output .= '<td '. $align[$key].$size[$key].$wrap[$key] .' class="'. $table->class .'cell">'. $fontsize . $item .'</td>';
3710 $output .= '</tr>'."\n";
3712 $output .= '</table>'."\n";
3714 return $output;
3717 function print_recent_activity_note($time, $user, $text, $link, $return=false) {
3718 static $strftimerecent;
3719 $output = '';
3721 $context = get_context_instance(CONTEXT_SYSTEM, SITEID);
3722 $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
3724 if (empty($strftimerecent)) {
3725 $strftimerecent = get_string('strftimerecent');
3728 $date = userdate($time, $strftimerecent);
3729 $name = fullname($user, $viewfullnames);
3731 $output .= '<div class="head">';
3732 $output .= '<div class="date">'.$date.'</div> '.
3733 '<div class="name">'.fullname($user, $viewfullnames).'</div>';
3734 $output .= '</div>';
3735 $output .= '<div class="info"><a href="'.$link.'">'.format_string($text,true).'</a></div>';
3737 if ($return) {
3738 return $output;
3739 } else {
3740 echo $output;
3746 * Prints a basic textarea field.
3748 * @uses $CFG
3749 * @param boolean $usehtmleditor ?
3750 * @param int $rows ?
3751 * @param int $cols ?
3752 * @param null $width <b>Legacy field no longer used!</b> Set to zero to get control over mincols
3753 * @param null $height <b>Legacy field no longer used!</b> Set to zero to get control over minrows
3754 * @param string $name ?
3755 * @param string $value ?
3756 * @param int $courseid ?
3757 * @todo Finish documenting this function
3759 function print_textarea($usehtmleditor, $rows, $cols, $width, $height, $name, $value='', $courseid=0, $return=false, $id='') {
3760 /// $width and height are legacy fields and no longer used as pixels like they used to be.
3761 /// However, you can set them to zero to override the mincols and minrows values below.
3763 global $CFG, $COURSE, $HTTPSPAGEREQUIRED;
3764 static $scriptcount = 0; // For loading the htmlarea script only once.
3766 $mincols = 65;
3767 $minrows = 10;
3768 $str = '';
3770 if ($id === '') {
3771 $id = 'edit-'.$name;
3774 if ( empty($CFG->editorsrc) ) { // for backward compatibility.
3775 if (empty($courseid)) {
3776 $courseid = $COURSE->id;
3779 if ($usehtmleditor) {
3780 if (!empty($courseid) and has_capability('moodle/course:managefiles', get_context_instance(CONTEXT_COURSE, $courseid))) {
3781 $httpsrequired = empty($HTTPSPAGEREQUIRED) ? '' : '&httpsrequired=1';
3782 // needed for course file area browsing in image insert plugin
3783 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3784 $CFG->httpswwwroot .'/lib/editor/htmlarea/htmlarea.php?id='.$courseid.$httpsrequired.'"></script>'."\n" : '';
3785 } else {
3786 $httpsrequired = empty($HTTPSPAGEREQUIRED) ? '' : '?httpsrequired=1';
3787 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3788 $CFG->httpswwwroot .'/lib/editor/htmlarea/htmlarea.php'.$httpsrequired.'"></script>'."\n" : '';
3791 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3792 $CFG->httpswwwroot .'/lib/editor/htmlarea/lang/en.php"></script>'."\n" : '';
3793 $scriptcount++;
3795 if ($height) { // Usually with legacy calls
3796 if ($rows < $minrows) {
3797 $rows = $minrows;
3800 if ($width) { // Usually with legacy calls
3801 if ($cols < $mincols) {
3802 $cols = $mincols;
3807 $str .= '<textarea class="form-textarea" id="'. $id .'" name="'. $name .'" rows="'. $rows .'" cols="'. $cols .'">';
3808 if ($usehtmleditor) {
3809 $str .= htmlspecialchars($value); // needed for editing of cleaned text!
3810 } else {
3811 $str .= s($value);
3813 $str .= '</textarea>'."\n";
3815 if ($usehtmleditor) {
3816 $str .= editorshortcutshelpbutton();
3819 if ($return) {
3820 return $str;
3822 echo $str;
3826 * Sets up the HTML editor on textareas in the current page.
3827 * If a field name is provided, then it will only be
3828 * applied to that field - otherwise it will be used
3829 * on every textarea in the page.
3831 * In most cases no arguments need to be supplied
3833 * @param string $name Form element to replace with HTMl editor by name
3835 function use_html_editor($name='', $editorhidebuttons='', $id='') {
3836 $editor = 'editor_'.md5($name); //name might contain illegal characters
3837 if ($id === '') {
3838 $id = 'edit-'.$name;
3840 echo "\n".'<script type="text/javascript" defer="defer">'."\n";
3841 echo '//<![CDATA['."\n\n"; // Extra \n is to fix odd wiki problem, MDL-8185
3842 echo "$editor = new HTMLArea('$id');\n";
3843 echo "var config = $editor.config;\n";
3845 echo print_editor_config($editorhidebuttons);
3847 if (empty($name)) {
3848 echo "\nHTMLArea.replaceAll($editor.config);\n";
3849 } else {
3850 echo "\n$editor.generate();\n";
3852 echo '//]]>'."\n";
3853 echo '</script>'."\n";
3856 function print_editor_config($editorhidebuttons='', $return=false) {
3857 global $CFG;
3859 $str = "config.pageStyle = \"body {";
3861 if (!(empty($CFG->editorbackgroundcolor))) {
3862 $str .= " background-color: $CFG->editorbackgroundcolor;";
3865 if (!(empty($CFG->editorfontfamily))) {
3866 $str .= " font-family: $CFG->editorfontfamily;";
3869 if (!(empty($CFG->editorfontsize))) {
3870 $str .= " font-size: $CFG->editorfontsize;";
3873 $str .= " }\";\n";
3874 $str .= "config.killWordOnPaste = ";
3875 $str .= (empty($CFG->editorkillword)) ? "false":"true";
3876 $str .= ';'."\n";
3877 $str .= 'config.fontname = {'."\n";
3879 $fontlist = isset($CFG->editorfontlist) ? explode(';', $CFG->editorfontlist) : array();
3880 $i = 1; // Counter is used to get rid of the last comma.
3882 foreach ($fontlist as $fontline) {
3883 if (!empty($fontline)) {
3884 if ($i > 1) {
3885 $str .= ','."\n";
3887 list($fontkey, $fontvalue) = split(':', $fontline);
3888 $str .= '"'. $fontkey ."\":\t'". $fontvalue ."'";
3890 $i++;
3893 $str .= '};';
3895 if (!empty($editorhidebuttons)) {
3896 $str .= "\nconfig.hideSomeButtons(\" ". $editorhidebuttons ." \");\n";
3897 } else if (!empty($CFG->editorhidebuttons)) {
3898 $str .= "\nconfig.hideSomeButtons(\" ". $CFG->editorhidebuttons ." \");\n";
3901 if (!empty($CFG->editorspelling) && !empty($CFG->aspellpath)) {
3902 $str .= print_speller_code($CFG->htmleditor, true);
3905 if ($return) {
3906 return $str;
3908 echo $str;
3912 * Returns a turn edit on/off button for course in a self contained form.
3913 * Used to be an icon, but it's now a simple form button
3915 * @uses $CFG
3916 * @uses $USER
3917 * @param int $courseid The course to update by id as found in 'course' table
3918 * @return string
3920 function update_course_icon($courseid) {
3922 global $CFG, $USER;
3924 $coursecontext = get_context_instance(CONTEXT_COURSE, $courseid);
3926 $capcheck = false;
3928 if (has_capability('moodle/course:manageactivities', $coursecontext) ||
3929 has_capability('moodle/site:manageblocks', $coursecontext)) {
3930 $capcheck = true;
3931 } else {
3932 // loop through all child context, see if user has moodle/course:manageactivities or moodle/site:manageblocks
3933 if ($children = get_child_contexts($coursecontext)) {
3934 foreach ($children as $child) {
3935 $childcontext = get_record('context', 'id', $child);
3936 if (has_capability('moodle/course:manageactivities', $childcontext) ||
3937 has_capability('moodle/site:manageblocks', $childcontext)) {
3938 $capcheck = true;
3939 break;
3946 if ($capcheck) {
3947 if (!empty($USER->editing)) {
3948 $string = get_string('turneditingoff');
3949 $edit = '0';
3950 } else {
3951 $string = get_string('turneditingon');
3952 $edit = '1';
3955 return '<form '.$CFG->frametarget.' method="get" action="'.$CFG->wwwroot.'/course/view.php">'.
3956 '<div>'.
3957 '<input type="hidden" name="id" value="'.$courseid.'" />'.
3958 '<input type="hidden" name="edit" value="'.$edit.'" />'.
3959 '<input type="hidden" name="sesskey" value="'.sesskey().'" />'.
3960 '<input type="submit" value="'.$string.'" />'.
3961 '</div></form>';
3966 * Returns a little popup menu for switching roles
3968 * @uses $CFG
3969 * @uses $USER
3970 * @param int $courseid The course to update by id as found in 'course' table
3971 * @return string
3973 function switchroles_form($courseid) {
3975 global $CFG, $USER;
3978 if (!$context = get_context_instance(CONTEXT_COURSE, $courseid)) {
3979 return '';
3982 if (!empty($USER->switchrole[$context->id])){ // Just a button to return to normal
3983 $options = array();
3984 $options['id'] = $courseid;
3985 $options['sesskey'] = sesskey();
3986 $options['switchrole'] = 0;
3988 return print_single_button($CFG->wwwroot.'/course/view.php', $options,
3989 get_string('switchrolereturn'), 'post', '_self', true);
3992 if (has_capability('moodle/role:switchroles', $context)) {
3993 if (!$roles = get_assignable_roles($context)) {
3994 return ''; // Nothing to show!
3996 // unset default user role - it would not work
3997 unset($roles[$CFG->guestroleid]);
3998 return popup_form($CFG->wwwroot.'/course/view.php?id='.$courseid.'&amp;sesskey='.sesskey().'&amp;switchrole=',
3999 $roles, 'switchrole', '', get_string('switchroleto'), 'switchrole', get_string('switchroleto'), true);
4002 return '';
4007 * Returns a turn edit on/off button for course in a self contained form.
4008 * Used to be an icon, but it's now a simple form button
4010 * @uses $CFG
4011 * @uses $USER
4012 * @param int $courseid The course to update by id as found in 'course' table
4013 * @return string
4015 function update_mymoodle_icon() {
4017 global $CFG, $USER;
4019 if (!empty($USER->editing)) {
4020 $string = get_string('updatemymoodleoff');
4021 $edit = '0';
4022 } else {
4023 $string = get_string('updatemymoodleon');
4024 $edit = '1';
4027 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/my/index.php\">".
4028 "<div>".
4029 "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
4030 "<input type=\"submit\" value=\"$string\" /></div></form>";
4034 * Prints the editing button on a module "view" page
4036 * @uses $CFG
4037 * @param type description
4038 * @todo Finish documenting this function
4040 function update_module_button($moduleid, $courseid, $string) {
4041 global $CFG, $USER;
4043 if (has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_MODULE, $moduleid))) {
4044 $string = get_string('updatethis', '', $string);
4046 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/course/mod.php\" onsubmit=\"this.target='{$CFG->framename}'; return true\">".//hack to allow edit on framed resources
4047 "<div>".
4048 "<input type=\"hidden\" name=\"update\" value=\"$moduleid\" />".
4049 "<input type=\"hidden\" name=\"return\" value=\"true\" />".
4050 "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />".
4051 "<input type=\"submit\" value=\"$string\" /></div></form>";
4052 } else {
4053 return '';
4058 * Prints the editing button on a category page
4060 * @uses $CFG
4061 * @uses $USER
4062 * @param int $categoryid ?
4063 * @return string
4064 * @todo Finish documenting this function
4066 function update_category_button($categoryid) {
4067 global $CFG, $USER;
4069 if (has_capability('moodle/category:update', get_context_instance(CONTEXT_COURSECAT, $categoryid))) {
4070 if (!empty($USER->categoryediting)) {
4071 $string = get_string('turneditingoff');
4072 $edit = 'off';
4073 } else {
4074 $string = get_string('turneditingon');
4075 $edit = 'on';
4078 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/course/category.php\">".
4079 '<div>'.
4080 "<input type=\"hidden\" name=\"id\" value=\"$categoryid\" />".
4081 "<input type=\"hidden\" name=\"categoryedit\" value=\"$edit\" />".
4082 "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />".
4083 "<input type=\"submit\" value=\"$string\" /></div></form>";
4088 * Prints the editing button on categories listing
4090 * @uses $CFG
4091 * @uses $USER
4092 * @return string
4094 function update_categories_button() {
4095 global $CFG, $USER;
4097 if (has_capability('moodle/category:update', get_context_instance(CONTEXT_SYSTEM, SITEID))) {
4098 if (!empty($USER->categoryediting)) {
4099 $string = get_string('turneditingoff');
4100 $categoryedit = 'off';
4101 } else {
4102 $string = get_string('turneditingon');
4103 $categoryedit = 'on';
4106 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/course/index.php\">".
4107 '<div>'.
4108 '<input type="hidden" name="categoryedit" value="'. $categoryedit .'" />'.
4109 '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />'.
4110 '<input type="submit" value="'. $string .'" /></div></form>';
4115 * Prints the editing button on search results listing
4116 * For bulk move courses to another category
4119 function update_categories_search_button($search,$page,$perpage) {
4120 global $CFG, $USER;
4122 // not sure if this capability is the best here
4123 if (has_capability('moodle/category:update', get_context_instance(CONTEXT_SYSTEM, SITEID))) {
4124 if (!empty($USER->categoryediting)) {
4125 $string = get_string("turneditingoff");
4126 $edit = "off";
4127 $perpage = 30;
4128 } else {
4129 $string = get_string("turneditingon");
4130 $edit = "on";
4133 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/course/search.php\">".
4134 '<div>'.
4135 "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
4136 "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />".
4137 "<input type=\"hidden\" name=\"search\" value=\"".s($search, true)."\" />".
4138 "<input type=\"hidden\" name=\"page\" value=\"$page\" />".
4139 "<input type=\"hidden\" name=\"perpage\" value=\"$perpage\" />".
4140 "<input type=\"submit\" value=\"".s($string)."\" /></div></form>";
4145 * Prints the editing button on group page
4147 * @uses $CFG
4148 * @uses $USER
4149 * @param int $courseid The course group is associated with
4150 * @param int $groupid The group to update
4151 * @return string
4153 function update_group_button($courseid, $groupid) {
4154 global $CFG, $USER;
4156 if (has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_GROUP, $groupid))) {
4157 $string = get_string('editgroupprofile');
4159 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/course/group.php\">".
4160 '<div>'.
4161 '<input type="hidden" name="id" value="'. $courseid .'" />'.
4162 '<input type="hidden" name="group" value="'. $groupid .'" />'.
4163 '<input type="hidden" name="edit" value="on" />'.
4164 '<input type="submit" value="'. $string .'" /></div></form>';
4169 * Prints the editing button on groups page
4171 * @uses $CFG
4172 * @uses $USER
4173 * @param int $courseid The id of the course to be edited
4174 * @return string
4175 * @todo Finish documenting this function
4177 function update_groups_button($courseid) {
4178 global $CFG, $USER;
4180 if (has_capability('moodle/course:managegroups', get_context_instance(CONTEXT_COURSE, $courseid))) {
4181 if (!empty($USER->groupsediting)) {
4182 $string = get_string('turneditingoff');
4183 $edit = 'off';
4184 } else {
4185 $string = get_string('turneditingon');
4186 $edit = 'on';
4189 return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/course/groups.php\">".
4190 '<div>'.
4191 "<input type=\"hidden\" name=\"id\" value=\"$courseid\" />".
4192 "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
4193 "<input type=\"submit\" value=\"$string\" /></div></form>";
4198 * Prints an appropriate group selection menu
4200 * @uses VISIBLEGROUPS
4201 * @param array $groups ?
4202 * @param int $groupmode ?
4203 * @param string $currentgroup ?
4204 * @param string $urlroot ?
4205 * @param boolean $showall: if set to 0, it is a student in separate groups, do not display all participants
4206 * @todo Finish documenting this function
4208 function print_group_menu($groups, $groupmode, $currentgroup, $urlroot, $showall=1, $return=false) {
4210 $output = '';
4212 /// Add an "All groups" to the start of the menu
4213 if ($showall){
4214 $groupsmenu[0] = get_string('allparticipants');
4216 foreach ($groups as $key => $groupname) {
4217 $groupsmenu[$key] = $groupname;
4220 if ($groupmode == VISIBLEGROUPS) {
4221 $grouplabel = get_string('groupsvisible').':';
4222 } else {
4223 $grouplabel = get_string('groupsseparate').':';
4225 $output .= popup_form($urlroot.'&amp;group=', $groupsmenu, 'selectgroup', $currentgroup, '', '', '', true, 'self', $grouplabel);
4227 if ($return) {
4228 return $output;
4229 } else {
4230 echo $output;
4236 * Given a course and a (current) coursemodule
4237 * This function returns a small popup menu with all the
4238 * course activity modules in it, as a navigation menu
4239 * The data is taken from the serialised array stored in
4240 * the course record
4242 * @param course $course A {@link $COURSE} object.
4243 * @param course $cm A {@link $COURSE} object.
4244 * @param string $targetwindow ?
4245 * @return string
4246 * @todo Finish documenting this function
4248 function navmenu($course, $cm=NULL, $targetwindow='self') {
4250 global $CFG, $THEME, $USER;
4252 if (empty($THEME->navmenuwidth)) {
4253 $width = 50;
4254 } else {
4255 $width = $THEME->navmenuwidth;
4258 if ($cm) {
4259 $cm = $cm->id;
4262 if ($course->format == 'weeks') {
4263 $strsection = get_string('week');
4264 } else {
4265 $strsection = get_string('topic');
4267 $strjumpto = get_string('jumpto');
4269 /// Casting $course->modinfo to string prevents one notice when the field is null
4270 if (!$modinfo = unserialize((string)$course->modinfo)) {
4271 return '';
4273 $context = get_context_instance(CONTEXT_COURSE, $course->id);
4275 $section = -1;
4276 $selected = '';
4277 $url = '';
4278 $previousmod = NULL;
4279 $backmod = NULL;
4280 $nextmod = NULL;
4281 $selectmod = NULL;
4282 $logslink = NULL;
4283 $flag = false;
4284 $menu = array();
4286 $sections = get_records('course_sections','course',$course->id,'section','section,visible,summary');
4288 if (!empty($THEME->makenavmenulist)) { /// A hack to produce an XHTML navmenu list for use in themes
4289 $THEME->navmenulist = navmenulist($course, $sections, $modinfo, $strsection, $strjumpto, $width, $cm);
4292 foreach ($modinfo as $mod) {
4293 if ($mod->mod == 'label') {
4294 continue;
4297 if ($mod->section > $course->numsections) { /// Don't show excess hidden sections
4298 break;
4301 if ($mod->section > 0 and $section <> $mod->section) {
4302 $thissection = $sections[$mod->section];
4304 if ($thissection->visible or !$course->hiddensections or
4305 has_capability('moodle/course:viewhiddensections', $context)) {
4306 $thissection->summary = strip_tags(format_string($thissection->summary,true));
4307 if ($course->format == 'weeks' or empty($thissection->summary)) {
4308 $menu[] = '--'.$strsection ." ". $mod->section;
4309 } else {
4310 if (strlen($thissection->summary) < ($width-3)) {
4311 $menu[] = '--'.$thissection->summary;
4312 } else {
4313 $menu[] = '--'.substr($thissection->summary, 0, $width).'...';
4319 $section = $mod->section;
4321 //Only add visible or teacher mods to jumpmenu
4322 if ($mod->visible or has_capability('moodle/course:viewhiddenactivities',
4323 get_context_instance(CONTEXT_MODULE, $mod->cm))) {
4324 $url = $mod->mod .'/view.php?id='. $mod->cm;
4325 if ($flag) { // the current mod is the "next" mod
4326 $nextmod = $mod;
4327 $flag = false;
4329 if ($cm == $mod->cm) {
4330 $selected = $url;
4331 $selectmod = $mod;
4332 $backmod = $previousmod;
4333 $flag = true; // set flag so we know to use next mod for "next"
4334 $mod->name = $strjumpto;
4335 $strjumpto = '';
4336 } else {
4337 $mod->name = strip_tags(format_string(urldecode($mod->name),true));
4338 if (strlen($mod->name) > ($width+5)) {
4339 $mod->name = substr($mod->name, 0, $width).'...';
4341 if (!$mod->visible) {
4342 $mod->name = '('.$mod->name.')';
4345 $menu[$url] = $mod->name;
4346 $previousmod = $mod;
4349 //Accessibility: added Alt text, replaced &gt; &lt; with 'silent' character and 'accesshide' text.
4350 check_theme_arrows();
4352 if ($selectmod and has_capability('moodle/site:viewreports', $context)) {
4353 $logstext = get_string('alllogs');
4354 $logslink = '<li><a title="'.$logstext.'" '.$CFG->frametarget.' href="'.
4355 $CFG->wwwroot.'/course/report/log/index.php?chooselog=1&amp;user=0&amp;date=0&amp;id='.
4356 $course->id.'&amp;modid='.$selectmod->cm.'">'.
4357 '<img class="icon log" src="'.$CFG->pixpath.'/i/log.gif" alt="'.$logstext.'" /></a></li>';
4360 if ($backmod) {
4361 $backtext= get_string('activityprev', 'access');
4362 $backmod = '<li><form action="'.$CFG->wwwroot.'/mod/'.$backmod->mod.'/view.php" '.$CFG->frametarget.'><div>'.
4363 '<input type="hidden" name="id" value="'.$backmod->cm.'" />'.
4364 '<button type="submit" title="'.$backtext.'">'.$THEME->larrow.
4365 '<span class="accesshide">'.$backtext.'</span></button></div></form></li>';
4367 if ($nextmod) {
4368 $nexttext= get_string('activitynext', 'access');
4369 $nextmod = '<li><form action="'.$CFG->wwwroot.'/mod/'.$nextmod->mod.'/view.php" '.$CFG->frametarget.'><div>'.
4370 '<input type="hidden" name="id" value="'.$nextmod->cm.'" />'.
4371 '<button type="submit" title="'.$nexttext.'">'.$THEME->rarrow.
4372 '<span class="accesshide">'.$nexttext.'</span></button></div></form></li>';
4375 return '<div class="navigation"><ul>'.$logslink . $backmod .
4376 '<li>'.popup_form($CFG->wwwroot .'/mod/', $menu, 'navmenupopup', $selected, $strjumpto,
4377 '', '', true, $targetwindow).'</li>'.
4378 $nextmod . '</ul></div>';
4382 * Given a course
4383 * This function returns a small popup menu with all the
4384 * course activity modules in it, as a navigation menu
4385 * outputs a simple list structure in XHTML
4386 * The data is taken from the serialised array stored in
4387 * the course record
4389 * @param course $course A {@link $COURSE} object.
4390 * @return string
4391 * @todo Finish documenting this function
4393 function navmenulist($course, $sections, $modinfo, $strsection, $strjumpto, $width=50, $cmid=0) {
4395 global $CFG;
4397 $section = -1;
4398 $selected = '';
4399 $url = '';
4400 $previousmod = NULL;
4401 $backmod = NULL;
4402 $nextmod = NULL;
4403 $selectmod = NULL;
4404 $logslink = NULL;
4405 $flag = false;
4406 $menu = array();
4408 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
4410 $menu[] = '<ul class="navmenulist"><li class="jumpto section"><span>'.$strjumpto.'</span><ul>';
4411 foreach ($modinfo as $mod) {
4412 if ($mod->mod == 'label') {
4413 continue;
4416 if ($mod->section > $course->numsections) { /// Don't show excess hidden sections
4417 break;
4420 if ($mod->section >= 0 and $section <> $mod->section) {
4421 $thissection = $sections[$mod->section];
4423 if ($thissection->visible or !$course->hiddensections or
4424 has_capability('moodle/course:viewhiddensections', $coursecontext)) {
4425 $thissection->summary = strip_tags(format_string($thissection->summary,true));
4426 if (!empty($doneheading)) {
4427 $menu[] = '</ul></li>';
4429 if ($course->format == 'weeks' or empty($thissection->summary)) {
4430 $item = $strsection ." ". $mod->section;
4431 } else {
4432 if (strlen($thissection->summary) < ($width-3)) {
4433 $item = $thissection->summary;
4434 } else {
4435 $item = substr($thissection->summary, 0, $width).'...';
4438 $menu[] = '<li class="section"><span>'.$item.'</span>';
4439 $menu[] = '<ul>';
4440 $doneheading = true;
4444 $section = $mod->section;
4446 //Only add visible or teacher mods to jumpmenu
4447 if ($mod->visible or has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_MODULE, $mod->id))) {
4448 $url = $mod->mod .'/view.php?id='. $mod->cm;
4449 if ($flag) { // the current mod is the "next" mod
4450 $nextmod = $mod;
4451 $flag = false;
4453 $mod->name = strip_tags(format_string(urldecode($mod->name),true));
4454 if (strlen($mod->name) > ($width+5)) {
4455 $mod->name = substr($mod->name, 0, $width).'...';
4457 if (!$mod->visible) {
4458 $mod->name = '('.$mod->name.')';
4460 $class = 'activity '.$mod->mod;
4461 $class .= ($cmid == $mod->cm) ? ' selected' : '';
4462 $menu[] = '<li class="'.$class.'">'.
4463 '<img src="'.$CFG->modpixpath.'/'.$mod->mod.'/icon.gif" alt="" />'.
4464 '<a href="'.$CFG->wwwroot.'/mod/'.$url.'">'.$mod->name.'</a></li>';
4465 $previousmod = $mod;
4468 if ($doneheading) {
4469 $menu[] = '</ul></li>';
4471 $menu[] = '</ul></li></ul>';
4473 return implode("\n", $menu);
4477 * Prints form items with the names $day, $month and $year
4479 * @param string $day fieldname
4480 * @param string $month fieldname
4481 * @param string $year fieldname
4482 * @param int $currenttime A default timestamp in GMT
4483 * @param boolean $return
4485 function print_date_selector($day, $month, $year, $currenttime=0, $return=false) {
4487 if (!$currenttime) {
4488 $currenttime = time();
4490 $currentdate = usergetdate($currenttime);
4492 for ($i=1; $i<=31; $i++) {
4493 $days[$i] = $i;
4495 for ($i=1; $i<=12; $i++) {
4496 $months[$i] = userdate(gmmktime(12,0,0,$i,1,2000), "%B");
4498 for ($i=1970; $i<=2020; $i++) {
4499 $years[$i] = $i;
4501 return choose_from_menu($days, $day, $currentdate['mday'], '', '', '0', $return)
4502 .choose_from_menu($months, $month, $currentdate['mon'], '', '', '0', $return)
4503 .choose_from_menu($years, $year, $currentdate['year'], '', '', '0', $return);
4508 *Prints form items with the names $hour and $minute
4510 * @param string $hour fieldname
4511 * @param string ? $minute fieldname
4512 * @param $currenttime A default timestamp in GMT
4513 * @param int $step minute spacing
4514 * @param boolean $return
4516 function print_time_selector($hour, $minute, $currenttime=0, $step=5, $return=false) {
4518 if (!$currenttime) {
4519 $currenttime = time();
4521 $currentdate = usergetdate($currenttime);
4522 if ($step != 1) {
4523 $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
4525 for ($i=0; $i<=23; $i++) {
4526 $hours[$i] = sprintf("%02d",$i);
4528 for ($i=0; $i<=59; $i+=$step) {
4529 $minutes[$i] = sprintf("%02d",$i);
4532 return choose_from_menu($hours, $hour, $currentdate['hours'], '','','0',$return)
4533 .choose_from_menu($minutes, $minute, $currentdate['minutes'], '','','0',$return);
4537 * Prints time limit value selector
4539 * @uses $CFG
4540 * @param int $timelimit default
4541 * @param string $unit
4542 * @param string $name
4543 * @param boolean $return
4545 function print_timer_selector($timelimit = 0, $unit = '', $name = 'timelimit', $return=false) {
4547 global $CFG;
4549 if ($unit) {
4550 $unit = ' '.$unit;
4553 // Max timelimit is sessiontimeout - 10 minutes.
4554 $maxvalue = ($CFG->sessiontimeout / 60) - 10;
4556 for ($i=1; $i<=$maxvalue; $i++) {
4557 $minutes[$i] = $i.$unit;
4559 return choose_from_menu($minutes, $name, $timelimit, get_string('none'), '','','0',$return);
4563 * Prints a grade menu (as part of an existing form) with help
4564 * Showing all possible numerical grades and scales
4566 * @uses $CFG
4567 * @param int $courseid ?
4568 * @param string $name ?
4569 * @param string $current ?
4570 * @param boolean $includenograde ?
4571 * @todo Finish documenting this function
4573 function print_grade_menu($courseid, $name, $current, $includenograde=true, $return=false) {
4575 global $CFG;
4577 $output = '';
4578 $strscale = get_string('scale');
4579 $strscales = get_string('scales');
4581 $scales = get_scales_menu($courseid);
4582 foreach ($scales as $i => $scalename) {
4583 $grades[-$i] = $strscale .': '. $scalename;
4585 if ($includenograde) {
4586 $grades[0] = get_string('nograde');
4588 for ($i=100; $i>=1; $i--) {
4589 $grades[$i] = $i;
4591 $output .= choose_from_menu($grades, $name, $current, '', '', 0, true);
4593 $linkobject = '<span class="helplink"><img class="iconhelp" alt="'.$strscales.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
4594 $output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true', 'ratingscales',
4595 $linkobject, 400, 500, $strscales, 'none', true);
4597 if ($return) {
4598 return $output;
4599 } else {
4600 echo $output;
4605 * Prints a scale menu (as part of an existing form) including help button
4606 * Just like {@link print_grade_menu()} but without the numeric grades
4608 * @param int $courseid ?
4609 * @param string $name ?
4610 * @param string $current ?
4611 * @todo Finish documenting this function
4613 function print_scale_menu($courseid, $name, $current, $return=false) {
4615 global $CFG;
4617 $output = '';
4618 $strscales = get_string('scales');
4619 $output .= choose_from_menu(get_scales_menu($courseid), $name, $current, '', '', 0, true);
4621 $linkobject = '<span class="helplink"><img class="iconhelp" alt="'.$strscales.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
4622 $output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true', 'ratingscales',
4623 $linkobject, 400, 500, $strscales, 'none', true);
4624 if ($return) {
4625 return $output;
4626 } else {
4627 echo $output;
4632 * Prints a help button about a scale
4634 * @uses $CFG
4635 * @param id $courseid ?
4636 * @param object $scale ?
4637 * @todo Finish documenting this function
4639 function print_scale_menu_helpbutton($courseid, $scale, $return=false) {
4641 global $CFG;
4643 $output = '';
4644 $strscales = get_string('scales');
4646 $linkobject = '<span class="helplink"><img class="iconhelp" alt="'.$scale->name.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
4647 $output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true&amp;scaleid='. $scale->id, 'ratingscale',
4648 $linkobject, 400, 500, $scale->name, 'none', true);
4649 if ($return) {
4650 return $output;
4651 } else {
4652 echo $output;
4657 * Print an error page displaying an error message.
4658 * Old method, don't call directly in new code - use print_error instead.
4661 * @uses $SESSION
4662 * @uses $CFG
4663 * @param string $message The message to display to the user about the error.
4664 * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
4666 function error ($message, $link='', $adminroot=false) {
4668 global $CFG, $SESSION;
4669 $message = clean_text($message); // In case nasties are in here
4671 if (defined('FULLME') && FULLME == 'cron') {
4672 // Errors in cron should be mtrace'd.
4673 mtrace($message);
4674 die;
4677 if (! defined('HEADER_PRINTED')) {
4678 //header not yet printed
4679 @header('HTTP/1.0 404 Not Found');
4680 print_header(get_string('error'));
4683 echo '<br />';
4684 print_simple_box($message, '', '', '', '', 'errorbox');
4686 // in case we are logging upgrade in admin/index.php stop it
4687 if (function_exists('upgrade_log_finish')) {
4688 upgrade_log_finish();
4691 if (!$link) {
4692 if ( !empty($SESSION->fromurl) ) {
4693 $link = $SESSION->fromurl;
4694 unset($SESSION->fromurl);
4695 } else {
4696 $link = $CFG->wwwroot .'/';
4700 if ($adminroot) {
4701 admin_externalpage_print_footer($adminroot);
4702 } else {
4703 print_continue($link);
4704 print_footer();
4706 for ($i=0;$i<512;$i++) { // Padding to help IE work with 404
4707 echo ' ';
4710 die;
4714 * Print an error page displaying an error message. New method - use this for new code.
4716 * @uses $SESSION
4717 * @uses $CFG
4718 * @param string $errorcode The name of the string from error.php to print
4719 * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
4720 * @param object $a Extra words and phrases that might be required in the error string
4721 * @param boolean $adminroot Is the page an admin settings page?
4723 function print_error ($errorcode, $module='', $link='', $a=NULL, $adminroot=false) {
4725 global $CFG;
4727 if (empty($module) || $module == 'moodle' || $module == 'core') {
4728 $module = 'error';
4729 $modulelink = 'moodle';
4730 } else {
4731 $modulelink = $module;
4734 if (!empty($CFG->errordocroot)) {
4735 $errordocroot = $CFG->errordocroot;
4736 } else if (!empty($CFG->docroot)) {
4737 $errordocroot = $CFG->docroot;
4738 } else {
4739 $errordocroot = 'http://docs.moodle.org';
4742 $message = '<p class="errormessage">'.get_string($errorcode, $module, $a).'</p>'.
4743 '<p class="errorcode">'.
4744 '<a href="'.$errordocroot.'/en/error/'.$modulelink.'/'.$errorcode.'">'.
4745 get_string('moreinformation').'</a></p>';
4746 error($message, $link, $adminroot);
4749 * Returns a string of html with an image of a help icon linked to a help page on a number of help topics.
4750 * Should be used only with htmleditor or textarea.
4751 * @param mixed $helptopics variable amount of params accepted. Each param may be a string or an array of arguments for
4752 * helpbutton.
4753 * @return string
4755 function editorhelpbutton(){
4756 global $CFG, $SESSION;
4757 $items = func_get_args();
4758 $i = 1;
4759 $urlparams = array();
4760 $titles = array();
4761 foreach ($items as $item){
4762 if (is_array($item)){
4763 $urlparams[] = "keyword$i=".urlencode($item[0]);
4764 $urlparams[] = "title$i=".urlencode($item[1]);
4765 if (isset($item[2])){
4766 $urlparams[] = "module$i=".urlencode($item[2]);
4768 $titles[] = trim($item[1], ". \t");
4769 }elseif (is_string($item)){
4770 $urlparams[] = "button$i=".urlencode($item);
4771 switch ($item){
4772 case 'reading' :
4773 $titles[] = get_string("helpreading");
4774 break;
4775 case 'writing' :
4776 $titles[] = get_string("helpwriting");
4777 break;
4778 case 'questions' :
4779 $titles[] = get_string("helpquestions");
4780 break;
4781 case 'emoticons' :
4782 $titles[] = get_string("helpemoticons");
4783 break;
4784 case 'richtext' :
4785 $titles[] = get_string('helprichtext');
4786 break;
4787 case 'text' :
4788 $titles[] = get_string('helptext');
4789 break;
4790 default :
4791 error('Unknown help topic '.$item);
4794 $i++;
4796 if (count($titles)>1){
4797 //join last two items with an 'and'
4798 $a = new object();
4799 $a->one = $titles[count($titles) - 2];
4800 $a->two = $titles[count($titles) - 1];
4801 $titles[count($titles) - 2] = get_string('and', '', $a);
4802 unset($titles[count($titles) - 1]);
4804 $alttag = join (', ', $titles);
4806 $paramstring = join('&', $urlparams);
4807 $linkobject = '<img alt="'.$alttag.'" class="iconhelp" src="'.$CFG->pixpath .'/help.gif" />';
4808 return link_to_popup_window(s('/lib/form/editorhelp.php?'.$paramstring), $alttag, $linkobject, 400, 500, $alttag, 'none', true);
4812 * Print a help button.
4814 * @uses $CFG
4815 * @param string $page The keyword that defines a help page
4816 * @param string $title The title of links, rollover tips, alt tags etc
4817 * 'Help with' (or the language equivalent) will be prefixed and '...' will be stripped.
4818 * @param string $module Which module is the page defined in
4819 * @param mixed $image Use a help image for the link? (true/false/"both")
4820 * @param boolean $linktext If true, display the title next to the help icon.
4821 * @param string $text If defined then this text is used in the page, and
4822 * the $page variable is ignored.
4823 * @param boolean $return If true then the output is returned as a string, if false it is printed to the current page.
4824 * @param string $imagetext The full text for the helpbutton icon. If empty use default help.gif
4825 * @return string
4826 * @todo Finish documenting this function
4828 function helpbutton ($page, $title='', $module='moodle', $image=true, $linktext=false, $text='', $return=false,
4829 $imagetext='') {
4830 global $CFG, $course;
4832 // fix for MDL-7734
4833 if (!empty($course->lang)) {
4834 $forcelang = $course->lang;
4835 } else {
4836 $forcelang = '';
4839 if ($module == '') {
4840 $module = 'moodle';
4843 $tooltip = get_string('helpprefix2', '', trim($title, ". \t"));
4845 $linkobject = '';
4847 if ($image) {
4848 if ($linktext) {
4849 // MDL-7469 If text link is displayed with help icon, change to alt to "help with this".
4850 $linkobject .= $title.'&nbsp;';
4851 $tooltip = get_string('helpwiththis');
4853 if ($imagetext) {
4854 $linkobject .= $imagetext;
4855 } else {
4856 $linkobject .= '<img class="iconhelp" alt="'.$tooltip.'" src="'.
4857 $CFG->pixpath .'/help.gif" />';
4859 } else {
4860 $linkobject .= $tooltip;
4863 $tooltip .= ' ('.get_string('newwindow').')'; // Warn users about new window for Accessibility
4865 // fix for MDL-7734
4866 if ($text) {
4867 $url = '/help.php?module='. $module .'&amp;text='. s(urlencode($text).'&amp;forcelang='.$forcelang);
4868 } else {
4869 $url = '/help.php?module='. $module .'&amp;file='. $page .'.html&amp;forcelang='.$forcelang;
4872 $link = '<span class="helplink">'.
4873 link_to_popup_window ($url, 'popup', $linkobject, 400, 500, $tooltip, 'none', true).
4874 '</span>';
4876 if ($return) {
4877 return $link;
4878 } else {
4879 echo $link;
4884 * Print a help button.
4886 * Prints a special help button that is a link to the "live" emoticon popup
4887 * @uses $CFG
4888 * @uses $SESSION
4889 * @param string $form ?
4890 * @param string $field ?
4891 * @todo Finish documenting this function
4893 function emoticonhelpbutton($form, $field, $return = false) {
4895 global $CFG, $SESSION;
4897 $SESSION->inserttextform = $form;
4898 $SESSION->inserttextfield = $field;
4899 $imagetext = '<img src="' . $CFG->pixpath . '/s/smiley.gif" alt="" class="emoticon" style="margin-left:3px; padding-right:1px;width:15px;height:15px;" />';
4900 $help = helpbutton('emoticons', get_string('helpemoticons'), 'moodle', true, true, '', true, $imagetext);
4901 if (!$return){
4902 echo $help;
4903 } else {
4904 return $help;
4909 * Print a help button.
4911 * Prints a special help button for html editors (htmlarea in this case)
4912 * @uses $CFG
4914 function editorshortcutshelpbutton() {
4916 global $CFG;
4917 $imagetext = '<img src="' . $CFG->httpswwwroot . '/lib/editor/htmlarea/images/kbhelp.gif" alt="'.
4918 get_string('editorshortcutkeys').'" class="iconkbhelp" />';
4920 return helpbutton('editorshortcuts', get_string('editorshortcutkeys'), 'moodle', true, false, '', true, $imagetext);
4924 * Print a message and exit.
4926 * @uses $CFG
4927 * @param string $message ?
4928 * @param string $link ?
4929 * @todo Finish documenting this function
4931 function notice ($message, $link='', $course=NULL, $adminroot='') {
4932 global $CFG, $SITE;
4934 $message = clean_text($message);
4936 print_box($message, 'generalbox', 'notice');
4937 print_continue($link);
4939 // xhtml strict fix, need to make sure it's the right footer
4940 if ($adminroot) {
4941 admin_externalpage_print_footer($adminroot);
4942 exit;
4945 if (empty($course)) {
4946 print_footer($SITE);
4947 } else {
4948 print_footer($course);
4950 exit;
4954 * Print a message along with "Yes" and "No" links for the user to continue.
4956 * @param string $message The text to display
4957 * @param string $linkyes The link to take the user to if they choose "Yes"
4958 * @param string $linkno The link to take the user to if they choose "No"
4960 function notice_yesno ($message, $linkyes, $linkno, $optionsyes=NULL, $optionsno=NULL, $methodyes='post', $methodno='post') {
4962 global $CFG;
4964 $message = clean_text($message);
4965 $linkyes = clean_text($linkyes);
4966 $linkno = clean_text($linkno);
4968 print_box_start('generalbox', 'notice');
4969 echo '<p>'. $message .'</p>';
4970 echo '<div class="buttons">';
4971 print_single_button($linkyes, $optionsyes, get_string('yes'), $methodyes, $CFG->framename);
4972 print_single_button($linkno, $optionsno, get_string('no'), $methodno, $CFG->framename);
4973 echo '</div>';
4974 print_box_end();
4978 * Redirects the user to another page, after printing a notice
4980 * @param string $url The url to take the user to
4981 * @param string $message The text message to display to the user about the redirect, if any
4982 * @param string $delay How long before refreshing to the new page at $url?
4983 * @todo '&' needs to be encoded into '&amp;' for XHTML compliance,
4984 * however, this is not true for javascript. Therefore we
4985 * first decode all entities in $url (since we cannot rely on)
4986 * the correct input) and then encode for where it's needed
4987 * echo "<script type='text/javascript'>alert('Redirect $url');</script>";
4989 function redirect($url, $message='', $delay=-1, $adminroot = '') {
4991 global $CFG;
4993 if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()])) {
4994 $url = sid_process_url($url);
4997 $message = clean_text($message);
4999 $url = html_entity_decode($url);
5000 $url = str_replace(array("\n", "\r"), '', $url); // some more cleaning
5001 $encodedurl = htmlentities($url);
5002 $tmpstr = clean_text('<a href="'.$encodedurl.'" />'); //clean encoded URL
5003 $encodedurl = substr($tmpstr, 9, strlen($tmpstr)-13);
5004 $url = html_entity_decode($encodedurl);
5005 $surl = addslashes($url);
5007 /// At developer debug level. Don't redirect if errors have been printed on screen.
5008 $errorprinted = false;
5009 if (debugging('', DEBUG_DEVELOPER) && $CFG->debugdisplay && error_get_last()) {
5010 $errorprinted = true;
5011 $message = "<strong>Error output, so disabling automatic redirect.</strong></p><p>" . $message;
5014 /// when no message and header printed yet, try to redirect
5015 if (empty($message) and !defined('HEADER_PRINTED')) {
5017 // Technically, HTTP/1.1 requires Location: header to contain
5018 // the absolute path. (In practice browsers accept relative
5019 // paths - but still, might as well do it properly.)
5020 // This code turns relative into absolute.
5021 if (!preg_match('|^[a-z]+:|', $url)) {
5022 // Get host name http://www.wherever.com
5023 $hostpart = preg_replace('|^(.*?[^:/])/.*$|', '$1', $CFG->wwwroot);
5024 if (preg_match('|^/|', $url)) {
5025 // URLs beginning with / are relative to web server root so we just add them in
5026 $url = $hostpart.$url;
5027 } else {
5028 // URLs not beginning with / are relative to path of current script, so add that on.
5029 $url = $hostpart.preg_replace('|\?.*$|','',me()).'/../'.$url;
5031 // Replace all ..s
5032 while (true) {
5033 $newurl = preg_replace('|/(?!\.\.)[^/]*/\.\./|', '/', $url);
5034 if ($newurl == $url) {
5035 break;
5037 $url = $newurl;
5041 $delay = 0;
5042 //try header redirection first
5043 @header($_SERVER['SERVER_PROTOCOL'] . ' 303 See Other'); //302 might not work for POST requests, 303 is ignored by obsolete clients
5044 @header('Location: '.$url);
5045 //another way for older browsers and already sent headers (eg trailing whitespace in config.php)
5046 echo '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />';
5047 echo '<script type="text/javascript">'. "\n" .'//<![CDATA['. "\n". "location.replace('$surl');". "\n". '//]]>'. "\n". '</script>'; // To cope with Mozilla bug
5048 die;
5051 if ($delay == -1) {
5052 $delay = 3; // if no delay specified wait 3 seconds
5054 if (! defined('HEADER_PRINTED')) {
5055 // this type of redirect might not be working in some browsers - such as lynx :-(
5056 print_header('', '', '', '', $errorprinted ? '' : ('<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'));
5057 $delay += 3; // double redirect prevention, it was sometimes breaking upgrades before 1.7
5059 echo '<div style="text-align:center">';
5060 echo '<p>'. $message .'</p>';
5061 echo '<p>( <a href="'. $encodedurl .'">'. get_string('continue') .'</a> )</p>';
5062 echo '</div>';
5064 if (!$errorprinted) {
5066 <script type="text/javascript">
5067 //<![CDATA[
5069 function redirect() {
5070 document.location.replace('<?php echo $surl ?>');
5072 setTimeout("redirect()", <?php echo ($delay * 1000) ?>);
5073 //]]>
5074 </script>
5075 <?php
5078 // fix for MDL-8517, admin pages redirections causes bad xhtml
5079 if ($adminroot) {
5080 admin_externalpage_print_footer($adminroot);
5081 } else {
5082 print_footer('none');
5084 die;
5088 * Print a bold message in an optional color.
5090 * @param string $message The message to print out
5091 * @param string $style Optional style to display message text in
5092 * @param string $align Alignment option
5093 * @param bool $return whether to return an output string or echo now
5095 function notify($message, $style='notifyproblem', $align='center', $return=false) {
5096 if ($style == 'green') {
5097 $style = 'notifysuccess'; // backward compatible with old color system
5100 $message = clean_text($message);
5102 $output = '<div class="'.$style.'" style="text-align:'. $align .'">'. $message .'</div>'."<br />\n";
5104 if ($return) {
5105 return $output;
5107 echo $output;
5112 * Given an email address, this function will return an obfuscated version of it
5114 * @param string $email The email address to obfuscate
5115 * @return string
5117 function obfuscate_email($email) {
5119 $i = 0;
5120 $length = strlen($email);
5121 $obfuscated = '';
5122 while ($i < $length) {
5123 if (rand(0,2)) {
5124 $obfuscated.='%'.dechex(ord($email{$i}));
5125 } else {
5126 $obfuscated.=$email{$i};
5128 $i++;
5130 return $obfuscated;
5134 * This function takes some text and replaces about half of the characters
5135 * with HTML entity equivalents. Return string is obviously longer.
5137 * @param string $plaintext The text to be obfuscated
5138 * @return string
5140 function obfuscate_text($plaintext) {
5142 $i=0;
5143 $length = strlen($plaintext);
5144 $obfuscated='';
5145 $prev_obfuscated = false;
5146 while ($i < $length) {
5147 $c = ord($plaintext{$i});
5148 $numerical = ($c >= ord('0')) && ($c <= ord('9'));
5149 if ($prev_obfuscated and $numerical ) {
5150 $obfuscated.='&#'.ord($plaintext{$i}).';';
5151 } else if (rand(0,2)) {
5152 $obfuscated.='&#'.ord($plaintext{$i}).';';
5153 $prev_obfuscated = true;
5154 } else {
5155 $obfuscated.=$plaintext{$i};
5156 $prev_obfuscated = false;
5158 $i++;
5160 return $obfuscated;
5164 * This function uses the {@link obfuscate_email()} and {@link obfuscate_text()}
5165 * to generate a fully obfuscated email link, ready to use.
5167 * @param string $email The email address to display
5168 * @param string $label The text to dispalyed as hyperlink to $email
5169 * @param boolean $dimmed If true then use css class 'dimmed' for hyperlink
5170 * @return string
5172 function obfuscate_mailto($email, $label='', $dimmed=false) {
5174 if (empty($label)) {
5175 $label = $email;
5177 if ($dimmed) {
5178 $title = get_string('emaildisable');
5179 $dimmed = ' class="dimmed"';
5180 } else {
5181 $title = '';
5182 $dimmed = '';
5184 return sprintf("<a href=\"%s:%s\" $dimmed title=\"$title\">%s</a>",
5185 obfuscate_text('mailto'), obfuscate_email($email),
5186 obfuscate_text($label));
5190 * Prints a single paging bar to provide access to other pages (usually in a search)
5192 * @param int $totalcount Thetotal number of entries available to be paged through
5193 * @param int $page The page you are currently viewing
5194 * @param int $perpage The number of entries that should be shown per page
5195 * @param string $baseurl The url which will be used to create page numbered links. Each page will consist of the base url appended by the page
5196 var an equal sign, then the page number.
5197 * @param string $pagevar This is the variable name that you use for the page number in your code (ie. 'tablepage', 'blogpage', etc)
5198 * @param bool $nocurr do not display the current page as a link
5199 * @param bool $return whether to return an output string or echo now
5200 * @return bool or string
5202 function print_paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar='page',$nocurr=false, $return=false) {
5203 $maxdisplay = 18;
5204 $output = '';
5206 if ($totalcount > $perpage) {
5207 $output .= '<div class="paging">';
5208 $output .= get_string('page') .':';
5209 if ($page > 0) {
5210 $pagenum = $page - 1;
5211 $output .= '&nbsp;(<a href="'. $baseurl . $pagevar .'='. $pagenum .'">'. get_string('previous') .'</a>)&nbsp;';
5213 $lastpage = ceil($totalcount / $perpage);
5214 if ($page > 15) {
5215 $startpage = $page - 10;
5216 $output .= '&nbsp;<a href="'. $baseurl . $pagevar .'=0">1</a>&nbsp;...';
5217 } else {
5218 $startpage = 0;
5220 $currpage = $startpage;
5221 $displaycount = 0;
5222 while ($displaycount < $maxdisplay and $currpage < $lastpage) {
5223 $displaypage = $currpage+1;
5224 if ($page == $currpage && empty($nocurr)) {
5225 $output .= '&nbsp;&nbsp;'. $displaypage;
5226 } else {
5227 $output .= '&nbsp;&nbsp;<a href="'. $baseurl . $pagevar .'='. $currpage .'">'. $displaypage .'</a>';
5229 $displaycount++;
5230 $currpage++;
5232 if ($currpage < $lastpage) {
5233 $lastpageactual = $lastpage - 1;
5234 $output .= '&nbsp;...<a href="'. $baseurl . $pagevar .'='. $lastpageactual .'">'. $lastpage .'</a>&nbsp;';
5236 $pagenum = $page + 1;
5237 if ($pagenum != $displaypage) {
5238 $output .= '&nbsp;&nbsp;(<a href="'. $baseurl . $pagevar .'='. $pagenum .'">'. get_string('next') .'</a>)';
5240 $output .= '</div>';
5243 if ($return) {
5244 return $output;
5247 echo $output;
5248 return true;
5252 * This function is used to rebuild the <nolink> tag because some formats (PLAIN and WIKI)
5253 * will transform it to html entities
5255 * @param string $text Text to search for nolink tag in
5256 * @return string
5258 function rebuildnolinktag($text) {
5260 $text = preg_replace('/&lt;(\/*nolink)&gt;/i','<$1>',$text);
5262 return $text;
5266 * Prints a nice side block with an optional header. The content can either
5267 * be a block of HTML or a list of text with optional icons.
5269 * @param string $heading Block $title embedded in HTML tags, for example <h2>.
5270 * @param string $content ?
5271 * @param array $list ?
5272 * @param array $icons ?
5273 * @param string $footer ?
5274 * @param array $attributes ?
5275 * @param string $title Plain text title, as embedded in the $heading.
5276 * @todo Finish documenting this function. Show example of various attributes, etc.
5278 function print_side_block($heading='', $content='', $list=NULL, $icons=NULL, $footer='', $attributes = array(), $title='') {
5280 //Accessibility: skip block link, with title-text (or $block_id) to differentiate links.
5281 static $block_id = 0;
5282 $block_id++;
5283 if (empty($heading)) {
5284 $skip_text = get_string('skipblock', 'access').' '.$block_id;
5286 else {
5287 $skip_text = get_string('skipa', 'access', strip_tags($title));
5289 $skip_link = '<a href="#sb-'.$block_id.'" class="skip-block" title="'.$skip_text.'"><span class="accesshide">'.$skip_text.'</span></a>';
5290 $skip_dest = '<span id="sb-'.$block_id.'" class="skip-block-to"></span>';
5292 if (! empty($heading)) {
5293 $heading = $skip_link . $heading;
5295 /*else { //ELSE: I think a single link on a page, "Skip block 4" is too confusing - don't print.
5296 echo $skip_link;
5299 print_side_block_start($heading, $attributes);
5301 if ($content) {
5302 echo $content;
5303 if ($footer) {
5304 echo '<div class="footer">'. $footer .'</div>';
5306 } else {
5307 if ($list) {
5308 $row = 0;
5309 //Accessibility: replaced unnecessary table with list, see themes/standard/styles_layout.css
5310 echo "\n<ul class='list'>\n";
5311 foreach ($list as $key => $string) {
5312 echo '<li class="r'. $row .'">';
5313 if ($icons) {
5314 echo '<div class="icon column c0">'. $icons[$key] .'</div>';
5316 echo '<div class="column c1">'. $string .'</div>';
5317 echo "</li>\n";
5318 $row = $row ? 0:1;
5320 echo "</ul>\n";
5322 if ($footer) {
5323 echo '<div class="footer">'. $footer .'</div>';
5328 print_side_block_end($attributes);
5329 echo $skip_dest;
5333 * Starts a nice side block with an optional header.
5335 * @param string $heading ?
5336 * @param array $attributes ?
5337 * @todo Finish documenting this function
5339 function print_side_block_start($heading='', $attributes = array()) {
5341 global $CFG, $THEME;
5343 // If there are no special attributes, give a default CSS class
5344 if (empty($attributes) || !is_array($attributes)) {
5345 $attributes = array('class' => 'sideblock');
5347 } else if(!isset($attributes['class'])) {
5348 $attributes['class'] = 'sideblock';
5350 } else if(!strpos($attributes['class'], 'sideblock')) {
5351 $attributes['class'] .= ' sideblock';
5354 // OK, the class is surely there and in addition to anything
5355 // else, it's tagged as a sideblock
5359 // IE misery: if I do it this way, blocks which start hidden cannot be "unhidden"
5361 // If there is a cookie to hide this thing, start it hidden
5362 if (!empty($attributes['id']) && isset($_COOKIE['hide:'.$attributes['id']])) {
5363 $attributes['class'] = 'hidden '.$attributes['class'];
5367 $attrtext = '';
5368 foreach ($attributes as $attr => $val) {
5369 $attrtext .= ' '.$attr.'="'.$val.'"';
5372 echo '<div '.$attrtext.'>';
5374 if ($heading) {
5375 //Accessibility: replaced <div> with H2; no, H2 more appropriate in moodleblock.class.php: _title_html.
5376 // echo '<div class="header">'.$heading.'</div>';
5377 echo '<div class="header">';
5378 if (!empty($THEME->roundcorners)) {
5379 echo '<div class="bt"><div></div></div>';
5380 echo '<div class="i1"><div class="i2"><div class="i3">';
5382 echo $heading;
5383 if (!empty($THEME->roundcorners)) {
5384 echo '</div></div></div>';
5386 echo '</div>';
5387 } else {
5388 if (!empty($THEME->roundcorners)) {
5389 echo '<div class="bt"><div></div></div>';
5393 if (!empty($THEME->roundcorners)) {
5394 echo '<div class="i1"><div class="i2"><div class="i3">';
5396 echo '<div class="content">';
5402 * Print table ending tags for a side block box.
5404 function print_side_block_end($attributes = array()) {
5405 global $CFG, $THEME;
5407 echo '</div>';
5409 if (!empty($THEME->roundcorners)) {
5410 echo '</div></div></div>';
5411 echo '<div class="bb"><div></div></div>';
5414 echo '</div>';
5416 // IE workaround: if I do it THIS way, it works! WTF?
5417 if (!empty($CFG->allowuserblockhiding) && isset($attributes['id'])) {
5418 echo '<script type="text/javascript">'."\n//<![CDATA[\n".'elementCookieHide("'.$attributes['id'].'"); '.
5419 "\n//]]>\n".'</script>';
5426 * Prints out code needed for spellchecking.
5427 * Original idea by Ludo (Marc Alier).
5429 * Opening CDATA and <script> are output by weblib::use_html_editor()
5430 * @uses $CFG
5431 * @param boolean $usehtmleditor Normally set by $CFG->htmleditor, can be overriden here
5432 * @param boolean $return If false, echos the code instead of returning it
5433 * @todo Find out if lib/editor/htmlarea/htmlarea.class.php::print_speller_code() is still used, and delete if not
5435 function print_speller_code ($usehtmleditor=false, $return=false) {
5436 global $CFG;
5437 $str = '';
5439 if(!$usehtmleditor) {
5440 $str .= 'function openSpellChecker() {'."\n";
5441 $str .= "\tvar speller = new spellChecker();\n";
5442 $str .= "\tspeller.popUpUrl = \"" . $CFG->wwwroot ."/lib/speller/spellchecker.html\";\n";
5443 $str .= "\tspeller.spellCheckScript = \"". $CFG->wwwroot ."/lib/speller/server-scripts/spellchecker.php\";\n";
5444 $str .= "\tspeller.spellCheckAll();\n";
5445 $str .= '}'."\n";
5446 } else {
5447 $str .= "function spellClickHandler(editor, buttonId) {\n";
5448 $str .= "\teditor._textArea.value = editor.getHTML();\n";
5449 $str .= "\tvar speller = new spellChecker( editor._textArea );\n";
5450 $str .= "\tspeller.popUpUrl = \"" . $CFG->wwwroot ."/lib/speller/spellchecker.html\";\n";
5451 $str .= "\tspeller.spellCheckScript = \"". $CFG->wwwroot ."/lib/speller/server-scripts/spellchecker.php\";\n";
5452 $str .= "\tspeller._moogle_edit=1;\n";
5453 $str .= "\tspeller._editor=editor;\n";
5454 $str .= "\tspeller.openChecker();\n";
5455 $str .= '}'."\n";
5458 if ($return) {
5459 return $str;
5461 echo $str;
5465 * Print button for spellchecking when editor is disabled
5467 function print_speller_button () {
5468 echo '<input type="button" value="Check spelling" onclick="openSpellChecker();" />'."\n";
5472 function page_id_and_class(&$getid, &$getclass) {
5473 // Create class and id for this page
5474 global $CFG, $ME;
5476 static $class = NULL;
5477 static $id = NULL;
5479 if (empty($CFG->pagepath)) {
5480 $CFG->pagepath = $ME;
5483 if (empty($class) || empty($id)) {
5484 $path = str_replace($CFG->httpswwwroot.'/', '', $CFG->pagepath); //Because the page could be HTTPSPAGEREQUIRED
5485 $path = str_replace('.php', '', $path);
5486 if (substr($path, -1) == '/') {
5487 $path .= 'index';
5489 if (empty($path) || $path == 'index') {
5490 $id = 'site-index';
5491 $class = 'course';
5492 } else if (substr($path, 0, 5) == 'admin') {
5493 $id = str_replace('/', '-', $path);
5494 $class = 'admin';
5495 } else {
5496 $id = str_replace('/', '-', $path);
5497 $class = explode('-', $id);
5498 array_pop($class);
5499 $class = implode('-', $class);
5503 $getid = $id;
5504 $getclass = $class;
5508 * Prints a maintenance message from /maintenance.html
5510 function print_maintenance_message () {
5511 global $CFG, $SITE;
5513 print_header(strip_tags($SITE->fullname), $SITE->fullname, 'home');
5514 print_simple_box_start('center');
5515 print_heading(get_string('sitemaintenance', 'admin'));
5516 @include($CFG->dataroot.'/1/maintenance.html');
5517 print_simple_box_end();
5518 print_footer();
5522 * Adjust the list of allowed tags based on $CFG->allowobjectembed and user roles (admin)
5524 function adjust_allowed_tags() {
5526 global $CFG, $ALLOWED_TAGS;
5528 if (!empty($CFG->allowobjectembed)) {
5529 $ALLOWED_TAGS .= '<embed><object>';
5533 /// Some code to print tabs
5535 /// A class for tabs
5536 class tabobject {
5537 var $id;
5538 var $link;
5539 var $text;
5540 var $linkedwhenselected;
5542 /// A constructor just because I like constructors
5543 function tabobject ($id, $link='', $text='', $title='', $linkedwhenselected=false) {
5544 $this->id = $id;
5545 $this->link = $link;
5546 $this->text = $text;
5547 $this->title = $title ? $title : $text;
5548 $this->linkedwhenselected = $linkedwhenselected;
5555 * Returns a string containing a nested list, suitable for formatting into tabs with CSS.
5557 * @param array $tabrows An array of rows where each row is an array of tab objects
5558 * @param string $selected The id of the selected tab (whatever row it's on)
5559 * @param array $inactive An array of ids of inactive tabs that are not selectable.
5560 * @param array $activated An array of ids of other tabs that are currently activated
5562 function print_tabs($tabrows, $selected=NULL, $inactive=NULL, $activated=NULL, $return=false) {
5563 global $CFG;
5565 /// Bring the row with the selected tab to the front
5566 if (!empty($CFG->tabselectedtofront) and ($selected !== NULL) ) {
5567 $found = false;
5568 $frontrows = array();
5569 $rearrows = array();
5570 foreach ($tabrows as $row) {
5571 if ($found) {
5572 $rearrows[] = $row;
5573 } else {
5574 foreach ($row as $tab) {
5575 if ($found) {
5576 continue;
5578 $found = ($selected == $tab->id);
5580 $frontrows[] = $row;
5583 $tabrows = array_merge($rearrows,$frontrows);
5586 /// $inactive must be an array
5587 if (!is_array($inactive)) {
5588 $inactive = array();
5591 /// $activated must be an array
5592 if (!is_array($activated)) {
5593 $activated = array();
5596 /// Convert the tab rows into a tree that's easier to process
5597 if (!$tree = convert_tabrows_to_tree($tabrows, $selected, $inactive, $activated)) {
5598 return false;
5601 /// Print out the current tree of tabs (this function is recursive)
5603 $output = convert_tree_to_html($tree);
5605 $output = "\n\n".'<div class="tabtree">'.$output.'</div><div class="clearer"> </div>'."\n\n";
5607 /// We're done!
5609 if ($return) {
5610 return $output;
5612 echo $output;
5616 function convert_tree_to_html($tree, $row=0) {
5618 $str = "\n".'<ul class="tabrow'.$row.'">'."\n";
5620 $first = true;
5621 $count = count($tree);
5623 foreach ($tree as $tab) {
5624 $count--; // countdown to zero
5626 $liclass = '';
5628 if ($first && ($count == 0)) { // Just one in the row
5629 $liclass = 'first last';
5630 $first = false;
5631 } else if ($first) {
5632 $liclass = 'first';
5633 $first = false;
5634 } else if ($count == 0) {
5635 $liclass = 'last';
5638 if ((empty($tab->subtree)) && (!empty($tab->selected))) {
5639 $liclass .= (empty($liclass)) ? 'onerow' : ' onerow';
5642 if ($tab->inactive || $tab->active || ($tab->selected && !$tab->linkedwhenselected)) {
5643 if ($tab->selected) {
5644 $liclass .= (empty($liclass)) ? 'here selected' : ' here selected';
5645 } else if ($tab->active) {
5646 $liclass .= (empty($liclass)) ? 'here active' : ' here active';
5650 $str .= (!empty($liclass)) ? '<li class="'.$liclass.'">' : '<li>';
5652 if ($tab->inactive || $tab->active || ($tab->selected && !$tab->linkedwhenselected)) {
5653 $str .= '<a href="#" title="'.$tab->title.'"><span>'.$tab->text.'</span></a>';
5654 } else {
5655 $str .= '<a href="'.$tab->link.'" title="'.$tab->title.'"><span>'.$tab->text.'</span></a>';
5658 if (!empty($tab->subtree)) {
5659 $str .= convert_tree_to_html($tab->subtree, $row+1);
5660 } else if ($tab->selected) {
5661 $str .= '<div class="tabrow'.($row+1).' empty">&nbsp;</div>'."\n";
5664 $str .= '</li>'."\n";
5666 $str .= '</ul>'."\n";
5668 return $str;
5672 function convert_tabrows_to_tree($tabrows, $selected, $inactive, $activated) {
5674 /// Work backwards through the rows (bottom to top) collecting the tree as we go.
5676 $tabrows = array_reverse($tabrows);
5678 $subtree = array();
5680 foreach ($tabrows as $row) {
5681 $tree = array();
5683 foreach ($row as $tab) {
5684 $tab->inactive = in_array((string)$tab->id, $inactive);
5685 $tab->active = in_array((string)$tab->id, $activated);
5686 $tab->selected = (string)$tab->id == $selected;
5688 if ($tab->active || $tab->selected) {
5689 if ($subtree) {
5690 $tab->subtree = $subtree;
5693 $tree[] = $tab;
5695 $subtree = $tree;
5698 return $subtree;
5703 * Returns a string containing a link to the user documentation for the current
5704 * page. Also contains an icon by default. Shown to teachers and admin only.
5706 * @param string $text The text to be displayed for the link
5707 * @param string $iconpath The path to the icon to be displayed
5709 function page_doc_link($text='', $iconpath='') {
5710 global $ME, $CFG;
5712 if (empty($CFG->docroot) || !has_capability('moodle/site:doclinks')) {
5713 return '';
5716 if (empty($CFG->pagepath)) {
5717 $CFG->pagepath = $ME;
5720 $target = '';
5721 if (!empty($CFG->doctonewwindow)) {
5722 $target = ' target="_blank"';
5725 $path = str_replace($CFG->httpswwwroot.'/','', $CFG->pagepath); // Because the page could be HTTPSPAGEREQUIRED
5726 $path = str_replace('.php', '', $path);
5728 if (empty($path)) { // Not for home page
5729 return '';
5732 $lang = str_replace('_utf8', '', current_language());
5734 $str = '<a href="' .$CFG->docroot. '/' .$lang. '/' .$path. '"' .$target. '>';
5736 if (empty($iconpath)) {
5737 $iconpath = $CFG->httpswwwroot . '/pix/docs.gif';
5740 // alt left blank intentionally to prevent repetition in screenreaders
5741 $str .= '<img class="iconhelp" src="' .$iconpath. '" alt="" />' .$text. '</a>';
5743 return $str;
5747 * Returns true if the current site debugging settings are equal or above specified level.
5748 * If passed a parameter it will emit a debugging notice similar to trigger_error(). The
5749 * routing of notices is controlled by $CFG->debugdisplay
5750 * eg use like this:
5752 * 1) debugging('a normal debug notice');
5753 * 2) debugging('something really picky', DEBUG_ALL);
5754 * 3) debugging('annoying debug message only for develpers', DEBUG_DEVELOPER);
5755 * 4) if (debugging()) { perform extra debugging operations (do not use print or echo) }
5757 * In code blocks controlled by debugging() (such as example 4)
5758 * any output should be routed via debugging() itself, or the lower-level
5759 * trigger_error() or error_log(). Using echo or print will break XHTML
5760 * JS and HTTP headers.
5763 * @param string $message a message to print
5764 * @param int $level the level at which this debugging statement should show
5765 * @return bool
5767 function debugging($message='', $level=DEBUG_NORMAL) {
5769 global $CFG;
5771 if (empty($CFG->debug)) {
5772 return false;
5775 if ($CFG->debug >= $level) {
5776 if ($message) {
5777 $caller = debug_backtrace();
5778 $caller = $caller[0];
5779 $from = " in $caller[file] on line $caller[line]";
5780 if (isset($caller['function'])) {
5781 $from .= " in $caller[function]()";
5783 if (!isset($CFG->debugdisplay)) {
5784 $CFG->debugdisplay = ini_get('display_errors');
5786 if ($CFG->debugdisplay) {
5787 notify($message . $from, 'notifytiny');
5788 } else {
5789 trigger_error($message . $from, E_USER_NOTICE);
5792 return true;
5794 return false;
5798 * Disable debug messages from debugging(), while keeping PHP error reporting level as is.
5800 function disable_debugging() {
5801 global $CFG;
5802 $CFG->debug = $CFG->debug | 0x80000000; // switch the sign bit in integer number ;-)
5807 * Returns string to add a frame attribute, if required
5809 function frametarget() {
5810 global $CFG;
5812 if (empty($CFG->framename) or ($CFG->framename == '_top')) {
5813 return '';
5814 } else {
5815 return ' target="'.$CFG->framename.'" ';
5819 // vim:autoindent:expandtab:shiftwidth=4:tabstop=4:tw=140: