MDL-8323 finished full conversion to proper $COURSE global - no more $CFG->coursethem...
[moodle.git] / lib / weblib.php
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
28  *
29  * Library of all general-purpose Moodle PHP functions and constants
30  * that produce HTML output
31  *
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
39  */
41 /// We are going to uses filterlib functions here
42 require_once("$CFG->libdir/filterlib.php");
44 /// Constants
46 /// Define text formatting types ... eventually we can add Wiki, BBcode etc
48 /**
49  * Does all sorts of transformations and filtering
50  */
51 define('FORMAT_MOODLE',   '0');   // Does all sorts of transformations and filtering
53 /**
54  * Plain HTML (with some tags stripped)
55  */
56 define('FORMAT_HTML',     '1');   // Plain HTML (with some tags stripped)
58 /**
59  * Plain text (even tags are printed in full)
60  */
61 define('FORMAT_PLAIN',    '2');   // Plain text (even tags are printed in full)
63 /**
64  * Wiki-formatted text
65  * Deprecated: left here just to note that '3' is not used (at the moment)
66  * and to catch any latent wiki-like text (which generates an error)
67  */
68 define('FORMAT_WIKI',     '3');   // Wiki-formatted text
70 /**
71  * Markdown-formatted text http://daringfireball.net/projects/markdown/
72  */
73 define('FORMAT_MARKDOWN', '4');   // Markdown-formatted text http://daringfireball.net/projects/markdown/
75 /**
76  * TRUSTTEXT marker - if present in text, text cleaning should be bypassed
77  */
78 define('TRUSTTEXT', '#####TRUSTTEXT#####');
81 /**
82  * Allowed tags - string of html tags that can be tested against for safe html tags
83  * @global string $ALLOWED_TAGS
84  */
85 $ALLOWED_TAGS =
86 '<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>';
88 /**
89  * Allowed protocols - array of protocols that are safe to use in links and so on
90  * @global string $ALLOWED_PROTOCOLS
91  */
92 $ALLOWED_PROTOCOLS = array('http', 'https', 'ftp', 'news', 'mailto', 'rtsp', 'teamspeak', 'gopher', 'mms',
93                            'color', 'callto', 'cursor', 'text-align', 'font-size', 'font-weight', 'font-style',
94                            'border', 'margin', 'padding', 'background');   // CSS as well to get through kses
97 /// Functions
99 /**
100  * Add quotes to HTML characters
101  *
102  * Returns $var with HTML characters (like "<", ">", etc.) properly quoted.
103  * This function is very similar to {@link p()}
104  *
105  * @param string $var the string potentially containing HTML characters
106  * @param boolean $strip to decide if we want to strip slashes or no. Default to false.
107  *                true should be used to print data from forms and false for data from DB.
108  * @return string
109  */
110 function s($var, $strip=false) {
112     if ($var == '0') {  // for integer 0, boolean false, string '0'
113         return '0';
114     }
116     if ($strip) {
117         return preg_replace("/&amp;(#\d+);/i", "&$1;", htmlspecialchars(stripslashes_safe($var)));
118     } else {
119         return preg_replace("/&amp;(#\d+);/i", "&$1;", htmlspecialchars($var));
120     }
123 /**
124  * Add quotes to HTML characters
125  *
126  * Prints $var with HTML characters (like "<", ">", etc.) properly quoted.
127  * This function is very similar to {@link s()}
128  *
129  * @param string $var the string potentially containing HTML characters
130  * @param boolean $strip to decide if we want to strip slashes or no. Default to false.
131  *                true should be used to print data from forms and false for data from DB.
132  * @return string
133  */
134 function p($var, $strip=false) {
135     echo s($var, $strip);
138 /**
139  * Does proper javascript quoting.
140  * Do not use addslashes anymore, because it does not work when magic_quotes_sybase is enabled.
141  *
142  * @param mixed value
143  * @return mixed quoted result
144  */
145 function addslashes_js($var) {
146     if (is_string($var)) {
147         $var = str_replace('\\', '\\\\', $var);
148         $var = str_replace(array('\'', '"', "\n", "\r", "\0"), array('\\\'', '\\"', '\\n', '\\r', '\\0'), $var);
149         $var = str_replace('</', '<\/', $var);   // XHTML compliance
150     } else if (is_array($var)) {
151         $var = array_map('addslashes_js', $var);
152     } else if (is_object($var)) {
153         $a = get_object_vars($var);
154         foreach ($a as $key=>$value) {
155           $a[$key] = addslashes_js($value);
156         }
157         $var = (object)$a;
158     }
159     return $var;
162 /**
163  * Remove query string from url
164  *
165  * Takes in a URL and returns it without the querystring portion
166  *
167  * @param string $url the url which may have a query string attached
168  * @return string
169  */
170  function strip_querystring($url) {
172     if ($commapos = strpos($url, '?')) {
173         return substr($url, 0, $commapos);
174     } else {
175         return $url;
176     }
179 /**
180  * Returns the URL of the HTTP_REFERER, less the querystring portion
181  * @return string
182  */
183 function get_referer() {
184     if (isset($_SERVER['HTTP_REFERER'])) {
185         return strip_querystring($_SERVER['HTTP_REFERER']);
186     } else {
187         return '';
188     }
192 /**
193  * Returns the name of the current script, WITH the querystring portion.
194  * this function is necessary because PHP_SELF and REQUEST_URI and SCRIPT_NAME
195  * return different things depending on a lot of things like your OS, Web
196  * server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.)
197  * <b>NOTE:</b> This function returns false if the global variables needed are not set.
198  *
199  * @return string
200  */
201  function me() {
203     if (!empty($_SERVER['REQUEST_URI'])) {
204         return $_SERVER['REQUEST_URI'];
206     } else if (!empty($_SERVER['PHP_SELF'])) {
207         if (!empty($_SERVER['QUERY_STRING'])) {
208             return $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
209         }
210         return $_SERVER['PHP_SELF'];
212     } else if (!empty($_SERVER['SCRIPT_NAME'])) {
213         if (!empty($_SERVER['QUERY_STRING'])) {
214             return $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
215         }
216         return $_SERVER['SCRIPT_NAME'];
218     } else if (!empty($_SERVER['URL'])) {     // May help IIS (not well tested)
219         if (!empty($_SERVER['QUERY_STRING'])) {
220             return $_SERVER['URL'] .'?'. $_SERVER['QUERY_STRING'];
221         }
222         return $_SERVER['URL'];
224     } else {
225         notify('Warning: Could not find any of these web server variables: $REQUEST_URI, $PHP_SELF, $SCRIPT_NAME or $URL');
226         return false;
227     }
230 /**
231  * Like {@link me()} but returns a full URL
232  * @see me()
233  * @return string
234  */
235 function qualified_me() {
237     global $CFG;
239     if (!empty($CFG->wwwroot)) {
240         $url = parse_url($CFG->wwwroot);
241     }
243     if (!empty($url['host'])) {
244         $hostname = $url['host'];
245     } else if (!empty($_SERVER['SERVER_NAME'])) {
246         $hostname = $_SERVER['SERVER_NAME'];
247     } else if (!empty($_ENV['SERVER_NAME'])) {
248         $hostname = $_ENV['SERVER_NAME'];
249     } else if (!empty($_SERVER['HTTP_HOST'])) {
250         $hostname = $_SERVER['HTTP_HOST'];
251     } else if (!empty($_ENV['HTTP_HOST'])) {
252         $hostname = $_ENV['HTTP_HOST'];
253     } else {
254         notify('Warning: could not find the name of this server!');
255         return false;
256     }
258     if (!empty($url['port'])) {
259         $hostname .= ':'.$url['port'];
260     } else if (!empty($_SERVER['SERVER_PORT'])) {
261         if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
262             $hostname .= ':'.$_SERVER['SERVER_PORT'];
263         }
264     }
266     if (isset($_SERVER['HTTPS'])) {
267         $protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
268     } else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS']
269         $protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://';
270     } else {
271         $protocol = 'http://';
272     }
274     $url_prefix = $protocol.$hostname;
275     return $url_prefix . me();
278 /**
279  * Determine if there is data waiting to be processed from a form
280  *
281  * Used on most forms in Moodle to check for data
282  * Returns the data as an object, if it's found.
283  * This object can be used in foreach loops without
284  * casting because it's cast to (array) automatically
285  *
286  * Checks that submitted POST data exists and returns it as object.
287  *
288  * @param string $url not used anymore
289  * @return mixed false or object
290  */
291 function data_submitted($url='') {
293     if (empty($_POST)) {
294         return false;
295     } else {
296         return (object)$_POST;
297     }
300 /**
301  * Moodle replacement for php stripslashes() function,
302  * works also for objects and arrays.
303  *
304  * The standard php stripslashes() removes ALL backslashes
305  * even from strings - so  C:\temp becomes C:temp - this isn't good.
306  * This function should work as a fairly safe replacement
307  * to be called on quoted AND unquoted strings (to be sure)
308  *
309  * @param mixed something to remove unsafe slashes from
310  * @return mixed
311  */
312 function stripslashes_safe($mixed) {
313     // there is no need to remove slashes from int, float and bool types
314     if (empty($mixed)) {
315         //nothing to do...
316     } else if (is_string($mixed)) {
317         if (ini_get_bool('magic_quotes_sybase')) { //only unescape single quotes
318             $mixed = str_replace("''", "'", $mixed);
319         } else { //the rest, simple and double quotes and backslashes
320             $mixed = str_replace("\\'", "'", $mixed);
321             $mixed = str_replace('\\"', '"', $mixed);
322             $mixed = str_replace('\\\\', '\\', $mixed);
323         }
324     } else if (is_array($mixed)) {
325         foreach ($mixed as $key => $value) {
326             $mixed[$key] = stripslashes_safe($value);
327         }
328     } else if (is_object($mixed)) {
329         $vars = get_object_vars($mixed);
330         foreach ($vars as $key => $value) {
331             $mixed->$key = stripslashes_safe($value);
332         }
333     }
335     return $mixed;
338 /**
339  * Recursive implementation of stripslashes()
340  *
341  * This function will allow you to strip the slashes from a variable.
342  * If the variable is an array or object, slashes will be stripped
343  * from the items (or properties) it contains, even if they are arrays
344  * or objects themselves.
345  *
346  * @param mixed the variable to remove slashes from
347  * @return mixed
348  */
349 function stripslashes_recursive($var) {
350     if(is_object($var)) {
351         $properties = get_object_vars($var);
352         foreach($properties as $property => $value) {
353             $var->$property = stripslashes_recursive($value);
354         }
355     }
356     else if(is_array($var)) {
357         foreach($var as $property => $value) {
358             $var[$property] = stripslashes_recursive($value);
359         }
360     }
361     else if(is_string($var)) {
362         $var = stripslashes($var);
363     }
364     return $var;
367 /**
368  * Given some normal text this function will break up any
369  * long words to a given size by inserting the given character
370  *
371  * It's multibyte savvy and doesn't change anything inside html tags.
372  *
373  * @param string $string the string to be modified
374  * @param int $maxsize maximum length of the string to be returned
375  * @param string $cutchar the string used to represent word breaks
376  * @return string
377  */
378 function break_up_long_words($string, $maxsize=20, $cutchar=' ') {
380 /// Loading the textlib singleton instance. We are going to need it.
381     $textlib = textlib_get_instance();
383 /// First of all, save all the tags inside the text to skip them
384     $tags = array();
385     filter_save_tags($string,$tags);
387 /// Process the string adding the cut when necessary
388     $output = '';
389     $length = $textlib->strlen($string);
390     $wordlength = 0;
392     for ($i=0; $i<$length; $i++) {
393         $char = $textlib->substr($string, $i, 1);
394         if ($char == ' ' or $char == "\t" or $char == "\n" or $char == "\r" or $char == "<" or $char == ">") {
395             $wordlength = 0;
396         } else {
397             $wordlength++;
398             if ($wordlength > $maxsize) {
399                 $output .= $cutchar;
400                 $wordlength = 0;
401             }
402         }
403         $output .= $char;
404     }
406 /// Finally load the tags back again
407     if (!empty($tags)) {
408         $output = str_replace(array_keys($tags), $tags, $output);
409     }
411     return $output;
414 /**
415  * This does a search and replace, ignoring case
416  * This function is only used for versions of PHP older than version 5
417  * which do not have a native version of this function.
418  * Taken from the PHP manual, by bradhuizenga @ softhome.net
419  *
420  * @param string $find the string to search for
421  * @param string $replace the string to replace $find with
422  * @param string $string the string to search through
423  * return string
424  */
425 if (!function_exists('str_ireplace')) {    /// Only exists in PHP 5
426     function str_ireplace($find, $replace, $string) {
428         if (!is_array($find)) {
429             $find = array($find);
430         }
432         if(!is_array($replace)) {
433             if (!is_array($find)) {
434                 $replace = array($replace);
435             } else {
436                 // this will duplicate the string into an array the size of $find
437                 $c = count($find);
438                 $rString = $replace;
439                 unset($replace);
440                 for ($i = 0; $i < $c; $i++) {
441                     $replace[$i] = $rString;
442                 }
443             }
444         }
446         foreach ($find as $fKey => $fItem) {
447             $between = explode(strtolower($fItem),strtolower($string));
448             $pos = 0;
449             foreach($between as $bKey => $bItem) {
450                 $between[$bKey] = substr($string,$pos,strlen($bItem));
451                 $pos += strlen($bItem) + strlen($fItem);
452             }
453             $string = implode($replace[$fKey],$between);
454         }
455         return ($string);
456     }
459 /**
460  * Locate the position of a string in another string
461  *
462  * This function is only used for versions of PHP older than version 5
463  * which do not have a native version of this function.
464  * Taken from the PHP manual, by dmarsh @ spscc.ctc.edu
465  *
466  * @param string $haystack The string to be searched
467  * @param string $needle The string to search for
468  * @param int $offset The position in $haystack where the search should begin.
469  */
470 if (!function_exists('stripos')) {    /// Only exists in PHP 5
471     function stripos($haystack, $needle, $offset=0) {
473         return strpos(strtoupper($haystack), strtoupper($needle), $offset);
474     }
477 /**
478  * This function will create a HTML link that will work on both
479  * Javascript and non-javascript browsers.
480  * Relies on the Javascript function openpopup in javascript.php
481  *
482  * $url must be relative to home page  eg /mod/survey/stuff.php
483  * @param string $url Web link relative to home page
484  * @param string $name Name to be assigned to the popup window
485  * @param string $linkname Text to be displayed as web link
486  * @param int $height Height to assign to popup window
487  * @param int $width Height to assign to popup window
488  * @param string $title Text to be displayed as popup page title
489  * @param string $options List of additional options for popup window
490  * @todo Add code examples and list of some options that might be used.
491  * @param boolean $return Should the link to the popup window be returned as a string (true) or printed immediately (false)?
492  * @return string
493  * @uses $CFG
494  */
495 function link_to_popup_window ($url, $name='popup', $linkname='click here',
496                                $height=400, $width=500, $title='Popup window',
497                                $options='none', $return=false) {
499     global $CFG;
501     if ($options == 'none') {
502         $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
503     }
504     $fullscreen = 0;
506     if (!(strpos($url,$CFG->wwwroot) === false)) { // some log url entries contain _SERVER[HTTP_REFERRER] in which case wwwroot is already there.
507         $url = substr($url, strlen($CFG->wwwroot));
508     }
510     $link = '<a title="'. s($title) .'" href="'. $CFG->wwwroot . $url .'" '.
511            "onclick=\"this.target='$name'; return openpopup('$url', '$name', '$options', $fullscreen);\">$linkname</a>";
512     if ($return) {
513         return $link;
514     } else {
515         echo $link;
516     }
519 /**
520  * This function will print a button submit form element
521  * that will work on both Javascript and non-javascript browsers.
522  * Relies on the Javascript function openpopup in javascript.php
523  *
524  * $url must be relative to home page  eg /mod/survey/stuff.php
525  * @param string $url Web link relative to home page
526  * @param string $name Name to be assigned to the popup window
527  * @param string $linkname Text to be displayed as web link
528  * @param int $height Height to assign to popup window
529  * @param int $width Height to assign to popup window
530  * @param string $title Text to be displayed as popup page title
531  * @param string $options List of additional options for popup window
532  * @param string $return If true, return as a string, otherwise print
533  * @return string
534  * @uses $CFG
535  */
536 function button_to_popup_window ($url, $name='popup', $linkname='click here',
537                                  $height=400, $width=500, $title='Popup window', $options='none', $return=false,
538                                  $id='', $class='') {
540     global $CFG;
542     if ($options == 'none') {
543         $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
544     }
546     if ($id) {
547         $id = ' id="'.$id.'" ';
548     }
549     if ($class) {
550         $class = ' class="'.$class.'" ';
551     }
552     $fullscreen = 0;
554     $button = '<input type="button" name="'.$name.'" title="'. $title .'" value="'. $linkname .' ..." '.$id.$class.
555               "onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\" />\n";
556     if ($return) {
557         return $button;
558     } else {
559         echo $button;
560     }
564 /**
565  * Prints a simple button to close a window
566  */
567 function close_window_button($name='closewindow', $return=false) {
568     $output = '';
570     $output .= '<div class="closewindow">' . "\n";
571     $output .= '<script type="text/javascript">' . "\n";
572     $output .= '//<![CDATA[' . "\n";
573     $output .= "document.write('<form>');\n";
574     $output .= "document.write('<input type=\"button\" onclick=\"self.close();\" value=\"".get_string("closewindow")."\" />');\n";
575     $output .= "document.write('<\/form>');\n";
576     $output .= '//]]>' . "\n";
577     $output .= '</script>' . "\n";
578     $output .= '<noscript><p>' . "\n";
579     $output .= get_string($name);
580     $output .= '</p></noscript>' . "\n";
581     $output .= '</div>' . "\n";
583     if ($return) {
584         return $output;
585     } else {
586         echo $output;
587     }
590 /*
591  * Try and close the current window immediately using Javascript
592  */
593 function close_window($delay=0) {
594 ?>
595 <script type="text/javascript">
596 //<![CDATA[
597     function close_this_window() {
598         self.close();
599     }
600     setTimeout("close_this_window()", <?php echo $delay * 1000 ?>);
601 //]]>
602 </script>
603 <noscript><center>
604 <?php print_string('pleaseclose') ?>
605 </center></noscript>
606 <?php
607     die;
611 /**
612  * Given an array of value, creates a popup menu to be part of a form
613  * $options["value"]["label"]
614  *
615  * @param    type description
616  * @todo Finish documenting this function
617  */
618 function choose_from_menu ($options, $name, $selected='', $nothing='choose', $script='',
619                            $nothingvalue='0', $return=false, $disabled=false, $tabindex=0, $id='') {
621     if ($nothing == 'choose') {
622         $nothing = get_string('choose') .'...';
623     }
625     $attributes = ($script) ? 'onchange="'. $script .'"' : '';
626     if ($disabled) {
627         $attributes .= ' disabled="disabled"';
628     }
630     if ($tabindex) {
631         $attributes .= ' tabindex="'.$tabindex.'"';
632     }
634     if ($id ==='') {
635         $id = 'menu'.$name;
636          // name may contaion [], which would make an invalid id. e.g. numeric question type editing form, assignment quickgrading
637         $id = str_replace('[', '', $id);
638         $id = str_replace(']', '', $id);
639     }
641     $output = '<select id="'.$id.'" name="'. $name .'" '. $attributes .'>' . "\n";
642     if ($nothing) {
643         $output .= '   <option value="'. s($nothingvalue) .'"'. "\n";
644         if ($nothingvalue === $selected) {
645             $output .= ' selected="selected"';
646         }
647         $output .= '>'. $nothing .'</option>' . "\n";
648     }
649     if (!empty($options)) {
650         foreach ($options as $value => $label) {
651             $output .= '   <option value="'. s($value) .'"';
652             if ((string)$value == (string)$selected) {
653                 $output .= ' selected="selected"';
654             }
655             if ($label === '') {
656                 $output .= '>'. $value .'</option>' . "\n";
657             } else {
658                 $output .= '>'. $label .'</option>' . "\n";
659             }
660         }
661     }
662     $output .= '</select>' . "\n";
664     if ($return) {
665         return $output;
666     } else {
667         echo $output;
668     }
671 /**
672  * Choose value 0 or 1 from a menu with options 'No' and 'Yes'.
673  * Other options like choose_from_menu.
674  */
675 function choose_from_menu_yesno($name, $selected, $script = '',
676         $return = false, $disabled = false, $tabindex = 0) {
677     return choose_from_menu(array(get_string('no'), get_string('yes')), $name,
678             $selected, '', $script, '0', $return, $disabled, $tabindex);
681 /**
682  * Just like choose_from_menu, but takes a nested array (2 levels) and makes a dropdown menu
683  * including option headings with the first level.
684  */
685 function choose_from_menu_nested($options,$name,$selected='',$nothing='choose',$script = '',
686                                  $nothingvalue=0,$return=false,$disabled=false,$tabindex=0) {
688    if ($nothing == 'choose') {
689         $nothing = get_string('choose') .'...';
690     }
692     $attributes = ($script) ? 'onchange="'. $script .'"' : '';
693     if ($disabled) {
694         $attributes .= ' disabled="disabled"';
695     }
697     if ($tabindex) {
698         $attributes .= ' tabindex="'.$tabindex.'"';
699     }
701     $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
702     if ($nothing) {
703         $output .= '   <option value="'. $nothingvalue .'"'. "\n";
704         if ($nothingvalue === $selected) {
705             $output .= ' selected="selected"';
706         }
707         $output .= '>'. $nothing .'</option>' . "\n";
708     }
709     if (!empty($options)) {
710         foreach ($options as $section => $values) {
711             $output .= '   <optgroup label="'.$section.'">'."\n";
712             foreach ($values as $value => $label) {
713                 $output .= '   <option value="'. $value .'"';
714                 if ((string)$value == (string)$selected) {
715                     $output .= ' selected="selected"';
716                 }
717                 if ($label === '') {
718                     $output .= '>'. $value .'</option>' . "\n";
719                 } else {
720                     $output .= '>'. $label .'</option>' . "\n";
721                 }
722             }
723             $output .= '   </optgroup>'."\n";
724         }
725     }
726     $output .= '</select>' . "\n";
728     if ($return) {
729         return $output;
730     } else {
731         echo $output;
732     }
736 /**
737  * Given an array of values, creates a group of radio buttons to be part of a form
738  *
739  * @param array  $options  An array of value-label pairs for the radio group (values as keys)
740  * @param string $name     Name of the radiogroup (unique in the form)
741  * @param string $checked  The value that is already checked
742  */
743 function choose_from_radio ($options, $name, $checked='', $return=false) {
745     static $idcounter = 0;
747     if (!$name) {
748         $name = 'unnamed';
749     }
751     $output = '<span class="radiogroup '.$name."\">\n";
753     if (!empty($options)) {
754         $currentradio = 0;
755         foreach ($options as $value => $label) {
756             $htmlid = 'auto-rb'.sprintf('%04d', ++$idcounter);
757             $output .= ' <span class="radioelement '.$name.' rb'.$currentradio."\">";
758             $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="radio" value="'.$value.'"';
759             if ($value == $checked) {
760                 $output .= ' checked="checked"';
761             }
762             if ($label === '') {
763                 $output .= ' /> <label for="'.$htmlid.'">'.  $value .'</label></span>' .  "\n";
764             } else {
765                 $output .= ' /> <label for="'.$htmlid.'">'.  $label .'</label></span>' .  "\n";
766             }
767             $currentradio = ($currentradio + 1) % 2;
768         }
769     }
771     $output .= '</span>' .  "\n";
773     if ($return) {
774         return $output;
775     } else {
776         echo $output;
777     }
780 /** Display an standard html checkbox with an optional label
781  *
782  * @param string  $name    The name of the checkbox
783  * @param string  $value   The valus that the checkbox will pass when checked
784  * @param boolean $checked The flag to tell the checkbox initial state
785  * @param string  $label   The label to be showed near the checkbox
786  * @param string  $alt     The info to be inserted in the alt tag
787  */
788 function print_checkbox ($name, $value, $checked = true, $label = '', $alt = '', $script='',$return=false) {
790     static $idcounter = 0;
792     if (!$name) {
793         $name = 'unnamed';
794     }
796     if ($alt) {
797         $alt = strip_tags($alt);
798     } else {
799         $alt = 'checkbox';
800     }
802     if ($checked) {
803         $strchecked = ' checked="checked"';
804     } else {
805         $strchecked = '';
806     }
808     $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
809     $output  = '<span class="checkbox '.$name."\">";
810     $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="checkbox" value="'.$value.'" alt="'.$alt.'"'.$strchecked.' '.((!empty($script)) ? ' onClick="'.$script.'" ' : '').' />';
811     if(!empty($label)) {
812         $output .= ' <label for="'.$htmlid.'">'.$label.'</label>';
813     }
814     $output .= '</span>'."\n";
816     if (empty($return)) {
817         echo $output;
818     } else {
819         return $output;
820     }
824 /** Display an standard html text field with an optional label
825  *
826  * @param string  $name    The name of the text field
827  * @param string  $value   The value of the text field
828  * @param string  $label   The label to be showed near the text field
829  * @param string  $alt     The info to be inserted in the alt tag
830  */
831 function print_textfield ($name, $value, $alt = '',$size=50,$maxlength=0, $return=false) {
833     static $idcounter = 0;
835     if (empty($name)) {
836         $name = 'unnamed';
837     }
839     if (empty($alt)) {
840         $alt = 'textfield';
841     }
843     if (!empty($maxlength)) {
844         $maxlength = ' maxlength="'.$maxlength.'" ';
845     }
847     $htmlid = 'auto-tf'.sprintf('%04d', ++$idcounter);
848     $output  = '<span class="textfield '.$name."\">";
849     $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="text" value="'.$value.'" size="'.$size.'" '.$maxlength.' alt="'.$alt.'" />';
851     $output .= '</span>'."\n";
853     if (empty($return)) {
854         echo $output;
855     } else {
856         return $output;
857     }
862 /**
863  * Implements a complete little popup form
864  *
865  * @uses $CFG
866  * @param string $common  The URL up to the point of the variable that changes
867  * @param array $options  Alist of value-label pairs for the popup list
868  * @param string $formid Id must be unique on the page (originaly $formname)
869  * @param string $selected The option that is already selected
870  * @param string $nothing The label for the "no choice" option
871  * @param string $help The name of a help page if help is required
872  * @param string $helptext The name of the label for the help button
873  * @param boolean $return Indicates whether the function should return the text
874  *         as a string or echo it directly to the page being rendered
875  * @param string $targetwindow The name of the target page to open the linked page in.
876  * @return string If $return is true then the entire form is returned as a string.
877  * @todo Finish documenting this function<br>
878  */
879 function popup_form($common, $options, $formid, $selected='', $nothing='choose', $help='', $helptext='', $return=false, $targetwindow='self', $selectlabel='') {
881     global $CFG;
882     static $go, $choose;   /// Locally cached, in case there's lots on a page
884     if (empty($options)) {
885         return '';
886     }
888     if (!isset($go)) {
889         $go = get_string('go');
890     }
892     if ($nothing == 'choose') {
893         if (!isset($choose)) {
894             $choose = get_string('choose');
895         }
896         $nothing = $choose.'...';
897     }
899     // changed reference to document.getElementById('id_abc') instead of document.abc
900     // MDL-7861
901     $output = '<form action="'.$CFG->wwwroot.'/course/jumpto.php"'.
902                         ' method="get" '.
903                          $CFG->frametarget.
904                         ' id="'.$formid.'"'.
905                         ' class="popupform">';
906     if ($help) {
907         $button = helpbutton($help, $helptext, 'moodle', true, false, '', true);
908     } else {
909         $button = '';
910     }
912     if ($selectlabel) {
913         $selectlabel = '<label for="'.$formid.'_jump">'.$selectlabel.'</label>';
914     }
916     $output .= '<fieldset class="invisiblefieldset">'.$selectlabel.$button.'<select id="'.$formid.'_jump" name="jump" onchange="'.$targetwindow.'.location=document.getElementById(\''.$formid.'\').jump.options[document.getElementById(\''.$formid.'\').jump.selectedIndex].value;">'."\n";
918     if ($nothing != '') {
919         $output .= "   <option value=\"javascript:void(0)\">$nothing</option>\n";
920     }
922     $inoptgroup = false;
924     foreach ($options as $value => $label) {
926         if ($label == '--') { /// we are ending previous optgroup
927             /// Check to see if we already have a valid open optgroup
928             /// XHTML demands that there be at least 1 option within an optgroup
929             if ($inoptgroup and (count($optgr) > 1) ) {
930                 $output .= implode('', $optgr);
931                 $output .= '   </optgroup>';
932             }
933             $optgr = array();
934             $inoptgroup = false;
935             continue;
936         } else if (substr($label,0,2) == '--') { /// we are starting a new optgroup
938             /// Check to see if we already have a valid open optgroup
939             /// XHTML demands that there be at least 1 option within an optgroup
940             if ($inoptgroup and (count($optgr) > 1) ) {
941                 $output .= implode('', $optgr);
942                 $output .= '   </optgroup>';
943             }
945             unset($optgr);
946             $optgr = array();
948             $optgr[]  = '   <optgroup label="'. substr($label,2) .'">';   // Plain labels
950             $inoptgroup = true; /// everything following will be in an optgroup
951             continue;
953         } else {
954            if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()]))
955             {
956                 $url=sid_process_url( $common . $value );
957             } else
958             {
959                 $url=$common . $value;
960             }
961             $optstr = '   <option value="' . $url . '"';
963             if ($value == $selected) {
964                 $optstr .= ' selected="selected"';
965             }
967             if ($label) {
968                 $optstr .= '>'. $label .'</option>' . "\n";
969             } else {
970                 $optstr .= '>'. $value .'</option>' . "\n";
971             }
973             if ($inoptgroup) {
974                 $optgr[] = $optstr;
975             } else {
976                 $output .= $optstr;
977             }
978         }
980     }
982     /// catch the final group if not closed
983     if ($inoptgroup and count($optgr) > 1) {
984         $output .= implode('', $optgr);
985         $output .= '    </optgroup>';
986     }
988     $output .= '</select>';
989     $output .= '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
990     $output .= '<div id="noscript'.$formid.'" style="display: inline;">';
991     $output .= '<input type="submit" value="'.$go.'" /></div>';
992     $output .= '<script type="text/javascript">'.
993                "\n//<![CDATA[\n".
994                'document.getElementById("noscript'.$formid.'").style.display = "none";'.
995                "\n//]]>\n".'</script>';
996     $output .= '</fieldset>';
997     $output .= '</form>';
999     if ($return) {
1000         return $output;
1001     } else {
1002         echo $output;
1003     }
1007 /**
1008  * Prints some red text
1009  *
1010  * @param string $error The text to be displayed in red
1011  */
1012 function formerr($error) {
1014     if (!empty($error)) {
1015         echo '<span class="error">'. $error .'</span>';
1016     }
1019 /**
1020  * Validates an email to make sure it makes sense.
1021  *
1022  * @param string $address The email address to validate.
1023  * @return boolean
1024  */
1025 function validate_email($address) {
1027     return (ereg('^[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+'.
1028                  '(\.[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+)*'.
1029                   '@'.
1030                   '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
1031                   '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$',
1032                   $address));
1035 /**
1036  * Extracts file argument either from file parameter or PATH_INFO
1037  *
1038  * @param string $scriptname name of the calling script
1039  * @return string file path (only safe characters)
1040  */
1041 function get_file_argument($scriptname) {
1042     global $_SERVER;
1044     $relativepath = FALSE;
1046     // first try normal parameter (compatible method == no relative links!)
1047     $relativepath = optional_param('file', FALSE, PARAM_PATH);
1048     if ($relativepath === '/testslasharguments') {
1049         echo 'test -1      : Incorrect use - try "file.php/testslasharguments" instead'; //indicate fopen/fread works for health center
1050         die;
1051     }
1053     // then try extract file from PATH_INFO (slasharguments method)
1054     if (!$relativepath and !empty($_SERVER['PATH_INFO'])) {
1055         $path_info = $_SERVER['PATH_INFO'];
1056         // check that PATH_INFO works == must not contain the script name
1057         if (!strpos($path_info, $scriptname)) {
1058             $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1059             if ($relativepath === '/testslasharguments') {
1060                 echo 'test 1      : Slasharguments test passed. Server confguration is compatible with file.php/1/pic.jpg slashargument setting.'; //indicate ok for health center
1061                 die;
1062             }
1063         }
1064     }
1066     // now if both fail try the old way
1067     // (for compatibility with misconfigured or older buggy php implementations)
1068     if (!$relativepath) {
1069         $arr = explode($scriptname, me());
1070         if (!empty($arr[1])) {
1071             $path_info = strip_querystring($arr[1]);
1072             $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1073             if ($relativepath === '/testslasharguments') {
1074                 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
1075                 die;
1076             }
1077         }
1078     }
1080     return $relativepath;
1083 /**
1084  * Searches the current environment variables for some slash arguments
1085  *
1086  * @param string $file ?
1087  * @todo Finish documenting this function
1088  */
1089 function get_slash_arguments($file='file.php') {
1091     if (!$string = me()) {
1092         return false;
1093     }
1095     $pathinfo = explode($file, $string);
1097     if (!empty($pathinfo[1])) {
1098         return addslashes($pathinfo[1]);
1099     } else {
1100         return false;
1101     }
1104 /**
1105  * Extracts arguments from "/foo/bar/something"
1106  * eg http://mysite.com/script.php/foo/bar/something
1107  *
1108  * @param string $string ?
1109  * @param int $i ?
1110  * @return array|string
1111  * @todo Finish documenting this function
1112  */
1113 function parse_slash_arguments($string, $i=0) {
1115     if (detect_munged_arguments($string)) {
1116         return false;
1117     }
1118     $args = explode('/', $string);
1120     if ($i) {     // return just the required argument
1121         return $args[$i];
1123     } else {      // return the whole array
1124         array_shift($args);  // get rid of the empty first one
1125         return $args;
1126     }
1129 /**
1130  * Just returns an array of text formats suitable for a popup menu
1131  *
1132  * @uses FORMAT_MOODLE
1133  * @uses FORMAT_HTML
1134  * @uses FORMAT_PLAIN
1135  * @uses FORMAT_MARKDOWN
1136  * @return array
1137  */
1138 function format_text_menu() {
1140     return array (FORMAT_MOODLE => get_string('formattext'),
1141                   FORMAT_HTML   => get_string('formathtml'),
1142                   FORMAT_PLAIN  => get_string('formatplain'),
1143                   FORMAT_MARKDOWN  => get_string('formatmarkdown'));
1146 /**
1147  * Given text in a variety of format codings, this function returns
1148  * the text as safe HTML.
1149  *
1150  * @uses $CFG
1151  * @uses FORMAT_MOODLE
1152  * @uses FORMAT_HTML
1153  * @uses FORMAT_PLAIN
1154  * @uses FORMAT_WIKI
1155  * @uses FORMAT_MARKDOWN
1156  * @param string $text The text to be formatted. This is raw text originally from user input.
1157  * @param int $format Identifier of the text format to be used
1158  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1159  * @param  array $options ?
1160  * @param int $courseid ?
1161  * @return string
1162  * @todo Finish documenting this function
1163  */
1164 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) {
1166     global $CFG, $COURSE;
1168     if ($text === '') {
1169         return ''; // no need to do any filters and cleaning
1170     }
1172     if (!isset($options->trusttext)) {
1173         $options->trusttext = false;
1174     }
1176     if (!isset($options->noclean)) {
1177         $options->noclean=false;
1178     }
1179     if (!isset($options->nocache)) {
1180         $options->nocache=false;
1181     }
1182     if (!isset($options->smiley)) {
1183         $options->smiley=true;
1184     }
1185     if (!isset($options->filter)) {
1186         $options->filter=true;
1187     }
1188     if (!isset($options->para)) {
1189         $options->para=true;
1190     }
1191     if (!isset($options->newlines)) {
1192         $options->newlines=true;
1193     }
1195     if (empty($courseid)) {
1196         $courseid = $COURSE->id;
1197     }
1199     if (!empty($CFG->cachetext) and empty($options->nocache)) {
1200         $time = time() - $CFG->cachetext;
1201         $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);
1202         if ($oldcacheitem = get_record_sql('SELECT * FROM '.$CFG->prefix.'cache_text WHERE md5key = \''.$md5key.'\'', true)) {
1203             if ($oldcacheitem->timemodified >= $time) {
1204                 return $oldcacheitem->formattedtext;
1205             }
1206         }
1207     }
1209     // trusttext overrides the noclean option!
1210     if ($options->trusttext) {
1211         if (trusttext_present($text)) {
1212             $text = trusttext_strip($text);
1213             if (!empty($CFG->enabletrusttext)) {
1214                 $options->noclean = true;
1215             } else {
1216                 $options->noclean = false;
1217             }
1218         } else {
1219             $options->noclean = false;
1220         }
1221     } else if (!debugging('', DEBUG_DEVELOPER)) {
1222         // strip any forgotten trusttext in non-developer mode
1223         // do not forget to disable text cache when debugging trusttext!!
1224         $text = trusttext_strip($text);
1225     }
1227     $CFG->currenttextiscacheable = true;   // Default status - can be changed by any filter
1229     switch ($format) {
1230         case FORMAT_HTML:
1231             if ($options->smiley) {
1232                 replace_smilies($text);
1233             }
1234             if (!$options->noclean) {
1235                 $text = clean_text($text, FORMAT_HTML);
1236             }
1237             if ($options->filter) {
1238                 $text = filter_text($text, $courseid);
1239             }
1240             break;
1242         case FORMAT_PLAIN:
1243             $text = s($text); // cleans dangerous JS
1244             $text = rebuildnolinktag($text);
1245             $text = str_replace('  ', '&nbsp; ', $text);
1246             $text = nl2br($text);
1247             break;
1249         case FORMAT_WIKI:
1250             // this format is deprecated
1251             $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle.  You should not be seeing
1252                      this message as all texts should have been converted to Markdown format instead.
1253                      Please post a bug report to http://moodle.org/bugs with information about where you
1254                      saw this message.</p>'.s($text);
1255             break;
1257         case FORMAT_MARKDOWN:
1258             $text = markdown_to_html($text);
1259             if ($options->smiley) {
1260                 replace_smilies($text);
1261             }
1262             if (!$options->noclean) {
1263                 $text = clean_text($text, FORMAT_HTML);
1264             }
1266             if ($options->filter) {
1267                 $text = filter_text($text, $courseid);
1268             }
1269             break;
1271         default:  // FORMAT_MOODLE or anything else
1272             $text = text_to_html($text, $options->smiley, $options->para, $options->newlines);
1273             if (!$options->noclean) {
1274                 $text = clean_text($text, FORMAT_HTML);
1275             }
1277             if ($options->filter) {
1278                 $text = filter_text($text, $courseid);
1279             }
1280             break;
1281     }
1283     if (empty($options->nocache) and !empty($CFG->cachetext) and $CFG->currenttextiscacheable) {
1284         $newcacheitem = new object();
1285         $newcacheitem->md5key = $md5key;
1286         $newcacheitem->formattedtext = addslashes($text);
1287         $newcacheitem->timemodified = time();
1288         if ($oldcacheitem) {                               // See bug 4677 for discussion
1289             $newcacheitem->id = $oldcacheitem->id;
1290             @update_record('cache_text', $newcacheitem);   // Update existing record in the cache table
1291                                                            // It's unlikely that the cron cache cleaner could have
1292                                                            // deleted this entry in the meantime, as it allows
1293                                                            // some extra time to cover these cases.
1294         } else {
1295             @insert_record('cache_text', $newcacheitem);   // Insert a new record in the cache table
1296                                                            // Again, it's possible that another user has caused this
1297                                                            // record to be created already in the time that it took
1298                                                            // to traverse this function.  That's OK too, as the
1299                                                            // call above handles duplicate entries, and eventually
1300                                                            // the cron cleaner will delete them.
1301         }
1302     }
1304     return $text;
1307 /** Converts the text format from the value to the 'internal'
1308  *  name or vice versa. $key can either be the value or the name
1309  *  and you get the other back.
1310  *
1311  *  @param mixed int 0-4 or string one of 'moodle','html','plain','markdown'
1312  *  @return mixed as above but the other way around!
1313  */
1314 function text_format_name( $key ) {
1315   $lookup = array();
1316   $lookup[FORMAT_MOODLE] = 'moodle';
1317   $lookup[FORMAT_HTML] = 'html';
1318   $lookup[FORMAT_PLAIN] = 'plain';
1319   $lookup[FORMAT_MARKDOWN] = 'markdown';
1320   $value = "error";
1321   if (!is_numeric($key)) {
1322     $key = strtolower( $key );
1323     $value = array_search( $key, $lookup );
1324   }
1325   else {
1326     if (isset( $lookup[$key] )) {
1327       $value =  $lookup[ $key ];
1328     }
1329   }
1330   return $value;
1333 /** Given a simple string, this function returns the string
1334  *  processed by enabled filters if $CFG->filterall is enabled
1335  *
1336  *  @param string  $string     The string to be filtered.
1337  *  @param boolean $striplinks To strip any link in the result text.
1338  *  @param int     $courseid   Current course as filters can, potentially, use it
1339  *  @return string
1340  */
1341 function format_string ($string, $striplinks = false, $courseid=NULL ) {
1343     global $CFG, $COURSE;
1345     //We'll use a in-memory cache here to speed up repeated strings
1346     static $strcache;
1348     //Calculate md5
1349     $md5 = md5($string.'<+>'.$striplinks);
1351     //Fetch from cache if possible
1352     if(isset($strcache[$md5])) {
1353         return $strcache[$md5];
1354     }
1356     if (empty($courseid)) {
1357         $courseid = $COURSE->id;       // (copied from format_text)
1358     }
1360     if (!empty($CFG->filterall)) {
1361         $string = filter_text($string, $courseid);
1362     }
1364     if ($striplinks) {  //strip links in string
1365         $string = preg_replace('/(<a[^>]+?>)(.+?)(<\/a>)/is','$2',$string);
1366     }
1368     //Store to cache
1369     $strcache[$md5] = $string;
1371     return $string;
1374 /**
1375  * Given text in a variety of format codings, this function returns
1376  * the text as plain text suitable for plain email.
1377  *
1378  * @uses FORMAT_MOODLE
1379  * @uses FORMAT_HTML
1380  * @uses FORMAT_PLAIN
1381  * @uses FORMAT_WIKI
1382  * @uses FORMAT_MARKDOWN
1383  * @param string $text The text to be formatted. This is raw text originally from user input.
1384  * @param int $format Identifier of the text format to be used
1385  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1386  * @return string
1387  */
1388 function format_text_email($text, $format) {
1390     switch ($format) {
1392         case FORMAT_PLAIN:
1393             return $text;
1394             break;
1396         case FORMAT_WIKI:
1397             $text = wiki_to_html($text);
1398         /// This expression turns links into something nice in a text format. (Russell Jungwirth)
1399         /// From: http://php.net/manual/en/function.eregi-replace.php and simplified
1400             $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1401             return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1402             break;
1404         case FORMAT_HTML:
1405             return html_to_text($text);
1406             break;
1408         case FORMAT_MOODLE:
1409         case FORMAT_MARKDOWN:
1410         default:
1411             $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1412             return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1413             break;
1414     }
1417 /**
1418  * Given some text in HTML format, this function will pass it
1419  * through any filters that have been defined in $CFG->textfilterx
1420  * The variable defines a filepath to a file containing the
1421  * filter function.  The file must contain a variable called
1422  * $textfilter_function which contains the name of the function
1423  * with $courseid and $text parameters
1424  *
1425  * @param string $text The text to be passed through format filters
1426  * @param int $courseid ?
1427  * @return string
1428  * @todo Finish documenting this function
1429  */
1430 function filter_text($text, $courseid=NULL) {
1431     global $CFG, $COURSE;
1433     if (empty($courseid)) {
1434         $courseid = $COURSE->id;       // (copied from format_text)
1435     }
1437     require_once($CFG->libdir.'/filterlib.php');
1438     if (!empty($CFG->textfilters)) {
1439         $textfilters = explode(',', $CFG->textfilters);
1440         foreach ($textfilters as $textfilter) {
1441             if (is_readable($CFG->dirroot .'/'. $textfilter .'/filter.php')) {
1442                 include_once($CFG->dirroot .'/'. $textfilter .'/filter.php');
1443                 $functionname = basename($textfilter).'_filter';
1444                 if (function_exists($functionname)) {
1445                     $text = $functionname($courseid, $text);
1446                 }
1447             }
1448         }
1449     }
1451     /// <nolink> tags removed for XHTML compatibility
1452     $text = str_replace('<nolink>', '', $text);
1453     $text = str_replace('</nolink>', '', $text);
1455     return $text;
1458 /**
1459  * Is the text marked as trusted?
1460  *
1461  * @param string $text text to be searched for TRUSTTEXT marker
1462  * @return boolean
1463  */
1464 function trusttext_present($text) {
1465     if (strpos($text, TRUSTTEXT) !== FALSE) {
1466         return true;
1467     } else {
1468         return false;
1469     }
1472 /**
1473  * This funtion MUST be called before the cleaning or any other
1474  * function that modifies the data! We do not know the origin of trusttext
1475  * in database, if it gets there in tweaked form we must not convert it
1476  * to supported form!!!
1477  *
1478  * Please be carefull not to use stripslashes on data from database
1479  * or twice stripslashes when processing data recieved from user.
1480  *
1481  * @param string $text text that may contain TRUSTTEXT marker
1482  * @return text without any TRUSTTEXT marker
1483  */
1484 function trusttext_strip($text) {
1485     global $CFG;
1487     while (true) { //removing nested TRUSTTEXT
1488         $orig = $text;
1489         $text = str_replace(TRUSTTEXT, '', $text);
1490         if (strcmp($orig, $text) === 0) {
1491             return $text;
1492         }
1493     }
1496 /**
1497  * Mark text as trusted, such text may contain any HTML tags because the
1498  * normal text cleaning will be bypassed.
1499  * Please make sure that the text comes from trusted user before storing
1500  * it into database!
1501  */
1502 function trusttext_mark($text) {
1503     global $CFG;
1504     if (!empty($CFG->enabletrusttext) and (strpos($text, TRUSTTEXT) === FALSE)) {
1505         return TRUSTTEXT.$text;
1506     } else {
1507         return $text;
1508     }
1510 function trusttext_after_edit(&$text, $context) {
1511     if (has_capability('moodle/site:trustcontent', $context)) {
1512         $text = trusttext_strip($text);
1513         $text = trusttext_mark($text);
1514     } else {
1515         $text = trusttext_strip($text);
1516     }
1519 function trusttext_prepare_edit(&$text, &$format, $usehtmleditor, $context) {
1520     global $CFG;
1522     $options = new object();
1523     $options->smiley = false;
1524     $options->filter = false;
1525     if (!empty($CFG->enabletrusttext)
1526          and has_capability('moodle/site:trustcontent', $context)
1527          and trusttext_present($text)) {
1528         $options->noclean = true;
1529     } else {
1530         $options->noclean = false;
1531     }
1532     $text = trusttext_strip($text);
1533     if ($usehtmleditor) {
1534         $text = format_text($text, $format, $options);
1535         $format = FORMAT_HTML;
1536     } else if (!$options->noclean){
1537         $text = clean_text($text, $format);
1538     }
1541 /**
1542  * Given raw text (eg typed in by a user), this function cleans it up
1543  * and removes any nasty tags that could mess up Moodle pages.
1544  *
1545  * @uses FORMAT_MOODLE
1546  * @uses FORMAT_PLAIN
1547  * @uses ALLOWED_TAGS
1548  * @param string $text The text to be cleaned
1549  * @param int $format Identifier of the text format to be used
1550  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1551  * @return string The cleaned up text
1552  */
1553 function clean_text($text, $format=FORMAT_MOODLE) {
1555     global $ALLOWED_TAGS;
1557     switch ($format) {
1558         case FORMAT_PLAIN:
1559             return $text;
1561         default:
1563         /// Fix non standard entity notations
1564             $text = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $text);
1565             $text = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $text);
1567         /// Remove tags that are not allowed
1568             $text = strip_tags($text, $ALLOWED_TAGS);
1570         /// Clean up embedded scripts and , using kses
1571             $text = cleanAttributes($text);
1573         /// Remove script events
1574             $text = eregi_replace("([^a-z])language([[:space:]]*)=", "\\1Xlanguage=", $text);
1575             $text = eregi_replace("([^a-z])on([a-z]+)([[:space:]]*)=", "\\1Xon\\2=", $text);
1577             return $text;
1578     }
1581 /**
1582  * This function takes a string and examines it for HTML tags.
1583  * If tags are detected it passes the string to a helper function {@link cleanAttributes2()}
1584  *  which checks for attributes and filters them for malicious content
1585  *         17/08/2004              ::          Eamon DOT Costello AT dcu DOT ie
1586  *
1587  * @param string $str The string to be examined for html tags
1588  * @return string
1589  */
1590 function cleanAttributes($str){
1591     $result = preg_replace_callback(
1592             '%(<[^>]*(>|$)|>)%m', #search for html tags
1593             "cleanAttributes2",
1594             $str
1595             );
1596     return  $result;
1599 /**
1600  * This function takes a string with an html tag and strips out any unallowed
1601  * protocols e.g. javascript:
1602  * It calls ancillary functions in kses which are prefixed by kses
1603 *        17/08/2004              ::          Eamon DOT Costello AT dcu DOT ie
1604  *
1605  * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st
1606  *              element the html to be cleared
1607  * @return string
1608  */
1609 function cleanAttributes2($htmlArray){
1611     global $CFG, $ALLOWED_PROTOCOLS;
1612     require_once($CFG->libdir .'/kses.php');
1614     $htmlTag = $htmlArray[1];
1615     if (substr($htmlTag, 0, 1) != '<') {
1616         return '&gt;';  //a single character ">" detected
1617     }
1618     if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) {
1619         return ''; // It's seriously malformed
1620     }
1621     $slash = trim($matches[1]); //trailing xhtml slash
1622     $elem = $matches[2];    //the element name
1623     $attrlist = $matches[3]; // the list of attributes as a string
1625     $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
1627     $attStr = '';
1628     foreach ($attrArray as $arreach) {
1629         $arreach['name'] = strtolower($arreach['name']);
1630         if ($arreach['name'] == 'style') {
1631             $value = $arreach['value'];
1632             while (true) {
1633                 $prevvalue = $value;
1634                 $value = kses_no_null($value);
1635                 $value = preg_replace("/\/\*.*\*\//Us", '', $value);
1636                 $value = kses_decode_entities($value);
1637                 $value = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $value);
1638                 $value = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $value);
1639                 if ($value === $prevvalue) {
1640                     $arreach['value'] = $value;
1641                     break;
1642                 }
1643             }
1644             $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']);
1645             $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']);
1646         } else if ($arreach['name'] == 'href') {
1647             //Adobe Acrobat Reader XSS protection
1648             $arreach['value'] = preg_replace('/(\.(pdf|fdf|xfdf|xdp|xfd))[^a-z0-9_\.\-].*$/i', '$1', $arreach['value']);
1649         }
1650         $attStr .=  ' '.$arreach['name'].'="'.$arreach['value'].'"';
1651     }
1653     $xhtml_slash = '';
1654     if (preg_match('%/\s*$%', $attrlist)) {
1655         $xhtml_slash = ' /';
1656     }
1657     return '<'. $slash . $elem . $attStr . $xhtml_slash .'>';
1660 /**
1661  * Replaces all known smileys in the text with image equivalents
1662  *
1663  * @uses $CFG
1664  * @param string $text Passed by reference. The string to search for smily strings.
1665  * @return string
1666  */
1667 function replace_smilies(&$text) {
1668 ///
1669     global $CFG;
1671 /// this builds the mapping array only once
1672     static $runonce = false;
1673     static $e = array();
1674     static $img = array();
1675     static $emoticons = array(
1676         ':-)'  => 'smiley',
1677         ':)'   => 'smiley',
1678         ':-D'  => 'biggrin',
1679         ';-)'  => 'wink',
1680         ':-/'  => 'mixed',
1681         'V-.'  => 'thoughtful',
1682         ':-P'  => 'tongueout',
1683         'B-)'  => 'cool',
1684         '^-)'  => 'approve',
1685         '8-)'  => 'wideeyes',
1686         ':o)'  => 'clown',
1687         ':-('  => 'sad',
1688         ':('   => 'sad',
1689         '8-.'  => 'shy',
1690         ':-I'  => 'blush',
1691         ':-X'  => 'kiss',
1692         '8-o'  => 'surprise',
1693         'P-|'  => 'blackeye',
1694         '8-['  => 'angry',
1695         'xx-P' => 'dead',
1696         '|-.'  => 'sleepy',
1697         '}-]'  => 'evil',
1698         '(h)'  => 'heart',
1699         '(heart)'  => 'heart',
1700         '(y)'  => 'yes',
1701         '(n)'  => 'no',
1702         '(martin)'  => 'martin',
1703         '( )'  => 'egg'
1704         );
1706     if ($runonce == false) {  /// After the first time this is not run again
1707         foreach ($emoticons as $emoticon => $image){
1708             $alttext = get_string($image, 'pix');
1710             $e[] = $emoticon;
1711             $img[] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $CFG->pixpath .'/s/'. $image .'.gif" />';
1712         }
1713         $runonce = true;
1714     }
1716     // Exclude from transformations all the code inside <script> tags
1717     // Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
1718     // Based on code from glossary fiter by Williams Castillo.
1719     //       - Eloy
1721     // Detect all the <script> zones to take out
1722     $excludes = array();
1723     preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
1725     // Take out all the <script> zones from text
1726     foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
1727         $excludes['<+'.$key.'+>'] = $value;
1728     }
1729     if ($excludes) {
1730         $text = str_replace($excludes,array_keys($excludes),$text);
1731     }
1733 /// this is the meat of the code - this is run every time
1734     $text = str_replace($e, $img, $text);
1736     // Recover all the <script> zones to text
1737     if ($excludes) {
1738         $text = str_replace(array_keys($excludes),$excludes,$text);
1739     }
1742 /**
1743  * Given plain text, makes it into HTML as nicely as possible.
1744  * May contain HTML tags already
1745  *
1746  * @uses $CFG
1747  * @param string $text The string to convert.
1748  * @param boolean $smiley Convert any smiley characters to smiley images?
1749  * @param boolean $para If true then the returned string will be wrapped in paragraph tags
1750  * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
1751  * @return string
1752  */
1754 function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
1755 ///
1757     global $CFG;
1759 /// Remove any whitespace that may be between HTML tags
1760     $text = eregi_replace(">([[:space:]]+)<", "><", $text);
1762 /// Remove any returns that precede or follow HTML tags
1763     $text = eregi_replace("([\n\r])<", " <", $text);
1764     $text = eregi_replace(">([\n\r])", "> ", $text);
1766     convert_urls_into_links($text);
1768 /// Make returns into HTML newlines.
1769     if ($newlines) {
1770         $text = nl2br($text);
1771     }
1773 /// Turn smileys into images.
1774     if ($smiley) {
1775         replace_smilies($text);
1776     }
1778 /// Wrap the whole thing in a paragraph tag if required
1779     if ($para) {
1780         return '<p>'.$text.'</p>';
1781     } else {
1782         return $text;
1783     }
1786 /**
1787  * Given Markdown formatted text, make it into XHTML using external function
1788  *
1789  * @uses $CFG
1790  * @param string $text The markdown formatted text to be converted.
1791  * @return string Converted text
1792  */
1793 function markdown_to_html($text) {
1794     global $CFG;
1796     require_once($CFG->libdir .'/markdown.php');
1798     return Markdown($text);
1801 /**
1802  * Given HTML text, make it into plain text using external function
1803  *
1804  * @uses $CFG
1805  * @param string $html The text to be converted.
1806  * @return string
1807  */
1808 function html_to_text($html) {
1810     global $CFG;
1812     require_once($CFG->libdir .'/html2text.php');
1814     return html2text($html);
1817 /**
1818  * Given some text this function converts any URLs it finds into HTML links
1819  *
1820  * @param string $text Passed in by reference. The string to be searched for urls.
1821  */
1822 function convert_urls_into_links(&$text) {
1823 /// Make lone URLs into links.   eg http://moodle.com/
1824     $text = eregi_replace("([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])",
1825                           "\\1<a href=\"\\2://\\3\\4\" target=\"_blank\">\\2://\\3\\4</a>", $text);
1827 /// eg www.moodle.com
1828     $text = eregi_replace("([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])",
1829                           "\\1<a href=\"http://www.\\2\\3\" target=\"_blank\">www.\\2\\3</a>", $text);
1832 /**
1833  * This function will highlight search words in a given string
1834  * It cares about HTML and will not ruin links.  It's best to use
1835  * this function after performing any conversions to HTML.
1836  * Function found here: http://forums.devshed.com/t67822/scdaa2d1c3d4bacb4671d075ad41f0854.html
1837  *
1838  * @param string $needle The string to search for
1839  * @param string $haystack The string to search for $needle in
1840  * @param int $case ?
1841  * @return string
1842  * @todo Finish documenting this function
1843  */
1844 function highlight($needle, $haystack, $case=0,
1845                     $left_string='<span class="highlight">', $right_string='</span>') {
1846     if (empty($needle)) {
1847         return $haystack;
1848     }
1850     //$list_of_words = eregi_replace("[^-a-zA-Z0-9&.']", " ", $needle);  // bug 3101
1851     $list_of_words = $needle;
1852     $list_array = explode(' ', $list_of_words);
1853     for ($i=0; $i<sizeof($list_array); $i++) {
1854         if (strlen($list_array[$i]) == 1) {
1855             $list_array[$i] = '';
1856         }
1857     }
1858     $list_of_words = implode(' ', $list_array);
1859     $list_of_words_cp = $list_of_words;
1860     $final = array();
1861     preg_match_all('/<(.+?)>/is',$haystack,$list_of_words);
1863     foreach (array_unique($list_of_words[0]) as $key=>$value) {
1864         $final['<|'.$key.'|>'] = $value;
1865     }
1867     $haystack = str_replace($final,array_keys($final),$haystack);
1868     $list_of_words_cp = eregi_replace(' +', '|', $list_of_words_cp);
1870     if ($list_of_words_cp{0}=='|') {
1871         $list_of_words_cp{0} = '';
1872     }
1873     if ($list_of_words_cp{strlen($list_of_words_cp)-1}=='|') {
1874         $list_of_words_cp{strlen($list_of_words_cp)-1}='';
1875     }
1877     $list_of_words_cp = trim($list_of_words_cp);
1879     if ($list_of_words_cp) {
1881       $list_of_words_cp = "(". $list_of_words_cp .")";
1883       if (!$case){
1884         $haystack = eregi_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1885       } else {
1886         $haystack = ereg_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1887       }
1888     }
1889     $haystack = str_replace(array_keys($final),$final,$haystack);
1891     return $haystack;
1894 /**
1895  * This function will highlight instances of $needle in $haystack
1896  * It's faster that the above function and doesn't care about
1897  * HTML or anything.
1898  *
1899  * @param string $needle The string to search for
1900  * @param string $haystack The string to search for $needle in
1901  * @return string
1902  */
1903 function highlightfast($needle, $haystack) {
1905     $parts = explode(moodle_strtolower($needle), moodle_strtolower($haystack));
1907     $pos = 0;
1909     foreach ($parts as $key => $part) {
1910         $parts[$key] = substr($haystack, $pos, strlen($part));
1911         $pos += strlen($part);
1913         $parts[$key] .= '<span class="highlight">'.substr($haystack, $pos, strlen($needle)).'</span>';
1914         $pos += strlen($needle);
1915     }
1917     return (join('', $parts));
1921 /// STANDARD WEB PAGE PARTS ///////////////////////////////////////////////////
1923 /**
1924  * Print a standard header
1925  *
1926  * @uses $USER
1927  * @uses $CFG
1928  * @uses $SESSION
1929  * @param string $title Appears at the top of the window
1930  * @param string $heading Appears at the top of the page
1931  * @param string $navigation Premade navigation string (for use as breadcrumbs links)
1932  * @param string $focus Indicates form element to get cursor focus on load eg  inputform.password
1933  * @param string $meta Meta tags to be added to the header
1934  * @param boolean $cache Should this page be cacheable?
1935  * @param string $button HTML code for a button (usually for module editing)
1936  * @param string $menu HTML code for a popup menu
1937  * @param boolean $usexml use XML for this page
1938  * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
1939  * @param bool   $return If true, return the visible elements of the header instead of echoing them.
1940  */
1941 function print_header ($title='', $heading='', $navigation='', $focus='',
1942                        $meta='', $cache=true, $button='&nbsp;', $menu='',
1943                        $usexml=false, $bodytags='', $return=false) {
1945     global $USER, $CFG, $THEME, $SESSION, $ME, $SITE, $COURSE;
1947 /// This makes sure that the header is never repeated twice on a page
1948     if (defined('HEADER_PRINTED')) {
1949         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().');
1950         return;
1951     }
1952     define('HEADER_PRINTED', 'true');
1955 /// Add the required stylesheets
1956     $stylesheetshtml = '';
1957     foreach ($CFG->stylesheets as $stylesheet) {
1958         $stylesheetshtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
1959     }
1960     $meta = $stylesheetshtml.$meta;
1962         
1963 /// Add the meta page from the themes if any were requested
1965     $metapage = '';
1967     if (!isset($THEME->standardmetainclude) || $THEME->standardmetainclude) {
1968         ob_start();
1969         include_once($CFG->dirroot.'/theme/standard/meta.php');
1970         $metapage .= ob_get_contents();
1971         ob_end_clean();
1972     }
1974     if ($THEME->parent && (!isset($THEME->parentmetainclude) || $THEME->parentmetainclude)) {
1975         if (file_exists($CFG->dirroot.'/theme/'.$THEME->parent.'/meta.php')) {
1976             ob_start();
1977             include_once($CFG->dirroot.'/theme/'.$THEME->parent.'/meta.php');
1978             $metapage .= ob_get_contents();
1979             ob_end_clean();
1980         }
1981     }
1983     if (!isset($THEME->metainclude) || $THEME->metainclude) {
1984         if (file_exists($CFG->dirroot.'/theme/'.current_theme().'/meta.php')) {
1985             ob_start();
1986             include_once($CFG->dirroot.'/theme/'.current_theme().'/meta.php');
1987             $metapage .= ob_get_contents();
1988             ob_end_clean();
1989         }
1990     }
1992     $meta = $meta."\n".$metapage;
1996     if ($navigation == 'home') {
1997         $home = true;
1998         $navigation = '';
1999     } else {
2000         $home = false;
2001     }
2003 /// This is another ugly hack to make navigation elements available to print_footer later
2004     $THEME->title      = $title;
2005     $THEME->heading    = $heading;
2006     $THEME->navigation = $navigation;
2007     $THEME->button     = $button;
2008     $THEME->menu       = $menu;
2009     $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
2011     if ($button == '') {
2012         $button = '&nbsp;';
2013     }
2015     if (!$menu and $navigation) {
2016         if (empty($CFG->loginhttps)) {
2017             $wwwroot = $CFG->wwwroot;
2018         } else {
2019             $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
2020         }
2021         $menu = user_login_string($COURSE);
2022     }
2024     if (isset($SESSION->justloggedin)) {
2025         unset($SESSION->justloggedin);
2026         if (!empty($CFG->displayloginfailures)) {
2027             if (!empty($USER->username) and $USER->username != 'guest') {
2028                 if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
2029                     $menu .= '&nbsp;<font size="1">';
2030                     if (empty($count->accounts)) {
2031                         $menu .= get_string('failedloginattempts', '', $count);
2032                     } else {
2033                         $menu .= get_string('failedloginattemptsall', '', $count);
2034                     }
2035                     if (has_capability('moodle/site:viewreports', get_context_instance(CONTEXT_SYSTEM, SITEID))) {
2036                         $menu .= ' (<a href="'.$CFG->wwwroot.'/course/report/log/index.php'.
2037                                              '?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
2038                     }
2039                     $menu .= '</font>';
2040                 }
2041             }
2042         }
2043     }
2046     $meta = '<meta http-equiv="content-type" content="text/html; charset=utf-8" />'. "\n". $meta ."\n";
2047     if (!$usexml) {
2048         @header('Content-type: text/html; charset=utf-8');
2049     }
2051     if ( get_string('thisdirection') == 'rtl' ) {
2052         $direction = ' dir="rtl"';
2053     } else {
2054         $direction = ' dir="ltr"';
2055     }
2056     //Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag.
2057     $language = str_replace('_utf8','',$CFG->lang);
2058     $direction .= ' lang="'.$language.'" xml:lang="'.$language.'"';
2060     if ($cache) {  // Allow caching on "back" (but not on normal clicks)
2061         @header('Cache-Control: private, pre-check=0, post-check=0, max-age=0');
2062         @header('Pragma: no-cache');
2063         @header('Expires: ');
2064     } else {       // Do everything we can to always prevent clients and proxies caching
2065         @header('Cache-Control: no-store, no-cache, must-revalidate');
2066         @header('Cache-Control: post-check=0, pre-check=0', false);
2067         @header('Pragma: no-cache');
2068         @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
2069         @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
2071         $meta .= "\n<meta http-equiv=\"pragma\" content=\"no-cache\" />";
2072         $meta .= "\n<meta http-equiv=\"expires\" content=\"0\" />";
2073     }
2074     @header('Accept-Ranges: none');
2076     if (empty($usexml)) {
2077         $direction =  ' xmlns="http://www.w3.org/1999/xhtml"'. $direction;  // See debug_header
2078     } else {
2079         $currentlanguage = current_language();
2080         $mathplayer = preg_match("/MathPlayer/i", $_SERVER['HTTP_USER_AGENT']);
2081         if(!$mathplayer) {
2082             header('Content-Type: application/xhtml+xml');
2083         }
2084         echo '<?xml version="1.0" ?>'."\n";
2085         if (!empty($CFG->xml_stylesheets)) {
2086             $stylesheets = explode(';', $CFG->xml_stylesheets);
2087             foreach ($stylesheets as $stylesheet) {
2088                 echo '<?xml-stylesheet type="text/xsl" href="'. $CFG->wwwroot .'/'. $stylesheet .'" ?>' . "\n";
2089             }
2090         }
2091         echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1';
2092         if (!empty($CFG->xml_doctype_extra)) {
2093             echo ' plus '. $CFG->xml_doctype_extra;
2094         }
2095         echo '//' . strtoupper($currentlanguage) . '" "'. $CFG->xml_dtd .'">'."\n";
2096         $direction = " xmlns=\"http://www.w3.org/1999/xhtml\"
2097                        xmlns:math=\"http://www.w3.org/1998/Math/MathML\"
2098                        xmlns:xlink=\"http://www.w3.org/1999/xlink\"
2099                        $direction";
2100         if($mathplayer) {
2101             $meta .= '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n";
2102             $meta .= '<!--comment required to prevent this becoming an empty tag-->'."\n";
2103             $meta .= '</object>'."\n";
2104             $meta .= '<?import namespace="math" implementation="#mathplayer" ?>' . "\n";
2105         }
2106     }
2108     // Clean up the title
2110     $title = str_replace('"', '&quot;', $title);
2111     $title = strip_tags($title);
2113     // Create class and id for this page
2115     page_id_and_class($pageid, $pageclass);
2117     $pageclass .= ' course-'.$COURSE->id;
2119     if (($pageid != 'site-index') && ($pageid != 'course-view') && 
2120         ($pageid != 'admin-settings')) {
2121         $pageclass .= ' nocoursepage';
2122     }
2124     if (!isloggedin()) {
2125         $pageclass .= ' notloggedin';
2126     }
2128     if (!empty($USER->editing)) {
2129         $pageclass .= ' editing';
2130     }
2132     if (!empty($CFG->blocksdrag)) {
2133         $pageclass .= ' drag';
2134     }
2136     $bodytags .= ' class="'.$pageclass.'" id="'.$pageid.'"';
2138     ob_start();
2139     include($CFG->header);
2140     $output = ob_get_contents();
2141     ob_end_clean();
2143     $output = force_strict_header($output);
2145     if (!empty($CFG->messaging)) {
2146         $output .= message_popup_window();
2147     }
2149     if ($return) {
2150         return $output;
2151     } else {
2152         echo $output;
2153     }
2156 /**
2157  * Debugging aid: serve page as 'application/xhtml+xml' where possible,
2158  *     and substitute the XHTML strict document type.
2159  *     Note, requires the 'xmlns' fix in function print_header above.
2160  *     See:  http://tracker.moodle.org/browse/MDL-7883
2161  * TODO:
2162  */
2163 function force_strict_header($output) {
2164     global $CFG;
2165     $strict = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
2166     $xsl = '/lib/xhtml.xsl';
2168     if (!headers_sent() && debugging(NULL, DEBUG_DEVELOPER)) {   // In developer debugging, the browser will barf
2169         $ctype = 'Content-Type: ';
2170         $prolog= "<?xml version='1.0' encoding='utf-8'?>\n";
2172         if (isset($_SERVER['HTTP_ACCEPT'])
2173             && false !== strpos($_SERVER['HTTP_ACCEPT'], 'application/xhtml+xml')) {
2174             //|| false !== strpos($_SERVER['HTTP_USER_AGENT'], 'Safari') //Safari "Entity 'copy' not defined".
2175             // Firefox et al.
2176             $ctype .= 'application/xhtml+xml';
2177             $prolog .= "<!--\n  DEBUG: $ctype \n-->\n";
2179         } else if (file_exists($CFG->dirroot.$xsl)
2180             && preg_match('/MSIE.*Windows NT/', $_SERVER['HTTP_USER_AGENT'])) {
2181             // XSL hack for IE 5+ on Windows.
2182             //$www_xsl = preg_replace('/(http:\/\/.+?\/).*/', '', $CFG->wwwroot) .$xsl;
2183             $www_xsl = $CFG->wwwroot .$xsl;
2184             $ctype .= 'application/xml';
2185             $prolog .= "<?xml-stylesheet type='text/xsl' href='$www_xsl'?>\n";
2186             $prolog .= "<!--\n  DEBUG: $ctype \n-->\n";
2188         } else {
2189             //ELSE: Mac/IE, old/non-XML browsers.
2190             $ctype .= 'text/html';
2191             $prolog = '';
2192         }
2193         @header($ctype.'; charset=utf-8');
2194         $output = $prolog . $output;
2196         // Test parser error-handling.
2197         if (isset($_GET['error'])) {
2198             $output .= "__ TEST: XML well-formed error < __\n";
2199         }
2200     }
2202     $output = preg_replace('/(<!DOCTYPE.+?>)/s', $strict, $output);   // Always change the DOCTYPE to Strict 1.0
2204     return $output;
2209 /**
2210  * This version of print_header is simpler because the course name does not have to be
2211  * provided explicitly in the strings. It can be used on the site page as in courses
2212  * Eventually all print_header could be replaced by print_header_simple
2213  *
2214  * @param string $title Appears at the top of the window
2215  * @param string $heading Appears at the top of the page
2216  * @param string $navigation Premade navigation string (for use as breadcrumbs links)
2217  * @param string $focus Indicates form element to get cursor focus on load eg  inputform.password
2218  * @param string $meta Meta tags to be added to the header
2219  * @param boolean $cache Should this page be cacheable?
2220  * @param string $button HTML code for a button (usually for module editing)
2221  * @param string $menu HTML code for a popup menu
2222  * @param boolean $usexml use XML for this page
2223  * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
2224  * @param bool   $return If true, return the visible elements of the header instead of echoing them.
2225  */
2226 function print_header_simple($title='', $heading='', $navigation='', $focus='', $meta='',
2227                        $cache=true, $button='&nbsp;', $menu='', $usexml=false, $bodytags='', $return=false) {
2229     global $COURSE, $CFG;
2231     $shortname ='';
2232     if ($COURSE->id != SITEID) {
2233         $shortname = '<a href="'.$CFG->wwwroot.'/course/view.php?id='. $COURSE->id .'">'. $COURSE->shortname .'</a> ->';
2234     }
2236     $output = print_header($COURSE->shortname .': '. $title, $COURSE->fullname .' '. $heading, $shortname .' '. $navigation, $focus, $meta,
2237                            $cache, $button, $menu, $usexml, $bodytags, true);
2239     if ($return) {
2240         return $output;
2241     } else {
2242         echo $output;
2243     }
2247 /**
2248  * Can provide a course object to make the footer contain a link to
2249  * to the course home page, otherwise the link will go to the site home
2250  *
2251  * @uses $CFG
2252  * @uses $USER
2253  * @param course $course {@link $COURSE} object containing course information
2254  * @param ? $usercourse ?
2255  * @todo Finish documenting this function
2256  */
2257 function print_footer($course=NULL, $usercourse=NULL, $return=false) {
2258     global $USER, $CFG, $THEME, $COURSE;
2260 /// Course links
2261     if ($course) {
2262         if (is_string($course) && $course == 'none') {          // Don't print any links etc
2263             $homelink = '';
2264             $loggedinas = '';
2265             $home  = false;
2266         } else if (is_string($course) && $course == 'home') {   // special case for site home page - please do not remove
2267             $course = get_site();
2268             $homelink  = '<div class="sitelink">'.
2269                '<a title="moodle '. $CFG->release .' ('. $CFG->version .')" href="http://moodle.org/">'.
2270                '<br /><img style="width:100px;height:30px" src="pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
2271             $home  = true;
2272         } else {
2273             $homelink = '<div class="homelink"><a '.$CFG->frametarget.' href="'.$CFG->wwwroot.
2274                         '/course/view.php?id='.$course->id.'">'.$course->shortname.'</a></div>';
2275             $home  = false;
2276         }
2277     } else {
2278         $course = get_site();  // Set course as site course by default
2279         $homelink = '<div class="homelink"><a '.$CFG->frametarget.' href="'.$CFG->wwwroot.'/">'.get_string('home').'</a></div>';
2280         $home  = false;
2281     }
2283 /// Set up some other navigation links (passed from print_header by ugly hack)
2284     $menu        = isset($THEME->menu) ? str_replace('navmenu', 'navmenufooter', $THEME->menu) : '';
2285     $title       = isset($THEME->title) ? $THEME->title : '';
2286     $button      = isset($THEME->button) ? $THEME->button : '';
2287     $heading     = isset($THEME->heading) ? $THEME->heading : '';
2288     $navigation  = isset($THEME->navigation) ? $THEME->navigation : '';
2289     $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
2292 /// Set the user link if necessary
2293     if (!$usercourse and is_object($course)) {
2294         $usercourse = $course;
2295     }
2297     if (!isset($loggedinas)) {
2298         $loggedinas = user_login_string($usercourse, $USER);
2299     }
2301     if ($loggedinas == $menu) {
2302         $menu = '';
2303     }
2305 /// Provide some performance info if required
2306     $performanceinfo = '';
2307     if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
2308         $perf = get_performance_info();
2309         if (defined('MDL_PERFTOLOG')) {
2310             error_log("PERF: " . $perf['txt']);
2311         }
2312         if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) {
2313             $performanceinfo = $perf['html'];
2314         }
2315     }
2318 /// Include the actual footer file
2320     ob_start();
2321     include($CFG->footer);
2322     $output = ob_get_contents();
2323     ob_end_clean();
2325     if ($return) {
2326         return $output;
2327     } else {
2328         echo $output;
2329     }
2332 /**
2333  * Returns the name of the current theme
2334  *
2335  * @uses $CFG
2336  * @param $USER
2337  * @param $SESSION
2338  * @return string
2339  */
2340 function current_theme() {
2341     global $CFG, $USER, $SESSION, $COURSE;
2343     if (!empty($CFG->pagetheme)) {  // Page theme is for special page-only themes set by code
2344         return $CFG->pagetheme;
2346     } else if (!empty($CFG->allowcoursethemes) and !empty($COURSE->theme)) {  // Course themes override others
2347         return $COURSE->theme;
2349     } else if (!empty($SESSION->theme)) {    // Session theme can override other settings
2350         return $SESSION->theme;
2352     } else if (!empty($CFG->allowuserthemes) and !empty($USER->theme)) {    // User theme can override site theme
2353         return $USER->theme;
2355     } else {
2356         return $CFG->theme;
2357     }
2361 /**
2362  * This function is called by stylesheets to set up the header
2363  * approriately as well as the current path
2364  *
2365  * @uses $CFG
2366  * @param int $lastmodified ?
2367  * @param int $lifetime ?
2368  * @param string $thename ?
2369  */
2370 function style_sheet_setup($lastmodified=0, $lifetime=300, $themename='', $forceconfig='', $lang='') {
2372     global $CFG, $THEME;
2374     // Fix for IE6 caching - we don't want the filemtime('styles.php'), instead use now.
2375     $lastmodified = time();
2377     header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmodified) . ' GMT');
2378     header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
2379     header('Cache-Control: max-age='. $lifetime);
2380     header('Pragma: ');
2381     header('Content-type: text/css');  // Correct MIME type
2383     $DEFAULT_SHEET_LIST = array('styles_layout', 'styles_fonts', 'styles_color');
2385     if (empty($themename)) {
2386         $themename = current_theme();  // So we have something.  Normally not needed.
2387     } else {
2388         $themename = clean_param($themename, PARAM_SAFEDIR);
2389     }
2391     if (!empty($forceconfig)) {        // Page wants to use the config from this theme instead
2392         unset($THEME);
2393         include($CFG->themedir.'/'.$forceconfig.'/'.'config.php');
2394     }
2396 /// If this is the standard theme calling us, then find out what sheets we need
2398     if ($themename == 'standard') {
2399         if (!isset($THEME->standardsheets) or $THEME->standardsheets === true) { // Use all the sheets we have
2400             $THEME->sheets = $DEFAULT_SHEET_LIST;
2401         } else if (empty($THEME->standardsheets)) {                              // We can stop right now!
2402             echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2403             exit;
2404         } else {                                                                 // Use the provided subset only
2405             $THEME->sheets = $THEME->standardsheets;
2406         }
2408 /// If we are a parent theme, then check for parent definitions
2410     } else if (!empty($THEME->parent) && $themename == $THEME->parent) {
2411         if (!isset($THEME->parentsheets) or $THEME->parentsheets === true) {     // Use all the sheets we have
2412             $THEME->sheets = $DEFAULT_SHEET_LIST;
2413         } else if (empty($THEME->parentsheets)) {                                // We can stop right now!
2414             echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2415             exit;
2416         } else {                                                                 // Use the provided subset only
2417             $THEME->sheets = $THEME->parentsheets;
2418         }
2419     }
2421 /// Work out the last modified date for this theme
2423     foreach ($THEME->sheets as $sheet) {
2424         if (file_exists($CFG->themedir.'/'.$themename.'/'.$sheet.'.css')) {
2425             $sheetmodified = filemtime($CFG->themedir.'/'.$themename.'/'.$sheet.'.css');
2426             if ($sheetmodified > $lastmodified) {
2427                 $lastmodified = $sheetmodified;
2428             }
2429         }
2430     }
2433 /// Get a list of all the files we want to include
2434     $files = array();
2436     foreach ($THEME->sheets as $sheet) {
2437         $files[] = array($CFG->themedir, $themename.'/'.$sheet.'.css');
2438     }
2440     if ($themename == 'standard') {          // Add any standard styles included in any modules
2441         if (!empty($THEME->modsheets)) {     // Search for styles.php within activity modules
2442             if ($mods = get_list_of_plugins('mod')) {
2443                 foreach ($mods as $mod) {
2444                     if (file_exists($CFG->dirroot.'/mod/'.$mod.'/styles.php')) {
2445                         $files[] = array($CFG->dirroot, '/mod/'.$mod.'/styles.php');
2446                     }
2447                 }
2448             }
2449         }
2451         if (!empty($THEME->blocksheets)) {     // Search for styles.php within block modules
2452             if ($mods = get_list_of_plugins('blocks')) {
2453                 foreach ($mods as $mod) {
2454                     if (file_exists($CFG->dirroot.'/blocks/'.$mod.'/styles.php')) {
2455                         $files[] = array($CFG->dirroot, '/blocks/'.$mod.'/styles.php');
2456                     }
2457                 }
2458             }
2459         }
2461         if (!isset($THEME->courseformatsheets) || $THEME->courseformatsheets) { // Search for styles.php in course formats
2462             if ($mods = get_list_of_plugins('format','',$CFG->dirroot.'/course')) {
2463                 foreach ($mods as $mod) {
2464                     if (file_exists($CFG->dirroot.'/course/format/'.$mod.'/styles.php')) {
2465                         $files[] = array($CFG->dirroot, '/course/format/'.$mod.'/styles.php');
2466                     }
2467                 }
2468             }
2469         }
2471         if (!empty($THEME->langsheets)) {     // Search for styles.php within the current language
2472             if (file_exists($CFG->dirroot.'/lang/'.$lang.'/styles.php')) {
2473                 $files[] = array($CFG->dirroot, '/lang/'.$lang.'/styles.php');
2474             }
2475         }
2476     }
2479     if ($files) {
2480     /// Produce a list of all the files first
2481         echo '/**************************************'."\n";
2482         echo ' * THEME NAME: '.$themename."\n *\n";
2483         echo ' * Files included in this sheet:'."\n *\n";
2484         foreach ($files as $file) {
2485             echo ' *   '.$file[1]."\n";
2486         }
2487         echo ' **************************************/'."\n\n";
2490         /// check if csscobstants is set
2491         if (!empty($THEME->cssconstants)) {
2492             require_once("$CFG->libdir/cssconstants.php");
2493             /// Actually collect all the files in order.
2494             $css = '';
2495             foreach ($files as $file) {
2496                 $css .= '/***** '.$file[1].' start *****/'."\n\n";
2497                 $css .= file_get_contents($file[0].'/'.$file[1]);
2498                 $ccs .= '/***** '.$file[1].' end *****/'."\n\n";
2499             }
2500             /// replace css_constants with their values
2501             echo replace_cssconstants($css);
2502         } else {
2503         /// Actually output all the files in order.
2504             foreach ($files as $file) {
2505                 echo '/***** '.$file[1].' start *****/'."\n\n";
2506                 @include_once($file[0].'/'.$file[1]);
2507                 echo '/***** '.$file[1].' end *****/'."\n\n";
2508             }
2509         }
2510     }
2512     return $CFG->themewww.'/'.$themename;   // Only to help old themes (1.4 and earlier)
2516 function theme_setup($theme = '', $params=NULL) {
2517 /// Sets up global variables related to themes
2519     global $CFG, $THEME, $SESSION, $USER, $HTTPSPAGEREQUIRED;
2521     if (empty($theme)) {
2522         $theme = current_theme();
2523     }
2525 /// If the theme doesn't exist for some reason then revert to standardwhite
2526     if (!file_exists($CFG->themedir .'/'. $theme .'/config.php')) {
2527         $CFG->theme = $theme = 'standardwhite';
2528     }
2530 /// Load up the theme config
2531     $THEME = NULL;   // Just to be sure
2532     include($CFG->themedir .'/'. $theme .'/config.php');  // Main config for current theme
2534 /// Put together the parameters
2535     if (!$params) {
2536         $params = array();
2537     }
2538     if ($theme != $CFG->theme) {
2539         $params[] = 'forceconfig='.$theme;
2540     }
2542 /// Force language too if required
2543     if (!empty($THEME->langsheets)) {
2544         $params[] = 'lang='.current_language();
2545     }
2547 /// Convert params to string
2548     if ($params) {
2549         $paramstring = '?'.implode('&', $params);
2550     } else {
2551         $paramstring = '';
2552     }
2554 /// Set up image paths
2555     if (empty($THEME->custompix)) {    // Could be set in the above file
2556         $CFG->pixpath = $CFG->wwwroot .'/pix';
2557         $CFG->modpixpath = $CFG->wwwroot .'/mod';
2558     } else {
2559         $CFG->pixpath = $CFG->themewww .'/'. $theme .'/pix';
2560         $CFG->modpixpath = $CFG->themewww .'/'. $theme .'/pix/mod';
2561     }
2563 /// Header and footer paths
2564     $CFG->header = $CFG->themedir .'/'. $theme .'/header.html';
2565     $CFG->footer = $CFG->themedir .'/'. $theme .'/footer.html';
2567 /// Define stylesheet loading order
2568     $CFG->stylesheets = array();
2569     if ($theme != 'standard') {    /// The standard sheet is always loaded first
2570         $CFG->stylesheets[] = $CFG->themewww.'/standard/styles.php'.$paramstring;
2571     }
2572     if (!empty($THEME->parent)) {  /// Parent stylesheets are loaded next
2573         $CFG->stylesheets[] = $CFG->themewww.'/'.$THEME->parent.'/styles.php'.$paramstring;
2574     }
2575     $CFG->stylesheets[] = $CFG->themewww.'/'.$theme.'/styles.php'.$paramstring;
2577 /// We have to change some URLs in styles if we are in a $HTTPSPAGEREQUIRED page
2578     if (!empty($HTTPSPAGEREQUIRED)) {
2579         $CFG->themewww = str_replace('http:', 'https:', $CFG->themewww);
2580         $CFG->pixpath = str_replace('http:', 'https:', $CFG->pixpath);
2581         $CFG->modpixpath = str_replace('http:', 'https:', $CFG->modpixpath);
2582         foreach ($CFG->stylesheets as $key => $stylesheet) {
2583             $CFG->stylesheets[$key] = str_replace('http:', 'https:', $stylesheet);
2584         }
2585     }
2590 /**
2591  * Returns text to be displayed to the user which reflects their login status
2592  *
2593  * @uses $CFG
2594  * @uses $USER
2595  * @param course $course {@link $COURSE} object containing course information
2596  * @param user $user {@link $USER} object containing user information
2597  * @return string
2598  */
2599 function user_login_string($course=NULL, $user=NULL) {
2600     global $USER, $CFG, $SITE;
2602     if (empty($user) and !empty($USER->id)) {
2603         $user = $USER;
2604     }
2606     if (empty($course)) {
2607         $course = $SITE;
2608     }
2610     if (isset($user->realuser)) {
2611         if ($realuser = get_record('user', 'id', $user->realuser)) {
2612             $fullname = fullname($realuser, true);
2613             $realuserinfo = " [<a $CFG->frametarget
2614             href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&amp;return=1\">$fullname</a>] ";
2615         }
2616     } else {
2617         $realuserinfo = '';
2618     }
2620     if (empty($CFG->loginhttps)) {
2621         $wwwroot = $CFG->wwwroot;
2622     } else {
2623         $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
2624     }
2626     if (empty($course->id)) {
2627         // $course->id is not defined during installation
2628         return '';
2629     } else if (isset($user->id) and $user->id) {
2630         $context = get_context_instance(CONTEXT_COURSE, $course->id);
2632         $fullname = fullname($user, true);
2633         $username = "<a $CFG->frametarget href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>";
2634         if (is_mnet_remote_user($user) and $idprovider = get_record('mnet_host', 'id', $user->mnethostid)) {
2635             $username .= " from <a $CFG->frametarget href=\"{$idprovider->wwwroot}\">{$idprovider->name}</a>";
2636         }
2637         if (isset($user->username) && $user->username == 'guest') {
2638             $loggedinas = $realuserinfo.get_string('loggedinasguest').
2639                       " (<a $CFG->frametarget href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
2640         } else if (!empty($user->switchrole[$context->id])) {
2641             $rolename = '';
2642             if ($role = get_record('role', 'id', $user->switchrole[$context->id])) {
2643                 $rolename = ': '.format_string($role->name);
2644             }
2645             $loggedinas = get_string('loggedinas', 'moodle', $username).$rolename.
2646                       " (<a $CFG->frametarget
2647                       href=\"$CFG->wwwroot/course/view.php?id=$course->id&amp;switchrole=0&amp;sesskey=".sesskey()."\">".get_string('switchrolereturn').'</a>)';
2648         } else {
2649             $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '.
2650                       " (<a $CFG->frametarget href=\"$CFG->wwwroot/login/logout.php\">".get_string('logout').'</a>)';
2651         }
2652     } else {
2653         $loggedinas = get_string('loggedinnot', 'moodle').
2654                       " (<a $CFG->frametarget href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
2655     }
2656     return '<div class="logininfo">'.$loggedinas.'</div>';
2659 /**
2660  * Tests whether $THEME->rarrow, $THEME->larrow have been set (theme/-/config.php).
2661  * If not it applies sensible defaults.
2662  *
2663  * Accessibility: right and left arrow Unicode characters for breadcrumb, calendar,
2664  * search forum block, etc. Important: these are 'silent' in a screen-reader
2665  * (unlike &gt; &raquo;), and must be accompanied by text.
2666  * @uses $THEME
2667  */
2668 function check_theme_arrows() {
2669     global $THEME;
2671     if (!isset($THEME->rarrow) and !isset($THEME->larrow)) {
2672         // Default, looks good in Win XP/IE 6, Win/Firefox 1.5, Win/Netscape 8...
2673         // Also OK in Win 9x/2K/IE 5.x
2674         $THEME->rarrow = '&#x25BA;';
2675         $THEME->larrow = '&#x25C4;';
2676         $uagent = $_SERVER['HTTP_USER_AGENT'];
2677         if (false !== strpos($uagent, 'Opera')
2678             || false !== strpos($uagent, 'Mac')) {
2679             // Looks good in Win XP/Mac/Opera 8/9, Mac/Firefox 2, Camino, Safari.
2680             // Not broken in Mac/IE 5, Mac/Netscape 7 (?).
2681             $THEME->rarrow = '&#x25B6;';
2682             $THEME->larrow = '&#x25C0;';
2683         }
2684         elseif (false !== strpos($uagent, 'Konqueror')) {
2685             $THEME->rarrow = '&rarr;';
2686             $THEME->larrow = '&larr;';
2687         }
2688         elseif (isset($_SERVER['HTTP_ACCEPT_CHARSET'])
2689             && false === stripos($_SERVER['HTTP_ACCEPT_CHARSET'], 'utf-8')) {
2690             // (Win/IE 5 doesn't set ACCEPT_CHARSET, but handles Unicode.)
2691             // To be safe, non-Unicode browsers!
2692             $THEME->rarrow = '&gt;';
2693             $THEME->larrow = '&lt;';
2694         }
2695     }
2698 /**
2699  * Prints breadcrumb trail of links, called in theme/-/header.html
2700  *
2701  * @uses $CFG
2702  * @param string $navigation The breadcrumb navigation string to be printed
2703  * @param string $separator The breadcrumb trail separator. The default 0 leads to the use
2704  *  of $THEME->rarrow, themes could use '&rarr;', '/', or '' for a style-sheet solution.
2705  * @param boolean $return False to echo the breadcrumb string (default), true to return it.
2706  */
2707 function print_navigation ($navigation, $separator=0, $return=false) {
2708     global $CFG, $THEME;
2709     $output = '';
2711     check_theme_arrows();
2712     if (0 === $separator) {
2713         $separator = $THEME->rarrow;
2714     }
2715     if (!empty($separator)) {
2716         $separator = '<span class="sep">'. $separator .'</span>';
2717     }
2719     if ($navigation) {
2720         //Accessibility: breadcrumb links now in a list, &raquo; replaced with a 'silent' character.
2721         $nav_text = get_string('youarehere','access');
2722         $output .= '<h2 class="accesshide">'.$nav_text."</h2><ul>\n";
2723         if (! $site = get_site()) {
2724             $site->shortname = get_string('home');
2725         }
2726         $navigation = "<li>$separator ". str_replace('->', "</li>\n<li>$separator", $navigation) ."</li>\n";
2727         $output .= '<li class="first"><a '.$CFG->frametarget.' href="'. $CFG->wwwroot.((!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM, SITEID)) && !empty($USER->id) && !empty($CFG->mymoodleredirect) && !isguest())
2728                                                                        ? '/my' : '') .'/">'. $site->shortname ."</a></li>\n". $navigation;
2729         $output .= "</ul>\n";
2730     }
2732     if ($return) {
2733         return $output;
2734     } else {
2735         echo $output;
2736     }
2739 /**
2740  * Prints a string in a specified size  (retained for backward compatibility)
2741  *
2742  * @param string $text The text to be displayed
2743  * @param int $size The size to set the font for text display.
2744  */
2745 function print_headline($text, $size=2, $return=false) {
2746     $output = print_heading($text, '', $size, true);
2747     if ($return) {
2748         return $output;
2749     } else {
2750         echo $output;
2751     }
2754 /**
2755  * Prints text in a format for use in headings.
2756  *
2757  * @param string $text The text to be displayed
2758  * @param string $align The alignment of the printed paragraph of text
2759  * @param int $size The size to set the font for text display.
2760  */
2761 function print_heading($text, $align='', $size=2, $class='main', $return=false) {
2762     if ($align) {
2763         $align = ' style="text-align:'.$align.';"';
2764     }
2765     if ($class) {
2766         $class = ' class="'.$class.'"';
2767     }
2768     $output = "<h$size $align $class>".stripslashes_safe($text)."</h$size>";
2770     if ($return) {
2771         return $output;
2772     } else {
2773         echo $output;
2774     }
2777 /**
2778  * Centered heading with attached help button (same title text)
2779  * and optional icon attached
2780  *
2781  * @param string $text The text to be displayed
2782  * @param string $helppage The help page to link to
2783  * @param string $module The module whose help should be linked to
2784  * @param string $icon Image to display if needed
2785  */
2786 function print_heading_with_help($text, $helppage, $module='moodle', $icon='', $return=false) {
2787     $output = '';
2788     $output .= '<h2 class="main help">'.$icon.stripslashes_safe($text);
2789     $output .= helpbutton($helppage, $text, $module, true, false, '', true);
2790     $output .= '</h2>';
2792     if ($return) {
2793         return $output;
2794     } else {
2795         echo $output;
2796     }
2800 function print_heading_block($heading, $class='', $return=false) {
2801     //Accessibility: 'headingblock' is now H1, see theme/standard/styles_*.css: ??
2802     $output = '<h2 class="headingblock header '.$class.'">'.stripslashes($heading).'</h2>';
2804     if ($return) {
2805         return $output;
2806     } else {
2807         echo $output;
2808     }
2812 /**
2813  * Print a link to continue on to another page.
2814  *
2815  * @uses $CFG
2816  * @param string $link The url to create a link to.
2817  */
2818 function print_continue($link, $return=false) {
2820     global $CFG;
2822     // in case we are logging upgrade in admin/index.php stop it
2823     if (function_exists('upgrade_log_finish')) {
2824         upgrade_log_finish();
2825     }
2827     $output = '';
2829     if ($link == '') {
2830         if (!empty($_SERVER['HTTP_REFERER'])) {
2831             $link = $_SERVER['HTTP_REFERER'];
2832             $link = str_replace('&', '&amp;', $link); // make it valid XHTML 
2833         } else {
2834             $link = $CFG->wwwroot .'/';
2835         }
2836     }
2838     $output .= '<div class="continuebutton">';
2840     $output .= print_single_button($link, NULL, get_string('continue'), 'post', $CFG->framename, true);
2841     $output .= '</div>'."\n";
2843     if ($return) {
2844         return $output;
2845     } else {
2846         echo $output;
2847     }
2851 /**
2852  * Print a message in a standard themed box.
2853  * Replaces print_simple_box (see deprecatedlib.php)
2854  *
2855  * @param string $message, the content of the box
2856  * @param string $classes, space-separated class names.
2857  * @param string $ids, space-separated id names.
2858  * @param boolean $return, return as string or just print it
2859  */
2860 function print_box($message, $classes='generalbox', $ids='', $return=false) {
2862     $output  = print_box_start($classes, $ids, true);
2863     $output .= stripslashes_safe($message);
2864     $output .= print_box_end(true);
2866     if ($return) {
2867         return $output;
2868     } else {
2869         echo $output;
2870     }
2873 /**
2874  * Starts a box using divs
2875  * Replaces print_simple_box_start (see deprecatedlib.php)
2876  *
2877  * @param string $classes, space-separated class names.
2878  * @param string $ids, space-separated id names.
2879  * @param boolean $return, return as string or just print it
2880  */
2881 function print_box_start($classes='generalbox', $ids='', $return=false) {
2882     $output = '';
2884     if ($ids) {
2885         $ids = ' id="'.$ids.'"';
2886     }
2888     $output .= '<div'.$ids.' class="box '.$classes.'">';
2890     if ($return) {
2891         return $output;
2892     } else {
2893         echo $output;
2894     }
2898 /**
2899  * Simple function to end a box (see above)
2900  * Replaces print_simple_box_end (see deprecatedlib.php)
2901  *
2902  * @param boolean $return, return as string or just print it
2903  */
2904 function print_box_end($return=false) {
2905     $output = '</div>';
2906     if ($return) {
2907         return $output;
2908     } else {
2909         echo $output;
2910     }
2914 /**
2915  * Print a self contained form with a single submit button.
2916  *
2917  * @param string $link ?
2918  * @param array $options ?
2919  * @param string $label ?
2920  * @param string $method ?
2921  * @todo Finish documenting this function
2922  */
2923 function print_single_button($link, $options, $label='OK', $method='get', $target='_self', $return=false) {
2924     $output = '';
2925     $link = str_replace('"', '&quot;', $link); //basic XSS protection
2926     $output .= '<div class="singlebutton">';
2927     // taking target out, will need to add later target="'.$target.'"
2928     $output .= '<form action="'. $link .'" method="'. $method .'">';   
2929     $output .= '<fieldset class="invisiblefieldset">';
2930     if ($options) {
2931         foreach ($options as $name => $value) {
2932             $output .= '<input type="hidden" name="'. $name .'" value="'. s($value) .'" />';
2933         }
2934     }
2935     $output .= '<input type="submit" value="'. s($label) .'" /></fieldset></form></div>';
2937     if ($return) {
2938         return $output;
2939     } else {
2940         echo $output;
2941     }
2945 /**
2946  * Print a spacer image with the option of including a line break.
2947  *
2948  * @param int $height ?
2949  * @param int $width ?
2950  * @param boolean $br ?
2951  * @todo Finish documenting this function
2952  */
2953 function print_spacer($height=1, $width=1, $br=true, $return=false) {
2954     global $CFG;
2955     $output = '';
2957     $output .= '<img class="spacer" height="'. $height .'" width="'. $width .'" src="'. $CFG->wwwroot .'/pix/spacer.gif" alt="" />';
2958     if ($br) {
2959         $output .= '<br />'."\n";
2960     }
2962     if ($return) {
2963         return $output;
2964     } else {
2965         echo $output;
2966     }
2969 /**
2970  * Given the path to a picture file in a course, or a URL,
2971  * this function includes the picture in the page.
2972  *
2973  * @param string $path ?
2974  * @param int $courseid ?
2975  * @param int $height ?
2976  * @param int $width ?
2977  * @param string $link ?
2978  * @todo Finish documenting this function
2979  */
2980 function print_file_picture($path, $courseid=0, $height='', $width='', $link='', $return=false) {
2981     global $CFG;
2982     $output = '';
2984     if ($height) {
2985         $height = 'height="'. $height .'"';
2986     }
2987     if ($width) {
2988         $width = 'width="'. $width .'"';
2989     }
2990     if ($link) {
2991         $output .= '<a href="'. $link .'">';
2992     }
2993     if (substr(strtolower($path), 0, 7) == 'http://') {
2994         $output .= '<img style="height:'.$height.'px;width:'.$width.'px;" src="'. $path .'" />';
2996     } else if ($courseid) {
2997         $output .= '<img style="height:'.$height.'px;width:'.$width.'px;" src="';
2998         if ($CFG->slasharguments) {        // Use this method if possible for better caching
2999             $output .= $CFG->wwwroot .'/file.php/'. $courseid .'/'. $path;
3000         } else {
3001             $output .= $CFG->wwwroot .'/file.php?file=/'. $courseid .'/'. $path;
3002         }
3003         $output .= '" />';
3004     } else {
3005         $output .= 'Error: must pass URL or course';
3006     }
3007     if ($link) {
3008         $output .= '</a>';
3009     }
3011     if ($return) {
3012         return $output;
3013     } else {
3014         echo $output;
3015     }
3018 /**
3019  * Print the specified user's avatar.
3020  *
3021  * @param int $userid ?
3022  * @param int $courseid ?
3023  * @param boolean $picture Print the user picture?
3024  * @param int $size Size in pixels.  Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatability
3025  * @param boolean $return If false print picture to current page, otherwise return the output as string
3026  * @param boolean $link Enclose printed image in a link to view specified course?
3027  * @param string $target link target attribute
3028  * @param boolean $alttext use username or userspecified text in image alt attribute
3029  * return string
3030  * @todo Finish documenting this function
3031  */
3032 function print_user_picture($userid, $courseid, $picture, $size=0, $return=false, $link=true, $target='', $alttext=true) {
3033     global $CFG;
3035     if ($link) {
3036         if ($target) {
3037             $target=' target="_blank"';
3038         }
3039         $output = '<a '.$target.' href="'. $CFG->wwwroot .'/user/view.php?id='. $userid .'&amp;course='. $courseid .'">';
3040     } else {
3041         $output = '';
3042     }
3043     if (empty($size)) {
3044         $file = 'f2';
3045         $size = 35;
3046     } else if ($size === true or $size == 1) {
3047         $file = 'f1';
3048         $size = 100;
3049     } else if ($size >= 50) {
3050         $file = 'f1';
3051     } else {
3052         $file = 'f2';
3053     }
3054     $class = "userpicture";
3055     if ($picture) {  // Print custom user picture
3056         if ($CFG->slasharguments) {        // Use this method if possible for better caching
3057             $src =  $CFG->wwwroot .'/user/pix.php/'. $userid .'/'. $file .'.jpg';
3058         } else {
3059             $src =  $CFG->wwwroot .'/user/pix.php?file=/'. $userid .'/'. $file .'.jpg';
3060         }
3061     } else {         // Print default user pictures (use theme version if available)
3062         $class .= " defaultuserpic";
3063         $src =  "$CFG->pixpath/u/$file.png";
3064     }
3065     $imagealt = '';
3066     if ($alttext and $user = get_record('user','id',$userid)) {
3067         if (!empty($user->imagealt)) {
3068             $imagealt = $user->imagealt;
3069         } else {
3070             $imagealt = get_string('pictureof','',fullname($user));
3071         }
3072     }
3074     $output .= '<img class="'.$class.'" src="'.$src.'" alt="'.s($imagealt).'" />';
3075     if ($link) {
3076         $output .= '</a>';
3077     }
3079     if ($return) {
3080         return $output;
3081     } else {
3082         echo $output;
3083     }
3086 /**
3087  * Prints a summary of a user in a nice little box.
3088  *
3089  * @uses $CFG
3090  * @uses $USER
3091  * @param user $user A {@link $USER} object representing a user
3092  * @param course $course A {@link $COURSE} object representing a course
3093  */
3094 function print_user($user, $course, $messageselect=false, $return=false) {
3096     global $CFG, $USER;
3098     $output = '';
3100     static $string;
3101     static $datestring;
3102     static $countries;
3104     $context = get_context_instance(CONTEXT_COURSE, $course->id);
3106     if (empty($string)) {     // Cache all the strings for the rest of the page
3108         $string->email       = get_string('email');
3109         $string->location    = get_string('location');
3110         $string->lastaccess  = get_string('lastaccess');
3111         $string->activity    = get_string('activity');
3112         $string->unenrol     = get_string('unenrol');
3113         $string->loginas     = get_string('loginas');
3114         $string->fullprofile = get_string('fullprofile');
3115         $string->role        = get_string('role');
3116         $string->name        = get_string('name');
3117         $string->never       = get_string('never');
3119         $datestring->day     = get_string('day');
3120         $datestring->days    = get_string('days');
3121         $datestring->hour    = get_string('hour');
3122         $datestring->hours   = get_string('hours');
3123         $datestring->min     = get_string('min');
3124         $datestring->mins    = get_string('mins');
3125         $datestring->sec     = get_string('sec');
3126         $datestring->secs    = get_string('secs');
3128         $countries = get_list_of_countries();
3129     }
3131 /// Get the hidden field list
3132     if (has_capability('moodle/course:viewhiddenuserfields', $context)) {
3133         $hiddenfields = array();
3134     } else {
3135         $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
3136     }
3138     $output .= '<table class="userinfobox">';
3139     $output .= '<tr>';
3140     $output .= '<td class="left side">';
3141     $output .= print_user_picture($user->id, $course->id, $user->picture, true, true);
3142     $output .= '</td>';
3143     $output .= '<td class="content">';
3144     $output .= '<div class="username">'.fullname($user, has_capability('moodle/site:viewfullnames', $context)).'</div>';
3145     $output .= '<div class="info">';
3146     if (!empty($user->role) and ($user->role <> $course->teacher)) {
3147         $output .= $string->role .': '. $user->role .'<br />';
3148     }
3149     if ($user->maildisplay == 1 or ($user->maildisplay == 2 and ($course->id != SITEID) and !isguest()) or
3150 has_capability('moodle/course:viewhiddenuserfields', $context)) {
3151         $output .= $string->email .': <a href="mailto:'. $user->email .'">'. $user->email .'</a><br />';
3152     }
3153     if (($user->city or $user->country) and (!isset($hiddenfields['city']) or !isset($hiddenfields['country']))) {
3154         $output .= $string->location .': ';
3155         if ($user->city && !isset($hiddenfields['city'])) {
3156             $output .= $user->city;
3157         }
3158         if (!empty($countries[$user->country]) && !isset($hiddenfields['country'])) {
3159             if ($user->city && !isset($hiddenfields['city'])) {
3160                 $output .= ', ';
3161             }
3162             $output .= $countries[$user->country];
3163         }
3164         $output .= '<br />';
3165     }
3167     if (!isset($hiddenfields['lastaccess'])) {
3168         if ($user->lastaccess) {
3169             $output .= $string->lastaccess .': '. userdate($user->lastaccess);
3170             $output .= '&nbsp; ('. format_time(time() - $user->lastaccess, $datestring) .')';
3171         } else {
3172             $output .= $string->lastaccess .': '. $string->never;
3173         }
3174     }
3175     $output .= '</div></td><td class="links">';
3176     //link to blogs
3177     if ($CFG->bloglevel > 0) {
3178         $output .= '<a href="'.$CFG->wwwroot.'/blog/index.php?userid='.$user->id.'">'.get_string('blogs','blog').'</a><br />';
3179     }
3181     if (has_capability('moodle/site:viewreports', $context)) {
3182         $timemidnight = usergetmidnight(time());
3183         $output .= '<a href="'. $CFG->wwwroot .'/course/user.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->activity .'</a><br />';
3184     }
3185     if (has_capability('moodle/role:assign', $context, NULL)) {  // Includes admins
3186         $output .= '<a href="'. $CFG->wwwroot .'/course/unenrol.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->unenrol .'</a><br />';
3187     }
3188     if ($USER->id != $user->id && has_capability('moodle/user:loginas', $context))  {
3189         $output .= '<a href="'. $CFG->wwwroot .'/course/loginas.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->loginas .'</a><br />';
3190     }
3191     $output .= '<a href="'. $CFG->wwwroot .'/user/view.php?id='. $user->id .'&amp;course='. $course->id .'">'. $string->fullprofile .'...</a>';
3193     if (!empty($messageselect)) {
3194         $output .= '<br /><input type="checkbox" name="user'.$user->id.'" /> ';
3195     }
3197     $output .= '</td></tr></table>';
3199     if ($return) {
3200         return $output;
3201     } else {
3202         echo $output;
3203     }
3206 /**
3207  * Print a specified group's avatar.
3208  *
3209  * @param group $group A {@link group} object representing a group or array of groups
3210  * @param int $courseid ?
3211  * @param boolean $large ?
3212  * @param boolean $return ?
3213  * @param boolean $link ?
3214  * @return string
3215  * @todo Finish documenting this function
3216  */
3217 function print_group_picture($group, $courseid, $large=false, $return=false, $link=true) {
3218     global $CFG;
3220     if (is_array($group)) {
3221         $output = '';
3222         foreach($group as $g) {
3223             $output .= print_group_picture($g, $courseid, $large, true, $link);
3224         }
3225         if ($return) {
3226             return $output;
3227         } else {
3228             echo $output;
3229             return;
3230         }
3231     }
3233     $context = get_context_instance(CONTEXT_COURSE, $courseid);
3235     if ($group->hidepicture and !has_capability('moodle/course:managegroups', $context)) {
3236         return '';
3237     }
3239     if ($link or has_capability('moodle/site:accessallgroups', $context)) {
3240         $output = '<a href="'. $CFG->wwwroot .'/user/index.php?id='. $courseid .'&amp;group='. $group->id .'">';
3241     } else {
3242         $output = '';
3243     }
3244     if ($large) {
3245         $file = 'f1';
3246         $size = 100;
3247     } else {
3248         $file = 'f2';
3249         $size = 35;
3250     }
3251     if ($group->picture) {  // Print custom group picture
3252         if ($CFG->slasharguments) {        // Use this method if possible for better caching
3253             $output .= '<img class="grouppicture" align="middle" src="'.$CFG->wwwroot.'/user/pixgroup.php/'.$group->id.'/'.$file.'.jpg"'.
3254                        ' style="width:'.$size.'px;height:'.$size.'px;" alt="'.s(get_string('group').' '.$group->name).'" title="'.s($group->name).'"/>';
3255         } else {
3256             $output .= '<img class="grouppicture" align="middle" src="'.$CFG->wwwroot.'/user/pixgroup.php?file=/'.$group->id.'/'.$file.'.jpg"'.
3257                        ' style="width:'.$size.'px;height:'.$size.'px;" alt="'.s(get_string('group').' '.$group->name).'" title="'.s($group->name).'"/>';
3258         }
3259     }
3260     if ($link or has_capability('moodle/site:accessallgroups', $context)) {
3261         $output .= '</a>';
3262     }
3264     if ($return) {
3265         return $output;
3266     } else {
3267         echo $output;
3268     }
3271 /**
3272  * Print a png image.
3273  *
3274  * @param string $url ?
3275  * @param int $sizex ?
3276  * @param int $sizey ?
3277  * @param boolean $return ?
3278  * @param string $parameters ?
3279  * @todo Finish documenting this function
3280  */
3281 function print_png($url, $sizex, $sizey, $return, $parameters='alt=""') {
3282     global $CFG;
3283     static $recentIE;
3285     if (!isset($recentIE)) {
3286         $recentIE = check_browser_version('MSIE', '5.0');
3287     }
3289     if ($recentIE) {  // work around the HORRIBLE bug IE has with alpha transparencies
3290         $output .= '<img src="'. $CFG->pixpath .'/spacer.gif" width="'. $sizex .'" height="'. $sizey .'"'.
3291                    ' class="png" style="width: '. $sizex .'px; height: '. $sizey .'px; '.
3292                    ' filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='.
3293                    "'$url', sizingMethod='scale') ".
3294                    ' '. $parameters .' />';
3295     } else {
3296         $output .= '<img src="'. $url .'" style="width: '. $sizex .'px; height: '. $sizey .'px; '. $parameters .' />';
3297     }
3299     if ($return) {
3300         return $output;
3301     } else {
3302         echo $output;
3303     }
3306 /**
3307  * Print a nicely formatted table.
3308  *
3309  * @param array $table is an object with several properties.
3310  *     <ul<li>$table->head - An array of heading names.
3311  *     <li>$table->align - An array of column alignments
3312  *     <li>$table->size  - An array of column sizes
3313  *     <li>$table->wrap - An array of "nowrap"s or nothing
3314  *     <li>$table->data[] - An array of arrays containing the data.
3315  *     <li>$table->width  - A percentage of the page
3316  *     <li>$table->tablealign  - Align the whole table
3317  *     <li>$table->cellpadding  - Padding on each cell
3318  *     <li>$table->cellspacing  - Spacing between cells
3319  * </ul>
3320  * @param bool $return whether to return an output string or echo now
3321  * @return boolean or $string
3322  * @todo Finish documenting this function
3323  */
3324 function print_table($table, $return=false) {
3325     $output = '';
3327     if (isset($table->align)) {
3328         foreach ($table->align as $key => $aa) {
3329             if ($aa) {
3330                 $align[$key] = ' text-align:'. $aa.';';
3331             } else {
3332                 $align[$key] = '';
3333             }
3334         }
3335     }
3336     if (isset($table->size)) {
3337         foreach ($table->size as $key => $ss) {
3338             if ($ss) {
3339                 $size[$key] = ' width:'. $ss .';';
3340             } else {
3341                 $size[$key] = '';
3342             }
3343         }
3344     }
3345     if (isset($table->wrap)) {
3346         foreach ($table->wrap as $key => $ww) {
3347             if ($ww) {
3348                 $wrap[$key] = ' white-space:nowrap;';
3349             } else {
3350                 $wrap[$key] = '';
3351             }
3352         }
3353     }
3355     if (empty($table->width)) {
3356         $table->width = '80%';
3357     }
3359     if (empty($table->tablealign)) {
3360         $table->tablealign = 'center';
3361     }
3363     if (empty($table->cellpadding)) {
3364         $table->cellpadding = '5';
3365     }
3367     if (empty($table->cellspacing)) {
3368         $table->cellspacing = '1';
3369     }
3371     if (empty($table->class)) {
3372         $table->class = 'generaltable';
3373     }
3375     $tableid = empty($table->id) ? '' : 'id="'.$table->id.'"';
3377     $output .= '<table width="'.$table->width.'" ';
3378     $output .= " cellpadding=\"$table->cellpadding\" cellspacing=\"$table->cellspacing\" class=\"$table->class boxalign$table->tablealign\" $tableid>\n";
3380     $countcols = 0;
3382     if (!empty($table->head)) {
3383         $countcols = count($table->head);
3384         $output .= '<tr>';
3385         foreach ($table->head as $key => $heading) {
3387             if (!isset($size[$key])) {
3388                 $size[$key] = '';
3389             }
3390             if (!isset($align[$key])) {
3391                 $align[$key] = '';
3392             }
3393             
3394             $output .= '<th class="header c'.$key.'" scope="col">'. $heading .'</th>';
3395             // commenting the following code out as <th style does not validate MDL-7861
3396             //$output .= '<th sytle="vertical-align:top;'. $align[$key].$size[$key] .';white-space:nowrap;" class="header c'.$key.'" scope="col">'. $heading .'</th>';
3397         }
3398         $output .= '</tr>'."\n";
3399     }
3401     if (!empty($table->data)) {
3402         $oddeven = 1;
3403         foreach ($table->data as $key => $row) {
3404             $oddeven = $oddeven ? 0 : 1;
3405             $output .= '<tr class="r'.$oddeven.'">'."\n";
3406             if ($row == 'hr' and $countcols) {
3407                 $output .= '<td colspan="'. $countcols .'"><div class="tabledivider"></div></td>';
3408             } else {  /// it's a normal row of data
3409                 foreach ($row as $key => $item) {
3410                     if (!isset($size[$key])) {
3411                         $size[$key] = '';
3412                     }
3413                     if (!isset($align[$key])) {
3414                         $align[$key] = '';
3415                     }
3416                     if (!isset($wrap[$key])) {
3417                         $wrap[$key] = '';
3418                     }
3419                     $output .= '<td style="'. $align[$key].$size[$key].$wrap[$key] .'" class="cell c'.$key.'">'. $item .'</td>';
3420                 }
3421             }
3422             $output .= '</tr>'."\n";
3423         }
3424     }
3425     $output .= '</table>'."\n";
3427     if ($return) {
3428         return $output;
3429     }
3431     echo $output;
3432     return true;
3435 /**
3436  * Creates a nicely formatted table and returns it.
3437  *
3438  * @param array $table is an object with several properties.
3439  *     <ul<li>$table->head - An array of heading names.
3440  *     <li>$table->align - An array of column alignments
3441  *     <li>$table->size  - An array of column sizes
3442  *     <li>$table->wrap - An array of "nowrap"s or nothing
3443  *     <li>$table->data[] - An array of arrays containing the data.
3444  *     <li>$table->class -  A css class name
3445  *     <li>$table->fontsize - Is the size of all the text
3446  *     <li>$table->tablealign  - Align the whole table
3447  *     <li>$table->width  - A percentage of the page
3448  *     <li>$table->cellpadding  - Padding on each cell
3449  *     <li>$table->cellspacing  - Spacing between cells
3450  * </ul>
3451  * @return string
3452  * @todo Finish documenting this function
3453  */
3454 function make_table($table) {
3456     if (isset($table->align)) {
3457         foreach ($table->align as $key => $aa) {
3458             if ($aa) {
3459                 $align[$key] = ' align="'. $aa .'"';
3460             } else {
3461                 $align[$key] = '';
3462             }
3463         }
3464     }
3465     if (isset($table->size)) {
3466         foreach ($table->size as $key => $ss) {
3467             if ($ss) {
3468                 $size[$key] = ' width="'. $ss .'"';
3469             } else {
3470                 $size[$key] = '';
3471             }
3472         }
3473     }
3474     if (isset($table->wrap)) {
3475         foreach ($table->wrap as $key => $ww) {
3476             if ($ww) {
3477                 $wrap[$key] = ' style="white-space:nowrap;" ';
3478             } else {
3479                 $wrap[$key] = '';
3480             }
3481         }
3482     }
3484     if (empty($table->width)) {
3485         $table->width = '80%';
3486     }
3488     if (empty($table->tablealign)) {
3489         $table->tablealign = 'center';
3490     }
3492     if (empty($table->cellpadding)) {
3493         $table->cellpadding = '5';
3494     }
3496     if (empty($table->cellspacing)) {
3497         $table->cellspacing = '1';
3498     }
3500     if (empty($table->class)) {
3501         $table->class = 'generaltable';
3502     }
3504     if (empty($table->fontsize)) {
3505         $fontsize = '';
3506     } else {
3507         $fontsize = '<font size="'. $table->fontsize .'">';
3508     }
3510     $output =  '<table width="'. $table->width .'" align="'. $table->tablealign .'" ';
3511     $output .= ' cellpadding="'. $table->cellpadding .'" cellspacing="'. $table->cellspacing .'" class="'. $table->class .'">'."\n";
3513     if (!empty($table->head)) {
3514         $output .= '<tr valign="top">';
3515         foreach ($table->head as $key => $heading) {
3516             if (!isset($size[$key])) {
3517                 $size[$key] = '';
3518             }
3519             if (!isset($align[$key])) {
3520                 $align[$key] = '';
3521             }
3522             $output .= '<th valign="top" '. $align[$key].$size[$key] .' style="white-space:nowrap;" class="'. $table->class .'header" scope="col">'.$fontsize.$heading.'</th>';
3523         }
3524         $output .= '</tr>'."\n";
3525     }
3527     foreach ($table->data as $row) {
3528         $output .= '<tr valign="top">';
3529         foreach ($row as $key => $item) {
3530             if (!isset($size[$key])) {
3531                 $size[$key] = '';
3532             }
3533             if (!isset($align[$key])) {
3534                 $align[$key] = '';
3535             }
3536             if (!isset($wrap[$key])) {
3537                 $wrap[$key] = '';
3538             }
3539             $output .= '<td '. $align[$key].$size[$key].$wrap[$key] .' class="'. $table->class .'cell">'. $fontsize . $item .'</td>';
3540         }
3541         $output .= '</tr>'."\n";
3542     }
3543     $output .= '</table>'."\n";
3545     return $output;
3548 function print_recent_activity_note($time, $user, $text, $link, $return=false) {
3549     static $strftimerecent;
3550     $output = '';
3552     $context = get_context_instance(CONTEXT_SYSTEM, SITEID);
3553     $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
3555     if (empty($strftimerecent)) {
3556         $strftimerecent = get_string('strftimerecent');
3557     }
3559     $date = userdate($time, $strftimerecent);
3560     $name = fullname($user, $viewfullnames);
3562     $output .= '<div class="head">';
3563     $output .= '<div class="date">'.$date.'</div> '.
3564          '<div class="name">'.fullname($user, $viewfullnames).'</div>';
3565     $output .= '</div>';
3566     $output .= '<div class="info"><a href="'.$link.'">'.format_string($text,true).'</a></div>';
3568     if ($return) {
3569         return $output;
3570     } else {
3571         echo $output;
3572     }
3576 /**
3577  * Prints a basic textarea field.
3578  *
3579  * @uses $CFG
3580  * @param boolean $usehtmleditor ?
3581  * @param int $rows ?
3582  * @param int $cols ?
3583  * @param null $width <b>Legacy field no longer used!</b>  Set to zero to get control over mincols
3584  * @param null $height <b>Legacy field no longer used!</b>  Set to zero to get control over minrows
3585  * @param string $name ?
3586  * @param string $value ?
3587  * @param int $courseid ?
3588  * @todo Finish documenting this function
3589  */
3590 function print_textarea($usehtmleditor, $rows, $cols, $width, $height, $name, $value='', $courseid=0, $return=false, $id='') {
3591 /// $width and height are legacy fields and no longer used as pixels like they used to be.
3592 /// However, you can set them to zero to override the mincols and minrows values below.
3594     global $CFG, $COURSE, $HTTPSPAGEREQUIRED;
3595     static $scriptcount = 0; // For loading the htmlarea script only once.
3597     $mincols = 65;
3598     $minrows = 10;
3599     $str = '';
3601     if ($id === '') {
3602         $id = 'edit-'.$name;
3603     }
3605     if ( empty($CFG->editorsrc) ) { // for backward compatibility.
3606         if (empty($courseid)) {
3607             $courseid = $COURSE->id;
3608         }
3610         if ($usehtmleditor) {
3611             if (!empty($courseid) and has_capability('moodle/course:managefiles', get_context_instance(CONTEXT_COURSE, $courseid))) {
3612                 $httpsrequired = empty($HTTPSPAGEREQUIRED) ? '' : '&httpsrequired=1';  
3613                 // needed for course file area browsing in image insert plugin
3614                 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3615                         $CFG->httpswwwroot .'/lib/editor/htmlarea/htmlarea.php?id='.$courseid.$httpsrequired.'"></script>'."\n" : '';
3616             } else {
3617                 $httpsrequired = empty($HTTPSPAGEREQUIRED) ? '' : '?httpsrequired=1';  
3618                 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3619                          $CFG->httpswwwroot .'/lib/editor/htmlarea/htmlarea.php'.$httpsrequired.'"></script>'."\n" : '';
3620                     
3621             }
3622             $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3623                     $CFG->httpswwwroot .'/lib/editor/htmlarea/lang/en.php"></script>'."\n" : '';
3624             $scriptcount++;
3626             if ($height) {    // Usually with legacy calls
3627                 if ($rows < $minrows) {
3628                     $rows = $minrows;
3629                 }
3630             }
3631             if ($width) {    // Usually with legacy calls
3632                 if ($cols < $mincols) {
3633                     $cols = $mincols;
3634                 }
3635             }
3636         }
3637     }
3638     $str .= '<textarea class="form-textarea" id="'. $id .'" name="'. $name .'" rows="'. $rows .'" cols="'. $cols .'">';
3639     if ($usehtmleditor) {
3640         $str .= htmlspecialchars($value); // needed for editing of cleaned text!
3641     } else {
3642         $str .= s($value);
3643     }
3644     $str .= '</textarea>'."\n";
3646         if ($usehtmleditor) {
3647                 $str .= editorshortcutshelpbutton();
3648         }
3650     if ($return) {
3651         return $str;
3652     }
3653     echo $str;
3656 /**
3657  * Sets up the HTML editor on textareas in the current page.
3658  * If a field name is provided, then it will only be
3659  * applied to that field - otherwise it will be used
3660  * on every textarea in the page.
3661  *
3662  * In most cases no arguments need to be supplied
3663  *
3664  * @param string $name Form element to replace with HTMl editor by name
3665  */
3666 function use_html_editor($name='', $editorhidebuttons='', $id='') {
3667     $editor = 'editor_'.md5($name); //name might contain illegal characters
3668     if ($id === '') {
3669         $id = 'edit-'.$name;
3670     }
3671     echo "\n".'<script type="text/javascript" defer="defer">'."\n";
3672     echo '//<![CDATA['."\n\n"; // Extra \n is to fix odd wiki problem, MDL-8185
3673     echo "$editor = new HTMLArea('$id');\n";
3674     echo "var config = $editor.config;\n";
3676     echo print_editor_config($editorhidebuttons);
3678     if (empty($name)) {
3679         echo "\nHTMLArea.replaceAll($editor.config);\n";
3680     } else {
3681         echo "\n$editor.generate();\n";
3682     }
3683     echo '//]]>'."\n";
3684     echo '</script>'."\n";</