083a1388e90c661e93af58c78b1c29d34ecffaf4
[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);
139 /**
140  * Ensure that a variable is set
141  *
142  * Return $var if it is defined, otherwise return $default,
143  * This function is very similar to {@link optional_variable()}
144  *
145  * @param    mixed $var the variable which may be unset
146  * @param    mixed $default the value to return if $var is unset
147  * @return   mixed
148  */
149 function nvl(&$var, $default='') {
150     global $CFG;
152     if (!empty($CFG->disableglobalshack)) {
153       error( "The nvl() function is deprecated ($var, $default)." );
154     }
155     return isset($var) ? $var : $default;
158 /**
159  * Remove query string from url
160  *
161  * Takes in a URL and returns it without the querystring portion
162  *
163  * @param string $url the url which may have a query string attached
164  * @return string
165  */
166  function strip_querystring($url) {
168     if ($commapos = strpos($url, '?')) {
169         return substr($url, 0, $commapos);
170     } else {
171         return $url;
172     }
175 /**
176  * Returns the URL of the HTTP_REFERER, less the querystring portion
177  * @return string
178  */
179 function get_referer() {
181     return strip_querystring(nvl($_SERVER['HTTP_REFERER']));
185 /**
186  * Returns the name of the current script, WITH the querystring portion.
187  * this function is necessary because PHP_SELF and REQUEST_URI and SCRIPT_NAME
188  * return different things depending on a lot of things like your OS, Web
189  * server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.)
190  * <b>NOTE:</b> This function returns false if the global variables needed are not set.
191  *
192  * @return string
193  */
194  function me() {
196     if (!empty($_SERVER['REQUEST_URI'])) {
197         return $_SERVER['REQUEST_URI'];
199     } else if (!empty($_SERVER['PHP_SELF'])) {
200         if (!empty($_SERVER['QUERY_STRING'])) {
201             return $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
202         }
203         return $_SERVER['PHP_SELF'];
205     } else if (!empty($_SERVER['SCRIPT_NAME'])) {
206         if (!empty($_SERVER['QUERY_STRING'])) {
207             return $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
208         }
209         return $_SERVER['SCRIPT_NAME'];
211     } else if (!empty($_SERVER['URL'])) {     // May help IIS (not well tested)
212         if (!empty($_SERVER['QUERY_STRING'])) {
213             return $_SERVER['URL'] .'?'. $_SERVER['QUERY_STRING'];
214         }
215         return $_SERVER['URL'];
217     } else {
218         notify('Warning: Could not find any of these web server variables: $REQUEST_URI, $PHP_SELF, $SCRIPT_NAME or $URL');
219         return false;
220     }
223 /**
224  * Like {@link me()} but returns a full URL
225  * @see me()
226  * @return string
227  */
228 function qualified_me() {
230     global $CFG;
232     if (!empty($CFG->wwwroot)) {
233         $url = parse_url($CFG->wwwroot);
234     }
236     if (!empty($url['host'])) {
237         $hostname = $url['host'];
238     } else if (!empty($_SERVER['SERVER_NAME'])) {
239         $hostname = $_SERVER['SERVER_NAME'];
240     } else if (!empty($_ENV['SERVER_NAME'])) {
241         $hostname = $_ENV['SERVER_NAME'];
242     } else if (!empty($_SERVER['HTTP_HOST'])) {
243         $hostname = $_SERVER['HTTP_HOST'];
244     } else if (!empty($_ENV['HTTP_HOST'])) {
245         $hostname = $_ENV['HTTP_HOST'];
246     } else {
247         notify('Warning: could not find the name of this server!');
248         return false;
249     }
251     if (!empty($url['port'])) {
252         $hostname .= ':'.$url['port'];
253     } else if (!empty($_SERVER['SERVER_PORT'])) {
254         if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
255             $hostname .= ':'.$_SERVER['SERVER_PORT'];
256         }
257     }
259     if (isset($_SERVER['HTTPS'])) {
260         $protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
261     } else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS']
262         $protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://';
263     } else {
264         $protocol = 'http://';
265     }
267     $url_prefix = $protocol.$hostname;
268     return $url_prefix . me();
271 /**
272  * Determine if a web referer is valid
273  *
274  * Returns true if the referer is the same as the goodreferer. If
275  * the referer to test is not specified, use {@link qualified_me()}.
276  * If the admin has not set secure forms ($CFG->secureforms) then
277  * this function returns true regardless of a match.
278  *
279  * @uses $CFG
280  * @param string $goodreferer the url to compare to referer
281  * @return boolean
282  */
283 function match_referer($goodreferer = '') {
284     global $CFG;
286     if (empty($CFG->secureforms)) {    // Don't bother checking referer
287         return true;
288     }
290     if ($goodreferer == 'nomatch') {   // Don't bother checking referer
291         return true;
292     }
294     if (empty($goodreferer)) {
295         $goodreferer = qualified_me();
296         // try to remove everything after ? because POST url may contain GET parameters (SID rewrite, etc.)
297         $pos = strpos($goodreferer, '?');
298         if ($pos !== FALSE) {
299             $goodreferer = substr($goodreferer, 0, $pos);
300         }
301     }
303     $referer = get_referer();
305     return (($referer == $goodreferer) or ($referer == $CFG->wwwroot .'/') or ($referer == $CFG->wwwroot .'/index.php'));
308 /**
309  * Determine if there is data waiting to be processed from a form
310  *
311  * Used on most forms in Moodle to check for data
312  * Returns the data as an object, if it's found.
313  * This object can be used in foreach loops without
314  * casting because it's cast to (array) automatically
315  *
316  * Checks that submitted POST data exists, and also
317  * checks the referer against the given url (it uses
318  * the current page if none was specified.
319  *
320  * @uses $CFG
321  * @param string $url the url to compare to referer for secure forms
322  * @return boolean
323  */
324 function data_submitted($url='') {
327     global $CFG;
329     if (empty($_POST)) {
330         return false;
332     } else {
333         if (match_referer($url)) {
334             return (object)$_POST;
335         } else {
336             if ($CFG->debug > 10) {
337                 notice('The form did not come from this page! (referer = '. get_referer() .')');
338             }
339             return false;
340         }
341     }
344 /**
345  * Moodle replacement for php stripslashes() function,
346  * works also for objects and arrays.
347  *
348  * The standard php stripslashes() removes ALL backslashes
349  * even from strings - so  C:\temp becomes C:temp - this isn't good.
350  * This function should work as a fairly safe replacement
351  * to be called on quoted AND unquoted strings (to be sure)
352  *
353  * @param mixed something to remove unsafe slashes from
354  * @return mixed
355  */
356 function stripslashes_safe($mixed) {
357     // there is no need to remove slashes from int, float and bool types
358     if (empty($mixed)) {
359         //nothing to do...
360     } else if (is_string($mixed)) {
361         if (ini_get_bool('magic_quotes_sybase')) { //only unescape single quotes
362             $mixed = str_replace("''", "'", $mixed);
363         } else { //the rest, simple and double quotes and backslashes
364             $mixed = str_replace("\\'", "'", $mixed);
365             $mixed = str_replace('\\"', '"', $mixed);
366             $mixed = str_replace('\\\\', '\\', $mixed);
367         }
368     } else if (is_array($mixed)) {
369         foreach ($mixed as $key => $value) {
370             $mixed[$key] = stripslashes_safe($value);
371         }
372     } else if (is_object($mixed)) {
373         $vars = get_object_vars($mixed);
374         foreach ($vars as $key => $value) {
375             $mixed->$key = stripslashes_safe($value);
376         }
377     }
379     return $mixed;
382 /**
383  * Recursive implementation of stripslashes()
384  *
385  * This function will allow you to strip the slashes from a variable.
386  * If the variable is an array or object, slashes will be stripped
387  * from the items (or properties) it contains, even if they are arrays
388  * or objects themselves.
389  *
390  * @param mixed the variable to remove slashes from
391  * @return mixed
392  */
393 function stripslashes_recursive($var) {
394     if(is_object($var)) {
395         $properties = get_object_vars($var);
396         foreach($properties as $property => $value) {
397             $var->$property = stripslashes_recursive($value);
398         }
399     }
400     else if(is_array($var)) {
401         foreach($var as $property => $value) {
402             $var[$property] = stripslashes_recursive($value);
403         }
404     }
405     else if(is_string($var)) {
406         $var = stripslashes($var);
407     }
408     return $var;
411 /**
412  * Given some normal text this function will break up any
413  * long words to a given size by inserting the given character
414  *
415  * It's multibyte savvy and doesn't change anything inside html tags.
416  *
417  * @param string $string the string to be modified
418  * @param int $maxsize maximum length of the string to be returned
419  * @param string $cutchar the string used to represent word breaks
420  * @return string
421  */
422 function break_up_long_words($string, $maxsize=20, $cutchar=' ') {
424 /// Loading the textlib singleton instance. We are going to need it.
425     $textlib = textlib_get_instance();
427 /// First of all, save all the tags inside the text to skip them
428     $tags = array();
429     filter_save_tags($string,$tags);
431 /// Process the string adding the cut when necessary
432     $output = '';
433     $length = $textlib->strlen($string, current_charset());
434     $wordlength = 0;
436     for ($i=0; $i<$length; $i++) {
437         $char = $textlib->substr($string, $i, 1, current_charset());
438         if ($char == ' ' or $char == "\t" or $char == "\n" or $char == "\r" or $char == "<" or $char == ">") {
439             $wordlength = 0;
440         } else {
441             $wordlength++;
442             if ($wordlength > $maxsize) {
443                 $output .= $cutchar;
444                 $wordlength = 0;
445             }
446         }
447         $output .= $char;
448     }
450 /// Finally load the tags back again
451     if (!empty($tags)) {
452         $output = str_replace(array_keys($tags), $tags, $output);
453     }
455     return $output;
458 /**
459  * This does a search and replace, ignoring case
460  * This function is only used for versions of PHP older than version 5
461  * which do not have a native version of this function.
462  * Taken from the PHP manual, by bradhuizenga @ softhome.net
463  *
464  * @param string $find the string to search for
465  * @param string $replace the string to replace $find with
466  * @param string $string the string to search through
467  * return string
468  */
469 if (!function_exists('str_ireplace')) {    /// Only exists in PHP 5
470     function str_ireplace($find, $replace, $string) {
472         if (!is_array($find)) {
473             $find = array($find);
474         }
476         if(!is_array($replace)) {
477             if (!is_array($find)) {
478                 $replace = array($replace);
479             } else {
480                 // this will duplicate the string into an array the size of $find
481                 $c = count($find);
482                 $rString = $replace;
483                 unset($replace);
484                 for ($i = 0; $i < $c; $i++) {
485                     $replace[$i] = $rString;
486                 }
487             }
488         }
490         foreach ($find as $fKey => $fItem) {
491             $between = explode(strtolower($fItem),strtolower($string));
492             $pos = 0;
493             foreach($between as $bKey => $bItem) {
494                 $between[$bKey] = substr($string,$pos,strlen($bItem));
495                 $pos += strlen($bItem) + strlen($fItem);
496             }
497             $string = implode($replace[$fKey],$between);
498         }
499         return ($string);
500     }
503 /**
504  * Locate the position of a string in another string
505  *
506  * This function is only used for versions of PHP older than version 5
507  * which do not have a native version of this function.
508  * Taken from the PHP manual, by dmarsh @ spscc.ctc.edu
509  *
510  * @param string $haystack The string to be searched
511  * @param string $needle The string to search for
512  * @param int $offset The position in $haystack where the search should begin.
513  */
514 if (!function_exists('stripos')) {    /// Only exists in PHP 5
515     function stripos($haystack, $needle, $offset=0) {
517         return strpos(strtoupper($haystack), strtoupper($needle), $offset);
518     }
521 /**
522  * Load a template from file
523  *
524  * Returns a (big) string containing the contents of a template file with all
525  * the variables interpolated.  all the variables must be in the $var[] array or
526  * object (whatever you decide to use).
527  *
528  * <b>WARNING: do not use this on big files!!</b>
529  *
530  * @param string $filename Location on the server's filesystem where template can be found.
531  * @param mixed $var Passed in by reference. An array or object which will be loaded with data from the template file.
532  */
533 function read_template($filename, &$var) {
535     $temp = str_replace("\\", "\\\\", implode(file($filename), ''));
536     $temp = str_replace('"', '\"', $temp);
537     eval("\$template = \"$temp\";");
538     return $template;
541 /**
542  * Set a variable's value depending on whether or not it already has a value.
543  *
544  * If variable is set, set it to the set_value otherwise set it to the
545  * unset_value.  used to handle checkboxes when you are expecting them from
546  * a form
547  *
548  * @param mixed $var Passed in by reference. The variable to check.
549  * @param mixed $set_value The value to set $var to if $var already has a value.
550  * @param mixed $unset_value The value to set $var to if $var does not already have a value.
551  */
552 function checked(&$var, $set_value = 1, $unset_value = 0) {
554     if (empty($var)) {
555         $var = $unset_value;
556     } else {
557         $var = $set_value;
558     }
561 /**
562  * Prints the word "checked" if a variable is true, otherwise prints nothing,
563  * used for printing the word "checked" in a checkbox form element.
564  *
565  * @param boolean $var Variable to be checked for true value
566  * @param string $true_value Value to be printed if $var is true
567  * @param string $false_value Value to be printed if $var is false
568  */
569 function frmchecked(&$var, $true_value = 'checked', $false_value = '') {
571     if ($var) {
572         echo $true_value;
573     } else {
574         echo $false_value;
575     }
578 /**
579  * This function will create a HTML link that will work on both
580  * Javascript and non-javascript browsers.
581  * Relies on the Javascript function openpopup in javascript.php
582  *
583  * $url must be relative to home page  eg /mod/survey/stuff.php
584  * @param string $url Web link relative to home page
585  * @param string $name Name to be assigned to the popup window
586  * @param string $linkname Text to be displayed as web link
587  * @param int $height Height to assign to popup window
588  * @param int $width Height to assign to popup window
589  * @param string $title Text to be displayed as popup page title
590  * @param string $options List of additional options for popup window
591  * @todo Add code examples and list of some options that might be used.
592  * @param boolean $return Should the link to the popup window be returned as a string (true) or printed immediately (false)?
593  * @return string
594  * @uses $CFG
595  */
596 function link_to_popup_window ($url, $name='popup', $linkname='click here',
597                                $height=400, $width=500, $title='Popup window',
598                                $options='none', $return=false) {
600     global $CFG;
602     if ($options == 'none') {
603         $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
604     }
605     $fullscreen = 0;
607     if (!(strpos($url,$CFG->wwwroot) === false)) { // some log url entries contain _SERVER[HTTP_REFERRER] in which case wwwroot is already there.
608         $url = substr($url, strlen($CFG->wwwroot));
609     }
611     $link = '<a target="'. $name .'" title="'. $title .'" href="'. $CFG->wwwroot . $url .'" '.
612            "onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\">$linkname</a>";
613     if ($return) {
614         return $link;
615     } else {
616         echo $link;
617     }
620 /**
621  * This function will print a button submit form element
622  * that will work on both Javascript and non-javascript browsers.
623  * Relies on the Javascript function openpopup in javascript.php
624  *
625  * $url must be relative to home page  eg /mod/survey/stuff.php
626  * @param string $url Web link relative to home page
627  * @param string $name Name to be assigned to the popup window
628  * @param string $linkname Text to be displayed as web link
629  * @param int $height Height to assign to popup window
630  * @param int $width Height to assign to popup window
631  * @param string $title Text to be displayed as popup page title
632  * @param string $options List of additional options for popup window
633  * @param string $return If true, return as a string, otherwise print
634  * @return string
635  * @uses $CFG
636  */
637 function button_to_popup_window ($url, $name='popup', $linkname='click here',
638                                  $height=400, $width=500, $title='Popup window', $options='none', $return=false,
639                                  $id='', $class='') {
641     global $CFG;
643     if ($options == 'none') {
644         $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
645     }
647     if ($id) {
648         $id = ' id="'.$id.'" ';
649     }
650     if ($class) {
651         $class = ' class="'.$class.'" ';
652     }
653     $fullscreen = 0;
655     $button = '<input type="button" name="'.$name.'" title="'. $title .'" value="'. $linkname .' ..." '.$id.$class.
656               "onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\" />\n";
657     if ($return) {
658         return $button;
659     } else {
660         echo $button;
661     }
665 /**
666  * Prints a simple button to close a window
667  */
668 function close_window_button($name='closewindow', $return=false) {
669     $output = '';
671     $output .= '<center>' . "\n";
672     $output .= '<script type="text/javascript">' . "\n";
673     $output .= '<!--' . "\n";
674     $output .= "document.write('<form>');\n";
675     $output .= "document.write('<input type=\"button\" onclick=\"self.close();\" value=\"".get_string("closewindow")."\" />');\n";
676     $output .= "document.write('<\/form>');\n";
677     $output .= '-->' . "\n";
678     $output .= '</script>' . "\n";
679     $output .= '<noscript>' . "\n";
680     $output .= get_string($name);
681     $output .= '</noscript>' . "\n";
682     $output .= '</center>' . "\n";
684     if ($return) {
685         return $output;
686     } else {
687         echo $output;
688     }
691 /*
692  * Try and close the current window immediately using Javascript
693  */
694 function close_window($delay=0) {
695     echo '<script language="JavaScript" type="text/javascript">'."\n";
696     echo '<!--'."\n";
697     if ($delay) {
698         sleep($delay);
699     }
700     echo 'self.close();'."\n";
701     echo '-->'."\n";
702     echo '</script>'."\n";
703     exit;
707 /**
708  * Given an array of value, creates a popup menu to be part of a form
709  * $options["value"]["label"]
710  *
711  * @param    type description
712  * @todo Finish documenting this function
713  */
714 function choose_from_menu ($options, $name, $selected='', $nothing='choose', $script='',
715                            $nothingvalue='0', $return=false, $disabled=false, $tabindex=0) {
717     if ($nothing == 'choose') {
718         $nothing = get_string('choose') .'...';
719     }
721     $attributes = ($script) ? 'onchange="'. $script .'"' : '';
722     if ($disabled) {
723         $attributes .= ' disabled="disabled"';
724     }
726     if ($tabindex) {
727         $attributes .= ' tabindex="'.$tabindex.'"';
728     }
730     $id = str_replace('[]', '', $name); // name may end in [], which would make an invalid id. e.g. numeric question type editing form.
731     $output = '<select id="menu'.$id.'" name="'. $name .'" '. $attributes .'>' . "\n";
732     if ($nothing) {
733         $output .= '   <option value="'. $nothingvalue .'"'. "\n";
734         if ($nothingvalue === $selected) {
735             $output .= ' selected="selected"';
736         }
737         $output .= '>'. $nothing .'</option>' . "\n";
738     }
739     if (!empty($options)) {
740         foreach ($options as $value => $label) {
741             $output .= '   <option value="'. $value .'"';
742             if ((string)$value == (string)$selected) {
743                 $output .= ' selected="selected"';
744             }
745             if ($label === '') {
746                 $output .= '>'. $value .'</option>' . "\n";
747             } else {
748                 $output .= '>'. $label .'</option>' . "\n";
749             }
750         }
751     }
752     $output .= '</select>' . "\n";
754     if ($return) {
755         return $output;
756     } else {
757         echo $output;
758     }
761 /**
762  * Choose value 0 or 1 from a menu with options 'No' and 'Yes'.
763  * Other options like choose_from_menu.
764  */
765 function choose_from_menu_yesno($name, $selected, $script = '',
766         $return = false, $disabled = false, $tabindex = 0) {
767     return choose_from_menu(array(get_string('no'), get_string('yes')), $name,
768             $selected, '', $script, '0', $return, $disabled, $tabindex);
771 /**
772  * Just like choose_from_menu, but takes a nested array (2 levels) and makes a dropdown menu
773  * including option headings with the first level.
774  */
775 function choose_from_menu_nested($options,$name,$selected='',$nothing='choose',$script = '',
776                                  $nothingvalue=0,$return=false,$disabled=false,$tabindex=0) {
778    if ($nothing == 'choose') {
779         $nothing = get_string('choose') .'...';
780     }
782     $attributes = ($script) ? 'onchange="'. $script .'"' : '';
783     if ($disabled) {
784         $attributes .= ' disabled="disabled"';
785     }
787     if ($tabindex) {
788         $attributes .= ' tabindex="'.$tabindex.'"';
789     }
791     $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
792     if ($nothing) {
793         $output .= '   <option value="'. $nothingvalue .'"'. "\n";
794         if ($nothingvalue === $selected) {
795             $output .= ' selected="selected"';
796         }
797         $output .= '>'. $nothing .'</option>' . "\n";
798     }
799     if (!empty($options)) {
800         foreach ($options as $section => $values) {
801             $output .= '   <optgroup label="'.$section.'">'."\n";
802             foreach ($values as $value => $label) {
803                 $output .= '   <option value="'. $value .'"';
804                 if ((string)$value == (string)$selected) {
805                     $output .= ' selected="selected"';
806                 }
807                 if ($label === '') {
808                     $output .= '>'. $value .'</option>' . "\n";
809                 } else {
810                     $output .= '>'. $label .'</option>' . "\n";
811                 }
812             }
813             $output .= '   </optgroup>'."\n";
814         }
815     }
816     $output .= '</select>' . "\n";
818     if ($return) {
819         return $output;
820     } else {
821         echo $output;
822     }
826 /**
827  * Given an array of values, creates a group of radio buttons to be part of a form
828  *
829  * @param array  $options  An array of value-label pairs for the radio group (values as keys)
830  * @param string $name     Name of the radiogroup (unique in the form)
831  * @param string $checked  The value that is already checked
832  */
833 function choose_from_radio ($options, $name, $checked='', $return=false) {
835     static $idcounter = 0;
837     if (!$name) {
838         $name = 'unnamed';
839     }
841     $output = '<span class="radiogroup '.$name."\">\n";
843     if (!empty($options)) {
844         $currentradio = 0;
845         foreach ($options as $value => $label) {
846             $htmlid = 'auto-rb'.sprintf('%04d', ++$idcounter);
847             $output .= ' <span class="radioelement '.$name.' rb'.$currentradio."\">";
848             $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="radio" value="'.$value.'"';
849             if ($value == $checked) {
850                 $output .= ' checked="checked"';
851             }
852             if ($label === '') {
853                 $output .= ' /> <label for="'.$htmlid.'">'.  $value .'</label></span>' .  "\n";
854             } else {
855                 $output .= ' /> <label for="'.$htmlid.'">'.  $label .'</label></span>' .  "\n";
856             }
857             $currentradio = ($currentradio + 1) % 2;
858         }
859     }
861     $output .= '</span>' .  "\n";
863     if ($return) {
864         return $output;
865     } else {
866         echo $output;
867     }
870 /** Display an standard html checkbox with an optional label
871  *
872  * @param string  $name    The name of the checkbox
873  * @param string  $value   The valus that the checkbox will pass when checked
874  * @param boolean $checked The flag to tell the checkbox initial state
875  * @param string  $label   The label to be showed near the checkbox
876  * @param string  $alt     The info to be inserted in the alt tag
877  */
878 function print_checkbox ($name, $value, $checked = true, $label = '', $alt = '', $script='',$return=false) {
880     static $idcounter = 0;
882     if (!$name) {
883         $name = 'unnamed';
884     }
886     if (!$alt) {
887         $alt = 'checkbox';
888     }
890     if ($checked) {
891         $strchecked = ' checked="checked"';
892     } else {
893         $strchecked = '';
894     }
896     $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
897     $output  = '<span class="checkbox '.$name."\">";
898     $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="checkbox" value="'.$value.'" alt="'.$alt.'"'.$strchecked.' '.((!empty($script)) ? ' onClick="'.$script.'" ' : '').' />';
899     if(!empty($label)) {
900         $output .= ' <label for="'.$htmlid.'">'.$label.'</label>';
901     }
902     $output .= '</span>'."\n";
904     if (empty($return)) {
905         echo $output;
906     } else {
907         return $output;
908     }
912 /** Display an standard html text field with an optional label
913  *
914  * @param string  $name    The name of the text field
915  * @param string  $value   The value of the text field
916  * @param string  $label   The label to be showed near the text field
917  * @param string  $alt     The info to be inserted in the alt tag
918  */
919 function print_textfield ($name, $value, $alt = '',$size=50,$maxlength=0, $return=false) {
921     static $idcounter = 0;
923     if (empty($name)) {
924         $name = 'unnamed';
925     }
927     if (empty($alt)) {
928         $alt = 'textfield';
929     }
931     if (!empty($maxlength)) {
932         $maxlength = ' maxlength="'.$maxlength.'" ';
933     }
935     $htmlid = 'auto-tf'.sprintf('%04d', ++$idcounter);
936     $output  = '<span class="textfield '.$name."\">";
937     $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="text" value="'.$value.'" size="'.$size.'" '.$maxlength.' alt="'.$alt.'" />';
939     $output .= '</span>'."\n";
941     if (empty($return)) {
942         echo $output;
943     } else {
944         return $output;
945     }
950 /**
951  * Implements a complete little popup form
952  *
953  * @uses $CFG
954  * @param string $common  The URL up to the point of the variable that changes
955  * @param array $options  Alist of value-label pairs for the popup list
956  * @param string $formname Name must be unique on the page
957  * @param string $selected The option that is already selected
958  * @param string $nothing The label for the "no choice" option
959  * @param string $help The name of a help page if help is required
960  * @param string $helptext The name of the label for the help button
961  * @param boolean $return Indicates whether the function should return the text
962  *         as a string or echo it directly to the page being rendered
963  * @param string $targetwindow The name of the target page to open the linked page in.
964  * @return string If $return is true then the entire form is returned as a string.
965  * @todo Finish documenting this function<br>
966  */
967 function popup_form($common, $options, $formname, $selected='', $nothing='choose', $help='', $helptext='', $return=false, $targetwindow='self') {
969     global $CFG;
970     static $go, $choose;   /// Locally cached, in case there's lots on a page
972     if (empty($options)) {
973         return '';
974     }
976     if (!isset($go)) {
977         $go = get_string('go');
978     }
980     if ($nothing == 'choose') {
981         if (!isset($choose)) {
982             $choose = get_string('choose');
983         }
984         $nothing = $choose.'...';
985     }
987     $startoutput = '<form action="'.$CFG->wwwroot.'/course/jumpto.php"'.
988                         ' method="get"'.
989                         ' target="'.$CFG->framename.'"'.
990                         ' name="'.$formname.'"'.
991                         ' class="popupform">';
993     $output = '<select name="jump" onchange="'.$targetwindow.'.location=document.'.$formname.
994                        '.jump.options[document.'.$formname.'.jump.selectedIndex].value;">'."\n";
996     if ($nothing != '') {
997         $output .= "   <option value=\"javascript:void(0)\">$nothing</option>\n";
998     }
1000     $inoptgroup = false;
1001     foreach ($options as $value => $label) {
1003         if (substr($label,0,2) == '--') { /// we are starting a new optgroup
1005             /// Check to see if we already have a valid open optgroup
1006             /// XHTML demands that there be at least 1 option within an optgroup
1007             if ($inoptgroup and (count($optgr) > 1) ) {
1008                 $output .= implode('', $optgr);
1009                 $output .= '   </optgroup>';
1010             }
1012             unset($optgr);
1013             $optgr = array();
1015             $optgr[]  = '   <optgroup label="'. substr($label,2) .'">';   // Plain labels
1017             $inoptgroup = true; /// everything following will be in an optgroup
1018             continue;
1020         } else {
1021            if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()]))
1022             {
1023                 $url=sid_process_url( $common . $value );
1024             } else
1025             {
1026                 $url=$common . $value;
1027             }
1028             $optstr = '   <option value="' . $url . '"';
1030             if ($value == $selected) {
1031                 $optstr .= ' selected="selected"';
1032             }
1034             if ($label) {
1035                 $optstr .= '>'. $label .'</option>' . "\n";
1036             } else {
1037                 $optstr .= '>'. $value .'</option>' . "\n";
1038             }
1040             if ($inoptgroup) {
1041                 $optgr[] = $optstr;
1042             } else {
1043                 $output .= $optstr;
1044             }
1045         }
1047     }
1049     /// catch the final group if not closed
1050     if ($inoptgroup and count($optgr) > 1) {
1051         $output .= implode('', $optgr);
1052         $output .= '    </optgroup>';
1053     }
1055     $output .= '</select>';
1056     $output .= '<noscript id="noscript'.$formname.'" style="display: inline;">';
1057     $output .= '<input type="submit" value="'.$go.'" /></noscript>';
1058     $output .= '<script type="text/javascript">'.
1059                "\n<!--\n".
1060                'document.getElementById("noscript'.$formname.'").style.display = "none";'.
1061                "\n-->\n".'</script>';
1062     $output .= '</form>' . "\n";
1064     if ($help) {
1065         $button = helpbutton($help, $helptext, 'moodle', true, false, '', true);
1066     } else {
1067         $button = '';
1068     }
1070     if ($return) {
1071         return $startoutput.$button.$output;
1072     } else {
1073         echo $startoutput.$button.$output;
1074     }
1078 /**
1079  * Prints some red text
1080  *
1081  * @param string $error The text to be displayed in red
1082  */
1083 function formerr($error) {
1085     if (!empty($error)) {
1086         echo '<font color="#ff0000">'. $error .'</font>';
1087     }
1090 /**
1091  * Validates an email to make sure it makes sense.
1092  *
1093  * @param string $address The email address to validate.
1094  * @return boolean
1095  */
1096 function validate_email($address) {
1098     return (ereg('^[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+'.
1099                  '(\.[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+)*'.
1100                   '@'.
1101                   '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
1102                   '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$',
1103                   $address));
1106 /**
1107  * Extracts file argument either from file parameter or PATH_INFO
1108  *
1109  * @param string $scriptname name of the calling script
1110  * @return string file path (only safe characters)
1111  */
1112 function get_file_argument($scriptname) {
1113     global $_SERVER;
1115     $relativepath = FALSE;
1117     // first try normal parameter (compatible method == no relative links!)
1118     $relativepath = optional_param('file', FALSE, PARAM_PATH);
1119     if ($relativepath === '/testslasharguments') {
1120         echo 'test -1      : Incorrect use - try "file.php/testslasharguments" instead'; //indicate fopen/fread works for health center
1121         die;
1122     }
1124     // then try extract file from PATH_INFO (slasharguments method)
1125     if (!$relativepath and !empty($_SERVER['PATH_INFO'])) {
1126         $path_info = $_SERVER['PATH_INFO'];
1127         // check that PATH_INFO works == must not contain the script name
1128         if (!strpos($path_info, $scriptname)) {
1129             $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1130             if ($relativepath === '/testslasharguments') {
1131                 echo 'test 1      : Slasharguments test passed. Server confguration is compatible with file.php/1/pic.jpg slashargument setting.'; //indicate ok for health center
1132                 die;
1133             }
1134         }
1135     }
1137     // now if both fail try the old way
1138     // (for compatibility with misconfigured or older buggy php implementations)
1139     if (!$relativepath) {
1140         $arr = explode($scriptname, me());
1141         if (!empty($arr[1])) {
1142             $path_info = strip_querystring($arr[1]);
1143             $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1144             if ($relativepath === '/testslasharguments') {
1145                 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
1146                 die;
1147             }
1148         }
1149     }
1151     return $relativepath;
1154 /**
1155  * Check for bad characters ?
1156  *
1157  * @param string $string ?
1158  * @param int $allowdots ?
1159  * @todo Finish documenting this function - more detail needed in description as well as details on arguments
1160  */
1161 function detect_munged_arguments($string, $allowdots=1) {
1162     if (substr_count($string, '..') > $allowdots) {   // Sometimes we allow dots in references
1163         return true;
1164     }
1165     if (ereg('[\|\`]', $string)) {  // check for other bad characters
1166         return true;
1167     }
1168     if (empty($string) or $string == '/') {
1169         return true;
1170     }
1172     return false;
1175 /**
1176  * Searches the current environment variables for some slash arguments
1177  *
1178  * @param string $file ?
1179  * @todo Finish documenting this function
1180  */
1181 function get_slash_arguments($file='file.php') {
1183     if (!$string = me()) {
1184         return false;
1185     }
1187     $pathinfo = explode($file, $string);
1189     if (!empty($pathinfo[1])) {
1190         return addslashes($pathinfo[1]);
1191     } else {
1192         return false;
1193     }
1196 /**
1197  * Extracts arguments from "/foo/bar/something"
1198  * eg http://mysite.com/script.php/foo/bar/something
1199  *
1200  * @param string $string ?
1201  * @param int $i ?
1202  * @return array|string
1203  * @todo Finish documenting this function
1204  */
1205 function parse_slash_arguments($string, $i=0) {
1207     if (detect_munged_arguments($string)) {
1208         return false;
1209     }
1210     $args = explode('/', $string);
1212     if ($i) {     // return just the required argument
1213         return $args[$i];
1215     } else {      // return the whole array
1216         array_shift($args);  // get rid of the empty first one
1217         return $args;
1218     }
1221 /**
1222  * Just returns an array of text formats suitable for a popup menu
1223  *
1224  * @uses FORMAT_MOODLE
1225  * @uses FORMAT_HTML
1226  * @uses FORMAT_PLAIN
1227  * @uses FORMAT_MARKDOWN
1228  * @return array
1229  */
1230 function format_text_menu() {
1232     return array (FORMAT_MOODLE => get_string('formattext'),
1233                   FORMAT_HTML   => get_string('formathtml'),
1234                   FORMAT_PLAIN  => get_string('formatplain'),
1235                   FORMAT_MARKDOWN  => get_string('formatmarkdown'));
1238 /**
1239  * Given text in a variety of format codings, this function returns
1240  * the text as safe HTML.
1241  *
1242  * @uses $CFG
1243  * @uses FORMAT_MOODLE
1244  * @uses FORMAT_HTML
1245  * @uses FORMAT_PLAIN
1246  * @uses FORMAT_WIKI
1247  * @uses FORMAT_MARKDOWN
1248  * @param string $text The text to be formatted. This is raw text originally from user input.
1249  * @param int $format Identifier of the text format to be used
1250  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1251  * @param  array $options ?
1252  * @param int $courseid ?
1253  * @return string
1254  * @todo Finish documenting this function
1255  */
1256 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) {
1258     global $CFG, $course;
1260     if (!isset($options->trusttext)) {
1261         $options->trusttext = false;
1262     }
1264     if (!isset($options->noclean)) {
1265         $options->noclean=false;
1266     }
1267     if (!isset($options->smiley)) {
1268         $options->smiley=true;
1269     }
1270     if (!isset($options->filter)) {
1271         $options->filter=true;
1272     }
1273     if (!isset($options->para)) {
1274         $options->para=true;
1275     }
1276     if (!isset($options->newlines)) {
1277         $options->newlines=true;
1278     }
1280     if (empty($courseid)) {
1281         if (!empty($course->id)) {         // An ugly hack for better compatibility
1282             $courseid = $course->id;
1283         }
1284     }
1286     if (!empty($CFG->cachetext)) {
1287         $time = time() - $CFG->cachetext;
1288         $md5key = md5($text.'-'.$courseid.$options->noclean.$options->smiley.$options->filter.$options->para.$options->newlines.$format.current_language().$courseid.$options->trusttext);
1289         if ($oldcacheitem = get_record_sql('SELECT * FROM '.$CFG->prefix.'cache_text WHERE md5key = \''.$md5key.'\'', true)) {
1290             if ($oldcacheitem->timemodified >= $time) {
1291                 return $oldcacheitem->formattedtext;
1292             }
1293         }
1294     }
1296     // trusttext overrides the noclean option!
1297     if ($options->trusttext) {
1298         if (trusttext_present($text)) {
1299             $text = trusttext_strip($text);
1300             if (!empty($CFG->enabletrusttext)) {
1301                 $options->noclean = true;
1302             } else {
1303                 $options->noclean = false;
1304             }
1305         } else {
1306             $options->noclean = false;
1307         }
1308     }
1310     $CFG->currenttextiscacheable = true;   // Default status - can be changed by any filter
1312     switch ($format) {
1313         case FORMAT_HTML:
1314             if ($options->smiley) {
1315                 replace_smilies($text);
1316             }
1317             if (!$options->noclean) {
1318                 $text = clean_text($text, $format);
1319             }
1320             if ($options->filter) {
1321                 $text = filter_text($text, $courseid);
1322             }
1323             break;
1325         case FORMAT_PLAIN:
1326             $text = s($text);
1327             $text = rebuildnolinktag($text);
1328             $text = str_replace('  ', '&nbsp; ', $text);
1329             $text = nl2br($text);
1330             break;
1332         case FORMAT_WIKI:
1333             // this format is deprecated
1334             $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle.  You should not be seeing
1335                      this message as all texts should have been converted to Markdown format instead.
1336                      Please post a bug report to http://moodle.org/bugs with information about where you
1337                      saw this message.</p>'.s($text);
1338             break;
1340         case FORMAT_MARKDOWN:
1341             $text = markdown_to_html($text);
1342             if ($options->smiley) {
1343                 replace_smilies($text);
1344             }
1345             if (!$options->noclean) {
1346                 $text = clean_text($text, $format);
1347             }
1349             if ($options->filter) {
1350                 $text = filter_text($text, $courseid);
1351             }
1352             break;
1354         default:  // FORMAT_MOODLE or anything else
1355             $text = text_to_html($text, $options->smiley, $options->para, $options->newlines);
1356             if (!$options->noclean) {
1357                 $text = clean_text($text, $format);
1358             }
1360             if ($options->filter) {
1361                 $text = filter_text($text, $courseid);
1362             }
1363             break;
1364     }
1366     if (!empty($CFG->cachetext) and $CFG->currenttextiscacheable) {
1367         $newcacheitem->md5key = $md5key;
1368         $newcacheitem->formattedtext = addslashes($text);
1369         $newcacheitem->timemodified = time();
1370         if ($oldcacheitem) {                               // See bug 4677 for discussion
1371             $newcacheitem->id = $oldcacheitem->id;
1372             @update_record('cache_text', $newcacheitem);   // Update existing record in the cache table
1373                                                            // It's unlikely that the cron cache cleaner could have 
1374                                                            // deleted this entry in the meantime, as it allows
1375                                                            // some extra time to cover these cases.
1376         } else {
1377             @insert_record('cache_text', $newcacheitem);   // Insert a new record in the cache table
1378                                                            // Again, it's possible that another user has caused this
1379                                                            // record to be created already in the time that it took 
1380                                                            // to traverse this function.  That's OK too, as the 
1381                                                            // call above handles duplicate entries, and eventually
1382                                                            // the cron cleaner will delete them.
1383         }
1384     }
1386     return $text;
1389 /** Converts the text format from the value to the 'internal'
1390  *  name or vice versa. $key can either be the value or the name
1391  *  and you get the other back.
1392  *
1393  *  @param mixed int 0-4 or string one of 'moodle','html','plain','markdown'
1394  *  @return mixed as above but the other way around!
1395  */
1396 function text_format_name( $key ) {
1397   $lookup = array();
1398   $lookup[FORMAT_MOODLE] = 'moodle';
1399   $lookup[FORMAT_HTML] = 'html';
1400   $lookup[FORMAT_PLAIN] = 'plain';
1401   $lookup[FORMAT_MARKDOWN] = 'markdown';
1402   $value = "error";
1403   if (!is_numeric($key)) {
1404     $key = strtolower( $key );
1405     $value = array_search( $key, $lookup );
1406   }
1407   else {
1408     if (isset( $lookup[$key] )) {
1409       $value =  $lookup[ $key ];
1410     }
1411   }
1412   return $value;
1415 /** Given a simple string, this function returns the string
1416  *  processed by enabled filters if $CFG->filterall is enabled
1417  *
1418  *  @param string  $string     The string to be filtered.
1419  *  @param boolean $striplinks To strip any link in the result text.
1420  *  @param int     $courseid   Current course as filters can, potentially, use it
1421  *  @return string
1422  */
1423 function format_string ($string, $striplinks = false, $courseid=NULL ) {
1425     global $CFG, $course;
1427     //We'll use a in-memory cache here to speed up repeated strings
1428     static $strcache;
1430     //Calculate md5
1431     $md5 = md5($string.'<+>'.$striplinks);
1433     //Fetch from cache if possible
1434     if(isset($strcache[$md5])) {
1435         return $strcache[$md5];
1436     }
1438     if (empty($courseid)) {
1439         if (!empty($course->id)) {         // An ugly hack for better compatibility
1440             $courseid = $course->id;       // (copied from format_text)
1441         }
1442     }
1444     if (!empty($CFG->filterall)) {
1445         $string = filter_text($string, $courseid);
1446     }
1448     if ($striplinks) {  //strip links in string
1449         $string = preg_replace('/(<a[^>]+?>)(.+?)(<\/a>)/is','$2',$string);
1450     }
1452     //Store to cache
1453     $strcache[$md5] = $string;
1455     return $string;
1458 /**
1459  * Given text in a variety of format codings, this function returns
1460  * the text as plain text suitable for plain email.
1461  *
1462  * @uses FORMAT_MOODLE
1463  * @uses FORMAT_HTML
1464  * @uses FORMAT_PLAIN
1465  * @uses FORMAT_WIKI
1466  * @uses FORMAT_MARKDOWN
1467  * @param string $text The text to be formatted. This is raw text originally from user input.
1468  * @param int $format Identifier of the text format to be used
1469  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1470  * @return string
1471  */
1472 function format_text_email($text, $format) {
1474     switch ($format) {
1476         case FORMAT_PLAIN:
1477             return $text;
1478             break;
1480         case FORMAT_WIKI:
1481             $text = wiki_to_html($text);
1482         /// This expression turns links into something nice in a text format. (Russell Jungwirth)
1483         /// From: http://php.net/manual/en/function.eregi-replace.php and simplified
1484             $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1485             return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1486             break;
1488         case FORMAT_HTML:
1489             return html_to_text($text);
1490             break;
1492         case FORMAT_MOODLE:
1493         case FORMAT_MARKDOWN:
1494         default:
1495             $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1496             return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1497             break;
1498     }
1501 /**
1502  * Given some text in HTML format, this function will pass it
1503  * through any filters that have been defined in $CFG->textfilterx
1504  * The variable defines a filepath to a file containing the
1505  * filter function.  The file must contain a variable called
1506  * $textfilter_function which contains the name of the function
1507  * with $courseid and $text parameters
1508  *
1509  * @param string $text The text to be passed through format filters
1510  * @param int $courseid ?
1511  * @return string
1512  * @todo Finish documenting this function
1513  */
1514 function filter_text($text, $courseid=NULL) {
1515     global $CFG;
1517     require_once($CFG->libdir.'/filterlib.php');
1518     if (!empty($CFG->textfilters)) {
1519         $textfilters = explode(',', $CFG->textfilters);
1520         foreach ($textfilters as $textfilter) {
1521             if (is_readable($CFG->dirroot .'/'. $textfilter .'/filter.php')) {
1522                 include_once($CFG->dirroot .'/'. $textfilter .'/filter.php');
1523                 $functionname = basename($textfilter).'_filter';
1524                 if (function_exists($functionname)) {
1525                     $text = $functionname($courseid, $text);
1526                 }
1527             }
1528         }
1529     }
1531     /// <nolink> tags removed for XHTML compatibility
1532     $text = str_replace('<nolink>', '', $text);
1533     $text = str_replace('</nolink>', '', $text);
1535     return $text;
1538 /**
1539  * Is the text marked as trusted?
1540  *
1541  * @param string $text text to be searched for TRUSTTEXT marker
1542  * @return boolean
1543  */
1544 function trusttext_present($text) {
1545     if (strpos($text, TRUSTTEXT) !== FALSE) {
1546         return true;
1547     } else {
1548         return false;
1549     }
1552 /**
1553  * This funtion MUST be called before the cleaning or any other
1554  * function that modifies the data! We do not know the origin of trusttext
1555  * in database, if it gets there in tweaked form we must not convert it
1556  * to supported form!!!
1557  *
1558  * Please be carefull not to use stripslashes on data from database
1559  * or twice stripslashes when processing data recieved from user.
1560  *
1561  * @param string $text text that may contain TRUSTTEXT marker 
1562  * @return text without any TRUSTTEXT marker
1563  */
1564 function trusttext_strip($text) {
1565     global $CFG;
1567     while (true) { //removing nested TRUSTTEXT
1568         $orig = $text; 
1569         $text = str_replace(TRUSTTEXT, '', $text);
1570         if (strcmp($orig, $text) === 0) {
1571             return $text;
1572         }
1573     }
1576 /**
1577  * Mark text as trusted, such text may contain any HTML tags because the
1578  * normal text cleaning will be bypassed.
1579  * Please make sure that the text comes from trusted user before storing
1580  * it into database!
1581  */
1582 function trusttext_mark($text) {
1583     global $CFG;
1584     if (!empty($CFG->enabletrusttext) and (strpos($text, TRUSTTEXT) === FALSE)) {
1585         return TRUSTTEXT.$text;
1586     } else {
1587         return $text;
1588     }
1590 function trusttext_after_edit(&$text, $context) {
1591     if (has_capability('moodle/site:trustcontent', $context)) {
1592         $text = trusttext_strip($text);
1593         $text = trusttext_mark($text);
1594     } else {
1595         $text = trusttext_strip($text);
1596     }
1599 function trusttext_prepare_edit(&$text, &$format, $usehtmleditor, $context) {
1600     global $CFG;
1602     $options = new object();
1603     $options->smiley = false;
1604     $options->filter = false;
1605     if (!empty($CFG->enabletrusttext)
1606          and has_capability('moodle/site:trustcontent', $context)
1607          and trusttext_present($text)) {
1608         $options->noclean = true;
1609     } else {
1610         $options->noclean = false;
1611     }
1612     $text = trusttext_strip($text);
1613     if ($usehtmleditor) {
1614         $text = format_text($text, $format, $options);
1615         $format = FORMAT_HTML;
1616     } else if (!$options->noclean){
1617         $text = clean_text($text, $format);
1618     }
1621 /**
1622  * Given raw text (eg typed in by a user), this function cleans it up
1623  * and removes any nasty tags that could mess up Moodle pages.
1624  *
1625  * @uses FORMAT_MOODLE
1626  * @uses FORMAT_PLAIN
1627  * @uses ALLOWED_TAGS
1628  * @param string $text The text to be cleaned
1629  * @param int $format Identifier of the text format to be used
1630  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1631  * @return string The cleaned up text
1632  */
1633 function clean_text($text, $format=FORMAT_MOODLE) {
1635     global $ALLOWED_TAGS;
1637     switch ($format) {
1638         case FORMAT_PLAIN:
1639             return $text;
1641         default:
1643         /// Fix non standard entity notations
1644             $text = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $text);
1645             $text = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $text);
1647         /// Remove tags that are not allowed
1648             $text = strip_tags($text, $ALLOWED_TAGS);
1650         /// Clean up embedded scripts and , using kses
1651             $text = cleanAttributes($text);
1653         /// Remove script events
1654             $text = eregi_replace("([^a-z])language([[:space:]]*)=", "\\1Xlanguage=", $text);
1655             $text = eregi_replace("([^a-z])on([a-z]+)([[:space:]]*)=", "\\1Xon\\2=", $text);
1657             return $text;
1658     }
1661 /**
1662  * This function takes a string and examines it for HTML tags.
1663  * If tags are detected it passes the string to a helper function {@link cleanAttributes2()}
1664  *  which checks for attributes and filters them for malicious content
1665  *         17/08/2004              ::          Eamon DOT Costello AT dcu DOT ie
1666  *
1667  * @param string $str The string to be examined for html tags
1668  * @return string
1669  */
1670 function cleanAttributes($str){
1671     $result = preg_replace_callback(
1672             '%(<[^>]*(>|$)|>)%m', #search for html tags
1673             "cleanAttributes2",
1674             $str
1675             );
1676     return  $result;
1679 /**
1680  * This function takes a string with an html tag and strips out any unallowed
1681  * protocols e.g. javascript:
1682  * It calls ancillary functions in kses which are prefixed by kses
1683 *        17/08/2004              ::          Eamon DOT Costello AT dcu DOT ie
1684  *
1685  * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st
1686  *              element the html to be cleared
1687  * @return string
1688  */
1689 function cleanAttributes2($htmlArray){
1691     global $CFG, $ALLOWED_PROTOCOLS;
1692     require_once($CFG->libdir .'/kses.php');
1694     $htmlTag = $htmlArray[1];
1695     if (substr($htmlTag, 0, 1) != '<') {
1696         return '&gt;';  //a single character ">" detected
1697     }
1698     if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) {
1699         return ''; // It's seriously malformed
1700     }
1701     $slash = trim($matches[1]); //trailing xhtml slash
1702     $elem = $matches[2];    //the element name
1703     $attrlist = $matches[3]; // the list of attributes as a string
1705     $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
1707     $attStr = '';
1708     foreach ($attrArray as $arreach) {
1709         $arreach['name'] = strtolower($arreach['name']);
1710         if ($arreach['name'] == 'style') {
1711             $value = $arreach['value'];
1712             while (true) {
1713                 $prevvalue = $value;
1714                 $value = kses_no_null($value);
1715                 $value = preg_replace("/\/\*.*\*\//Us", '', $value);
1716                 $value = kses_decode_entities($value);
1717                 $value = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $value);
1718                 $value = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $value);
1719                 if ($value === $prevvalue) {
1720                     $arreach['value'] = $value;
1721                     break;
1722                 }
1723             }
1724             $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']);
1725             $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']);
1726         }
1727         $attStr .=  ' '.$arreach['name'].'="'.$arreach['value'].'" ';
1728     }
1730     // Remove last space from attribute list
1731     $attStr = rtrim($attStr);
1733     $xhtml_slash = '';
1734     if (preg_match('%/\s*$%', $attrlist)) {
1735         $xhtml_slash = ' /';
1736     }
1737     return '<'. $slash . $elem . $attStr . $xhtml_slash .'>';
1740 /**
1741  * Replaces all known smileys in the text with image equivalents
1742  *
1743  * @uses $CFG
1744  * @param string $text Passed by reference. The string to search for smily strings.
1745  * @return string
1746  */
1747 function replace_smilies(&$text) {
1748 ///
1749     global $CFG;
1751 /// this builds the mapping array only once
1752     static $runonce = false;
1753     static $e = array();
1754     static $img = array();
1755     static $emoticons = array(
1756         ':-)'  => 'smiley',
1757         ':)'   => 'smiley',
1758         ':-D'  => 'biggrin',
1759         ';-)'  => 'wink',
1760         ':-/'  => 'mixed',
1761         'V-.'  => 'thoughtful',
1762         ':-P'  => 'tongueout',
1763         'B-)'  => 'cool',
1764         '^-)'  => 'approve',
1765         '8-)'  => 'wideeyes',
1766         ':o)'  => 'clown',
1767         ':-('  => 'sad',
1768         ':('   => 'sad',
1769         '8-.'  => 'shy',
1770         ':-I'  => 'blush',
1771         ':-X'  => 'kiss',
1772         '8-o'  => 'surprise',
1773         'P-|'  => 'blackeye',
1774         '8-['  => 'angry',
1775         'xx-P' => 'dead',
1776         '|-.'  => 'sleepy',
1777         '}-]'  => 'evil',
1778         );
1780     if ($runonce == false) {  /// After the first time this is not run again
1781         foreach ($emoticons as $emoticon => $image){
1782             $alttext = get_string($image, 'pix');
1784             $e[] = $emoticon;
1785             $img[] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $CFG->pixpath .'/s/'. $image .'.gif" />';
1786         }
1787         $runonce = true;
1788     }
1790     // Exclude from transformations all the code inside <script> tags
1791     // Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
1792     // Based on code from glossary fiter by Williams Castillo.
1793     //       - Eloy
1795     // Detect all the <script> zones to take out
1796     $excludes = array();
1797     preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
1799     // Take out all the <script> zones from text
1800     foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
1801         $excludes['<+'.$key.'+>'] = $value;
1802     }
1803     if ($excludes) {
1804         $text = str_replace($excludes,array_keys($excludes),$text);
1805     }
1807 /// this is the meat of the code - this is run every time
1808     $text = str_replace($e, $img, $text);
1810     // Recover all the <script> zones to text
1811     if ($excludes) {
1812         $text = str_replace(array_keys($excludes),$excludes,$text);
1813     }
1816 /**
1817  * Given plain text, makes it into HTML as nicely as possible.
1818  * May contain HTML tags already
1819  *
1820  * @uses $CFG
1821  * @param string $text The string to convert.
1822  * @param boolean $smiley Convert any smiley characters to smiley images?
1823  * @param boolean $para If true then the returned string will be wrapped in paragraph tags
1824  * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
1825  * @return string
1826  */
1828 function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
1829 ///
1831     global $CFG;
1833 /// Remove any whitespace that may be between HTML tags
1834     $text = eregi_replace(">([[:space:]]+)<", "><", $text);
1836 /// Remove any returns that precede or follow HTML tags
1837     $text = eregi_replace("([\n\r])<", " <", $text);
1838     $text = eregi_replace(">([\n\r])", "> ", $text);
1840     convert_urls_into_links($text);
1842 /// Make returns into HTML newlines.
1843     if ($newlines) {
1844         $text = nl2br($text);
1845     }
1847 /// Turn smileys into images.
1848     if ($smiley) {
1849         replace_smilies($text);
1850     }
1852 /// Wrap the whole thing in a paragraph tag if required
1853     if ($para) {
1854         return '<p>'.$text.'</p>';
1855     } else {
1856         return $text;
1857     }
1860 /**
1861  * Given Markdown formatted text, make it into XHTML using external function
1862  *
1863  * @uses $CFG
1864  * @param string $text The markdown formatted text to be converted.
1865  * @return string Converted text
1866  */
1867 function markdown_to_html($text) {
1868     global $CFG;
1870     require_once($CFG->libdir .'/markdown.php');
1872     return Markdown($text);
1875 /**
1876  * Given HTML text, make it into plain text using external function
1877  *
1878  * @uses $CFG
1879  * @param string $html The text to be converted.
1880  * @return string
1881  */
1882 function html_to_text($html) {
1884     global $CFG;
1886     require_once($CFG->libdir .'/html2text.php');
1888     return html2text($html);
1891 /**
1892  * Given some text this function converts any URLs it finds into HTML links
1893  *
1894  * @param string $text Passed in by reference. The string to be searched for urls.
1895  */
1896 function convert_urls_into_links(&$text) {
1897 /// Make lone URLs into links.   eg http://moodle.com/
1898     $text = eregi_replace("([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])",
1899                           "\\1<a href=\"\\2://\\3\\4\" target=\"_blank\">\\2://\\3\\4</a>", $text);
1901 /// eg www.moodle.com
1902     $text = eregi_replace("([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])",
1903                           "\\1<a href=\"http://www.\\2\\3\" target=\"_blank\">www.\\2\\3</a>", $text);
1906 /**
1907  * This function will highlight search words in a given string
1908  * It cares about HTML and will not ruin links.  It's best to use
1909  * this function after performing any conversions to HTML.
1910  * Function found here: http://forums.devshed.com/t67822/scdaa2d1c3d4bacb4671d075ad41f0854.html
1911  *
1912  * @param string $needle The string to search for
1913  * @param string $haystack The string to search for $needle in
1914  * @param int $case ?
1915  * @return string
1916  * @todo Finish documenting this function
1917  */
1918 function highlight($needle, $haystack, $case=0,
1919                     $left_string='<span class="highlight">', $right_string='</span>') {
1920     if (empty($needle)) {
1921         return $haystack;
1922     }
1924     //$list_of_words = eregi_replace("[^-a-zA-Z0-9&.']", " ", $needle);  // bug 3101
1925     $list_of_words = $needle;
1926     $list_array = explode(' ', $list_of_words);
1927     for ($i=0; $i<sizeof($list_array); $i++) {
1928         if (strlen($list_array[$i]) == 1) {
1929             $list_array[$i] = '';
1930         }
1931     }
1932     $list_of_words = implode(' ', $list_array);
1933     $list_of_words_cp = $list_of_words;
1934     $final = array();
1935     preg_match_all('/<(.+?)>/is',$haystack,$list_of_words);
1937     foreach (array_unique($list_of_words[0]) as $key=>$value) {
1938         $final['<|'.$key.'|>'] = $value;
1939     }
1941     $haystack = str_replace($final,array_keys($final),$haystack);
1942     $list_of_words_cp = eregi_replace(' +', '|', $list_of_words_cp);
1944     if ($list_of_words_cp{0}=='|') {
1945         $list_of_words_cp{0} = '';
1946     }
1947     if ($list_of_words_cp{strlen($list_of_words_cp)-1}=='|') {
1948         $list_of_words_cp{strlen($list_of_words_cp)-1}='';
1949     }
1951     $list_of_words_cp = trim($list_of_words_cp);
1953     if ($list_of_words_cp) {
1955       $list_of_words_cp = "(". $list_of_words_cp .")";
1957       if (!$case){
1958         $haystack = eregi_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1959       } else {
1960         $haystack = ereg_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1961       }
1962     }
1963     $haystack = str_replace(array_keys($final),$final,$haystack);
1965     return $haystack;
1968 /**
1969  * This function will highlight instances of $needle in $haystack
1970  * It's faster that the above function and doesn't care about
1971  * HTML or anything.
1972  *
1973  * @param string $needle The string to search for
1974  * @param string $haystack The string to search for $needle in
1975  * @return string
1976  */
1977 function highlightfast($needle, $haystack) {
1979     $parts = explode(strtolower($needle), strtolower($haystack));
1981     $pos = 0;
1983     foreach ($parts as $key => $part) {
1984         $parts[$key] = substr($haystack, $pos, strlen($part));
1985         $pos += strlen($part);
1987         $parts[$key] .= '<span class="highlight">'.substr($haystack, $pos, strlen($needle)).'</span>';
1988         $pos += strlen($needle);
1989     }
1991     return (join('', $parts));
1995 /// STANDARD WEB PAGE PARTS ///////////////////////////////////////////////////
1997 /**
1998  * Print a standard header
1999  *
2000  * @uses $USER
2001  * @uses $CFG
2002  * @uses $SESSION
2003  * @param string $title Appears at the top of the window
2004  * @param string $heading Appears at the top of the page
2005  * @param string $navigation Premade navigation string (for use as breadcrumbs links)
2006  * @param string $focus Indicates form element to get cursor focus on load eg  inputform.password
2007  * @param string $meta Meta tags to be added to the header
2008  * @param boolean $cache Should this page be cacheable?
2009  * @param string $button HTML code for a button (usually for module editing)
2010  * @param string $menu HTML code for a popup menu
2011  * @param boolean $usexml use XML for this page
2012  * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
2013  * @param bool   $return If true, return the visible elements of the header instead of echoing them.
2014  */
2015 function print_header ($title='', $heading='', $navigation='', $focus='',
2016                        $meta='', $cache=true, $button='&nbsp;', $menu='',
2017                        $usexml=false, $bodytags='', $return=false) {
2019     global $USER, $CFG, $THEME, $SESSION, $ME, $SITE, $HTTPSPAGEREQUIRED;
2021 /// This makes sure that the header is never repeated twice on a page
2022     if (defined('HEADER_PRINTED')) {
2023         if ($CFG->debug > 7) {
2024             notify('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().');
2025         }
2026         return;
2027     }
2028     define('HEADER_PRINTED', 'true');
2031     global $course, $COURSE;
2032     if (!empty($COURSE->lang)) {
2033         $CFG->courselang = $COURSE->lang;
2034         moodle_setlocale();
2035     } else if (!empty($course->lang)) { // ugly backwards compatibility hack
2036         $CFG->courselang = $course->lang;
2037         moodle_setlocale();
2038     }
2039     if (!empty($COURSE->theme)) {
2040         if (!empty($CFG->allowcoursethemes)) {
2041             $CFG->coursetheme = $COURSE->theme;
2042             theme_setup();
2043         }
2044     } else if (!empty($course->theme)) { // ugly backwards compatibility hack
2045         if (!empty($CFG->allowcoursethemes)) {
2046             $CFG->coursetheme = $course->theme;
2047             theme_setup();
2048         }
2049     }
2051 /// We have to change some URLs in styles if we are in a $HTTPSPAGEREQUIRED page
2052     if (!empty($HTTPSPAGEREQUIRED)) {
2053         $CFG->themewww = str_replace('http:', 'https:', $CFG->themewww);
2054         $CFG->pixpath = str_replace('http:', 'https:', $CFG->pixpath);
2055         $CFG->modpixpath = str_replace('http:', 'https:', $CFG->modpixpath);
2056         foreach ($CFG->stylesheets as $key => $stylesheet) {
2057             $CFG->stylesheets[$key] = str_replace('http:', 'https:', $stylesheet);
2058         }
2059     }
2061 /// Add the required stylesheets
2062     $stylesheetshtml = '';
2063     foreach ($CFG->stylesheets as $stylesheet) {
2064         $stylesheetshtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
2065     }
2066     $meta = $stylesheetshtml.$meta;
2069     if ($navigation == 'home') {
2070         $home = true;
2071         $navigation = '';
2072     } else {
2073         $home = false;
2074     }
2076 /// This is another ugly hack to make navigation elements available to print_footer later
2077     $THEME->title      = $title;
2078     $THEME->heading    = $heading;
2079     $THEME->navigation = $navigation;
2080     $THEME->button     = $button;
2081     $THEME->menu       = $menu;
2082     $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
2084     if ($button == '') {
2085         $button = '&nbsp;';
2086     }
2088     if (!$menu and $navigation) {
2089         if (empty($CFG->loginhttps)) {
2090             $wwwroot = $CFG->wwwroot;
2091         } else {
2092             $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
2093         }
2094         if (isset($course->id)) {
2095             $menu = user_login_string($course);
2096         } else {
2097             $menu = user_login_string($SITE);
2098         }
2099     }
2101     if (isset($SESSION->justloggedin)) {
2102         unset($SESSION->justloggedin);
2103         if (!empty($CFG->displayloginfailures)) {
2104             if (!empty($USER->username) and !isguest()) {
2105                 if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
2106                     $menu .= '&nbsp;<font size="1">';
2107                     if (empty($count->accounts)) {
2108                         $menu .= get_string('failedloginattempts', '', $count);
2109                     } else {
2110                         $menu .= get_string('failedloginattemptsall', '', $count);
2111                     }
2112                     if (isadmin()) {
2113                         $menu .= ' (<a href="'.$CFG->wwwroot.'/course/report/log/index.php'.
2114                                              '?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
2115                     }
2116                     $menu .= '</font>';
2117                 }
2118             }
2119         }
2120     }
2123     $encoding = current_charset();
2125     $meta = '<meta http-equiv="content-type" content="text/html; charset='. $encoding .'" />'. "\n". $meta ."\n";
2126     if (!$usexml) {
2127         @header('Content-type: text/html; charset='.$encoding);
2128     }
2130     if ( get_string('thisdirection') == 'rtl' ) {
2131         $direction = ' dir="rtl"';
2132     } else {
2133         $direction = ' dir="ltr"';
2134     }
2135     //Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag.
2136     $language = str_replace('_utf8','',$CFG->lang);
2137     $direction .= ' lang="'.$language.'" xml:lang="'.$language.'"';
2139     if ($cache) {  // Allow caching on "back" (but not on normal clicks)
2140         @header('Cache-Control: private, pre-check=0, post-check=0, max-age=0');
2141         @header('Pragma: no-cache');
2142         @header('Expires: ');
2143     } else {       // Do everything we can to always prevent clients and proxies caching
2144         @header('Cache-Control: no-store, no-cache, must-revalidate');
2145         @header('Cache-Control: post-check=0, pre-check=0', false);
2146         @header('Pragma: no-cache');
2147         @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
2148         @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
2150         $meta .= "\n<meta http-equiv=\"pragma\" content=\"no-cache\" />";
2151         $meta .= "\n<meta http-equiv=\"expires\" content=\"0\" />";
2152     }
2153     @header('Accept-Ranges: none');
2155     if ($usexml) {       // Added by Gustav Delius / Mad Alex for MathML output
2156                          // Modified by Julian Sedding
2157         $currentlanguage = current_language();
2158         $mathplayer = preg_match("/MathPlayer/i", $_SERVER['HTTP_USER_AGENT']);
2159         if(!$mathplayer) {
2160             header('Content-Type: application/xhtml+xml');
2161         }
2162         echo '<?xml version="1.0" ?>'."\n";
2163         if (!empty($CFG->xml_stylesheets)) {
2164             $stylesheets = explode(';', $CFG->xml_stylesheets);
2165             foreach ($stylesheets as $stylesheet) {
2166                 echo '<?xml-stylesheet type="text/xsl" href="'. $CFG->wwwroot .'/'. $stylesheet .'" ?>' . "\n";
2167             }
2168         }
2169         echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1';
2170         if (!empty($CFG->xml_doctype_extra)) {
2171             echo ' plus '. $CFG->xml_doctype_extra;
2172         }
2173         echo '//' . strtoupper($currentlanguage) . '" "'. $CFG->xml_dtd .'">'."\n";
2174         $direction = " xmlns=\"http://www.w3.org/1999/xhtml\"
2175                        xmlns:math=\"http://www.w3.org/1998/Math/MathML\"
2176                        xmlns:xlink=\"http://www.w3.org/1999/xlink\"
2177                        $direction";
2178         if($mathplayer) {
2179             $meta .= '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n";
2180             $meta .= '<!--comment required to prevent this becoming an empty tag-->'."\n";
2181             $meta .= '</object>'."\n";
2182             $meta .= '<?import namespace="math" implementation="#mathplayer" ?>' . "\n";
2183         }
2184     }
2186     // Clean up the title
2188     $title = str_replace('"', '&quot;', $title);
2189     $title = strip_tags($title);
2191     // Create class and id for this page
2193     page_id_and_class($pageid, $pageclass);
2195     if (isset($course->id)) {
2196         $pageclass .= ' course-'.$course->id;
2197     } else {
2198         $pageclass .= ' course-'.SITEID;
2199     }
2201     if (!empty($USER->editing)) {
2202         $pageclass .= ' editing';
2203     }
2205     $bodytags .= ' class="'.$pageclass.'" id="'.$pageid.'"';
2207     ob_start();
2208     include ($CFG->themedir.current_theme().'/header.html');
2209     $output = ob_get_contents();
2210     ob_end_clean();
2212     if (!empty($CFG->messaging)) {
2213         $output .= message_popup_window();
2214     }
2216     if ($return) {
2217         return $output;
2218     } else {
2219         echo $output;
2220     }
2223 /**
2224  * This version of print_header is simpler because the course name does not have to be
2225  * provided explicitly in the strings. It can be used on the site page as in courses
2226  * Eventually all print_header could be replaced by print_header_simple
2227  *
2228  * @param string $title Appears at the top of the window
2229  * @param string $heading Appears at the top of the page
2230  * @param string $navigation Premade navigation string (for use as breadcrumbs links)
2231  * @param string $focus Indicates form element to get cursor focus on load eg  inputform.password
2232  * @param string $meta Meta tags to be added to the header
2233  * @param boolean $cache Should this page be cacheable?
2234  * @param string $button HTML code for a button (usually for module editing)
2235  * @param string $menu HTML code for a popup menu
2236  * @param boolean $usexml use XML for this page
2237  * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
2238  * @param bool   $return If true, return the visible elements of the header instead of echoing them.
2239  */
2240 function print_header_simple($title='', $heading='', $navigation='', $focus='', $meta='',
2241                        $cache=true, $button='&nbsp;', $menu='', $usexml=false, $bodytags='', $return=false) {
2243     global $course,$CFG;                // The same hack is used in print_header
2245     $shortname ='';
2246     if ($course->category) {
2247         $shortname = '<a href="'.$CFG->wwwroot.'/course/view.php?id='. $course->id .'">'. $course->shortname .'</a> ->';
2248     }
2250     $output = print_header($course->shortname .': '. $title, $course->fullname .' '. $heading, $shortname .' '. $navigation, $focus, $meta,
2251                            $cache, $button, $menu, $usexml, $bodytags, true);
2253     if ($return) {
2254         return $output;
2255     } else {
2256         echo $output;
2257     }
2261 /**
2262  * Can provide a course object to make the footer contain a link to
2263  * to the course home page, otherwise the link will go to the site home
2264  *
2265  * @uses $CFG
2266  * @uses $USER
2267  * @param course $course {@link $COURSE} object containing course information
2268  * @param ? $usercourse ?
2269  * @todo Finish documenting this function
2270  */
2271 function print_footer($course=NULL, $usercourse=NULL, $return=false) {
2272     global $USER, $CFG, $THEME;
2274 /// Course links
2275     if ($course) {
2276         if (is_string($course) && $course == 'none') {          // Don't print any links etc
2277             $homelink = '';
2278             $loggedinas = '';
2279             $home  = false;
2280         } else if (is_string($course) && $course == 'home') {   // special case for site home page - please do not remove
2281             $course = get_site();
2282             $homelink  = '<div class="sitelink">'.
2283                '<a title="moodle '. $CFG->release .' ('. $CFG->version .')" href="http://moodle.org/" target="_blank">'.
2284                '<br /><img width="100" height="30" src="pix/moodlelogo.gif" border="0" alt="moodlelogo" /></a></div>';
2285             $home  = true;
2286         } else {
2287             $homelink = '<div class="homelink"><a target="'.$CFG->framename.'" href="'.$CFG->wwwroot.
2288                         '/course/view.php?id='.$course->id.'">'.$course->shortname.'</a></div>';
2289             $home  = false;
2290         }
2291     } else {
2292         $course = get_site();  // Set course as site course by default
2293         $homelink = '<div class="homelink"><a target="'.$CFG->framename.'" href="'.$CFG->wwwroot.'/">'.get_string('home').'</a></div>';
2294         $home  = false;
2295     }
2297 /// Set up some other navigation links (passed from print_header by ugly hack)
2298     $menu        = isset($THEME->menu) ? str_replace('navmenu', 'navmenufooter', $THEME->menu) : '';
2299     $title       = isset($THEME->title) ? $THEME->title : '';
2300     $button      = isset($THEME->button) ? $THEME->button : '';
2301     $heading     = isset($THEME->heading) ? $THEME->heading : '';
2302     $navigation  = isset($THEME->navigation) ? $THEME->navigation : '';
2303     $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
2306 /// Set the user link if necessary
2307     if (!$usercourse and is_object($course)) {
2308         $usercourse = $course;
2309     }
2311     if (!isset($loggedinas)) {
2312         $loggedinas = user_login_string($usercourse, $USER);
2313     }
2315     if ($loggedinas == $menu) {
2316         $menu = '';
2317     }
2319 /// Provide some performance info if required
2320     $performanceinfo = '';
2321     if (defined('MDL_PERF') || $CFG->perfdebug > 7) {
2322         $perf = get_performance_info();
2323         if (defined('MDL_PERFTOLOG')) {
2324             error_log("PERF: " . $perf['txt']);
2325         }
2326         if (defined('MDL_PERFTOFOOT') || $CFG->debug > 7 || $CFG->perfdebug > 7) {
2327             $performanceinfo = $perf['html'];
2328         }
2329     }
2332 /// Include the actual footer file
2334     ob_start();
2335     include ($CFG->themedir.current_theme().'/footer.html');
2336     $output = ob_get_contents();
2337     ob_end_clean();
2339     if ($return) {
2340         return $output;
2341     } else {
2342         echo $output;
2343     }
2346 /**
2347  * Returns the name of the current theme
2348  *
2349  * @uses $CFG
2350  * @param $USER
2351  * @param $SESSION
2352  * @return string
2353  */
2354 function current_theme() {
2355     global $CFG, $USER, $SESSION, $course;
2357     if (!empty($CFG->pagetheme)) {  // Page theme is for special page-only themes set by code
2358         return $CFG->pagetheme;
2360     } else if (!empty($CFG->coursetheme) and !empty($CFG->allowcoursethemes)) {  // Course themes override others
2361         return $CFG->coursetheme;
2363     } else if (!empty($SESSION->theme)) {    // Session theme can override other settings
2364         return $SESSION->theme;
2366     } else if (!empty($USER->theme) and !empty($CFG->allowuserthemes)) {    // User theme can override site theme
2367         return $USER->theme;
2369     } else {
2370         return $CFG->theme;
2371     }
2375 /**
2376  * This function is called by stylesheets to set up the header
2377  * approriately as well as the current path
2378  *
2379  * @uses $CFG
2380  * @param int $lastmodified ?
2381  * @param int $lifetime ?
2382  * @param string $thename ?
2383  */
2384 function style_sheet_setup($lastmodified=0, $lifetime=300, $themename='', $forceconfig='', $lang='') {
2386     global $CFG, $THEME;
2388     // Fix for IE6 caching - we don't want the filemtime('styles.php'), instead use now.
2389     $lastmodified = time();
2391     header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmodified) . ' GMT');
2392     header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
2393     header('Cache-Control: max-age='. $lifetime);
2394     header('Pragma: ');
2395     header('Content-type: text/css');  // Correct MIME type
2397     $DEFAULT_SHEET_LIST = array('styles_layout', 'styles_fonts', 'styles_color');
2399     if (empty($themename)) {
2400         $themename = current_theme();  // So we have something.  Normally not needed.
2401     } else {
2402         $themename = clean_param($themename, PARAM_SAFEDIR);
2403     }
2405     if (!empty($forceconfig)) {        // Page wants to use the config from this theme instead
2406         unset($THEME);
2407         include($CFG->themedir.$forceconfig.'/'.'config.php');
2408     }
2410 /// If this is the standard theme calling us, then find out what sheets we need
2412     if ($themename == 'standard') {
2413         if (!isset($THEME->standardsheets) or $THEME->standardsheets === true) { // Use all the sheets we have
2414             $THEME->sheets = $DEFAULT_SHEET_LIST;
2415         } else if (empty($THEME->standardsheets)) {                              // We can stop right now!
2416             echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2417             exit;
2418         } else {                                                                 // Use the provided subset only
2419             $THEME->sheets = $THEME->standardsheets;
2420         }
2422 /// If we are a parent theme, then check for parent definitions
2424     } else if (!empty($THEME->parent) && $themename == $THEME->parent) {
2425         if (!isset($THEME->parentsheets) or $THEME->parentsheets === true) {     // Use all the sheets we have
2426             $THEME->sheets = $DEFAULT_SHEET_LIST;
2427         } else if (empty($THEME->parentsheets)) {                                // We can stop right now!
2428             echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2429             exit;
2430         } else {                                                                 // Use the provided subset only
2431             $THEME->sheets = $THEME->parentsheets;
2432         }
2433     }
2435 /// Work out the last modified date for this theme
2437     foreach ($THEME->sheets as $sheet) {
2438         if (file_exists($CFG->themedir.$themename.'/'.$sheet.'.css')) {
2439             $sheetmodified = filemtime($CFG->themedir.$themename.'/'.$sheet.'.css');
2440             if ($sheetmodified > $lastmodified) {
2441                 $lastmodified = $sheetmodified;
2442             }
2443         }
2444     }
2447 /// Get a list of all the files we want to include
2448     $files = array();
2450     foreach ($THEME->sheets as $sheet) {
2451         $files[] = array($CFG->themedir, $themename.'/'.$sheet.'.css');
2452     }
2454     if ($themename == 'standard') {          // Add any standard styles included in any modules
2455         if (!empty($THEME->modsheets)) {     // Search for styles.php within activity modules
2456             if ($mods = get_list_of_plugins('mod')) {
2457                 foreach ($mods as $mod) {
2458                     if (file_exists($CFG->dirroot.'/mod/'.$mod.'/styles.php')) {
2459                         $files[] = array($CFG->dirroot, '/mod/'.$mod.'/styles.php');
2460                     }
2461                 }
2462             }
2463         }
2465         if (!empty($THEME->blocksheets)) {     // Search for styles.php within block modules
2466             if ($mods = get_list_of_plugins('blocks')) {
2467                 foreach ($mods as $mod) {
2468                     if (file_exists($CFG->dirroot.'/blocks/'.$mod.'/styles.php')) {
2469                         $files[] = array($CFG->dirroot, '/blocks/'.$mod.'/styles.php');
2470                     }
2471                 }
2472             }
2473         }
2475         if (!empty($THEME->langsheets)) {     // Search for styles.php within the current language
2476             if (file_exists($CFG->dirroot.'/lang/'.$lang.'/styles.php')) {
2477                 $files[] = array($CFG->dirroot, '/lang/'.$lang.'/styles.php');
2478             }
2479         }
2480     }
2483     if ($files) {
2484     /// Produce a list of all the files first
2485         echo '/**************************************'."\n";
2486         echo ' * THEME NAME: '.$themename."\n *\n";
2487         echo ' * Files included in this sheet:'."\n *\n";
2488         foreach ($files as $file) {
2489             echo ' *   '.$file[1]."\n";
2490         }
2491         echo ' **************************************/'."\n\n";
2494     /// Actually output all the files in order.
2495         foreach ($files as $file) {
2496             echo '/***** '.$file[1].' start *****/'."\n\n";
2497             @include_once($file[0].$file[1]);
2498             echo '/***** '.$file[1].' end *****/'."\n\n";
2499         }
2500     }
2502     return $CFG->themewww.$themename;   // Only to help old themes (1.4 and earlier)
2506 function theme_setup($theme = '', $params=NULL) {
2507 /// Sets up global variables related to themes
2509     global $CFG, $THEME, $SESSION, $USER;
2511     if (empty($theme)) {
2512         $theme = current_theme();
2513     }
2515 /// Load up the theme config
2516     $THEME = NULL;   // Just to be sure
2517     include($CFG->themedir. $theme .'/config.php');  // Main config for current theme
2519 /// Put together the parameters
2520     if (!$params) {
2521         $params = array();
2522     }
2523     if ($theme != $CFG->theme) {
2524         $params[] = 'forceconfig='.$theme;
2525     }
2527 /// Force language too if required
2528     if (!empty($THEME->langsheets)) {
2529         $params[] = 'lang='.current_language();
2530     }
2532 /// Convert params to string
2533     if ($params) {
2534         $paramstring = '?'.implode('&', $params);
2535     } else {
2536         $paramstring = '';
2537     }
2539 /// Set up image paths
2540     if (empty($THEME->custompix)) {    // Could be set in the above file
2541         $CFG->pixpath = $CFG->wwwroot .'/pix';
2542         $CFG->modpixpath = $CFG->wwwroot .'/mod';
2543     } else {
2544         $CFG->pixpath = $CFG->themewww . $theme .'/pix';
2545         $CFG->modpixpath = $CFG->themewww . $theme .'/pix/mod';
2546     }
2548 /// Header and footer paths
2549     $CFG->header = $CFG->themedir . $theme .'/header.html';
2550     $CFG->footer = $CFG->themedir . $theme .'/footer.html';
2552 /// Define stylesheet loading order
2553     $CFG->stylesheets = array();
2554     if ($theme != 'standard') {    /// The standard sheet is always loaded first
2555         $CFG->stylesheets[] = $CFG->themewww.'standard/styles.php'.$paramstring;
2556     }
2557     if (!empty($THEME->parent)) {  /// Parent stylesheets are loaded next
2558         $CFG->stylesheets[] = $CFG->themewww.$THEME->parent.'/styles.php'.$paramstring;
2559     }
2560     $CFG->stylesheets[] = $CFG->themewww.$theme.'/styles.php'.$paramstring;
2565 /**
2566  * Returns text to be displayed to the user which reflects their login status
2567  *
2568  * @uses $CFG
2569  * @uses $USER
2570  * @param course $course {@link $COURSE} object containing course information
2571  * @param user $user {@link $USER} object containing user information
2572  * @return string
2573  */
2574 function user_login_string($course=NULL, $user=NULL) {
2575     global $USER, $CFG, $SITE;
2577     if (empty($user) and !empty($USER->id)) {
2578         $user = $USER;
2579     }
2581     if (empty($course)) {
2582         $course = $SITE;
2583     }
2585     if (isset($user->realuser)) {
2586         if ($realuser = get_record('user', 'id', $user->realuser)) {
2587             $fullname = fullname($realuser, true);
2588             $realuserinfo = " [<a target=\"{$CFG->framename}\"
2589             href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&amp;return=1\">$fullname</a>] ";
2590         }
2591     } else {
2592         $realuserinfo = '';
2593     }
2595     if (empty($CFG->loginhttps)) {
2596         $wwwroot = $CFG->wwwroot;
2597     } else {
2598         $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
2599     }
2601     if (isset($user->id) and $user->id) {
2602         $fullname = fullname($user, true);
2603         $username = "<a target=\"{$CFG->framename}\" href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>";
2604         if (isguest($user->id)) {
2605             $loggedinas = $realuserinfo.get_string('loggedinasguest').
2606                       " (<a target=\"{$CFG->framename}\" href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
2607         } else {
2608             $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '.
2609                       " (<a target=\"{$CFG->framename}\" href=\"$CFG->wwwroot/login/logout.php\">".get_string('logout').'</a>)';
2610         }
2611     } else {
2612         $loggedinas = get_string('loggedinnot', 'moodle').
2613                       " (<a target=\"{$CFG->framename}\" href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
2614     }
2615     return '<div class="logininfo">'.$loggedinas.'</div>';
2618 /**
2619  * Prints breadcrumbs links
2620  *
2621  * @uses $CFG
2622  * @param string $navigation The breadcrumbs string to be printed
2623  */
2624 function print_navigation ($navigation, $return=false) {
2625     global $CFG, $USER;
2627     $output = '';
2629     if ($navigation) {
2630         //Accessibility: breadcrumb links now in a list, &raquo; replaced with image.
2631         $nav_text = get_string('youarehere','access');
2632         $output .= '<h2 class="accesshide">'.$nav_text.",</h2><ul>\n";
2633         if (! $site = get_site()) {
2634             $site->shortname = get_string('home');
2635         }
2636         $navigation = '<li title="'.$nav_text.'"><img src="'.$CFG->pixpath.'/a/r_breadcrumb.gif" class="resize" alt="" /> '
2637             .str_replace('->', '</li><li title="'.$nav_text.'"><img src="'.$CFG->pixpath.'/a/r_breadcrumb.gif" class="resize" alt="" /> ', $navigation)."</li>\n";
2638         $output .= '<li class="first"><a target="'. $CFG->framename .'" href="'. $CFG->wwwroot.((!isadmin() && !empty($USER->id) && !empty($CFG->mymoodleredirect) && !isguest())
2639                                                                        ? '/my' : '') .'/">'. $site->shortname ."</a></li>\n". $navigation;
2640         $output .= "</ul>\n";  
2641     }
2643     if ($return) {
2644         return $output;
2645     } else {
2646         echo $output;
2647     }
2650 /**
2651  * Prints a string in a specified size  (retained for backward compatibility)
2652  *
2653  * @param string $text The text to be displayed
2654  * @param int $size The size to set the font for text display.
2655  */
2656 function print_headline($text, $size=2, $return=false) {
2657     $output = print_heading($text, 'left', $size, true);
2658     if ($return) {
2659         return $output;
2660     } else {
2661         echo $output;
2662     }
2665 /**
2666  * Prints text in a format for use in headings.
2667  *
2668  * @param string $text The text to be displayed
2669  * @param string $align The alignment of the printed paragraph of text
2670  * @param int $size The size to set the font for text display.
2671  */
2672 function print_heading($text, $align='', $size=2, $class='main', $return=false) {
2673     if ($align) {
2674         $align = ' align="'.$align.'"';
2675     }
2676     if ($class) {
2677         $class = ' class="'.$class.'"';
2678     }
2679     $output = "<h$size $align $class>".stripslashes_safe($text)."</h$size>";
2681     if ($return) {
2682         return $output;
2683     } else {
2684         echo $output;
2685     }
2688 /**
2689  * Centered heading with attached help button (same title text)
2690  * and optional icon attached
2691  *
2692  * @param string $text The text to be displayed
2693  * @param string $helppage The help page to link to
2694  * @param string $module The module whose help should be linked to
2695  * @param string $icon Image to display if needed
2696  */
2697 function print_heading_with_help($text, $helppage, $module='moodle', $icon='', $return=false) {
2698     $output = '';
2699     $output .= '<h2 class="main help">'.$icon.stripslashes_safe($text);
2700     $output .= helpbutton($helppage, $text, $module, true, false, '', true);
2701     $output .= '</h2>';
2703     if ($return) {
2704         return $output;
2705     } else {
2706         echo $output;
2707     }
2711 function print_heading_block($heading, $class='', $return=false) {
2712     //Accessibility: 'headingblock' is now H1, see theme/standard/styles_*.css: ??
2713     $output = '<h2 class="headingblock header '.$class.'">'.stripslashes($heading).'</h2>';
2715     if ($return) {
2716         return $output;
2717     } else {
2718         echo $output;
2719     }
2723 /**
2724  * Print a link to continue on to another page.
2725  *
2726  * @uses $CFG
2727  * @param string $link The url to create a link to.
2728  */
2729 function print_continue($link, $return=false) {
2731     global $CFG;
2733     // in case we are logging upgrade in admin/index.php stop it
2734     if (function_exists('upgrade_log_finish')) {
2735         upgrade_log_finish();
2736     }
2738     $output = '';
2740     if (!$link) {
2741         $link = $_SERVER['HTTP_REFERER'];
2742     }
2744     $output .= '<div class="continuebutton">';
2745     $output .= print_single_button($link, NULL, get_string('continue'), 'post', $CFG->framename, true);
2746     $output .= '</div>'."\n";
2748     if ($return) {
2749         return $output;
2750     } else {
2751         echo $output;
2752     }
2755 /**
2756  * Print a message in a standard themed box.
2757  * See, {@link print_simple_box_start}.
2758  *
2759  * @param string $align string, alignment of the box, not the text (default center, left, right).
2760  * @param string $width string, width of the box, including units %, for example '100%'. 
2761  * @param string $color string, background colour of the box, for example '#eee'.
2762  * @param int $padding integer, padding in pixels, specified without units.
2763  * @param string $class string, space-separated class names.
2764  * @todo Finish documenting this function
2765  */
2766 function print_simple_box($message, $align='', $width='', $color='', $padding=5, $class='generalbox', $id='', $return=false) {
2767     $output = '';
2768     $output .= print_simple_box_start($align, $width, $color, $padding, $class, $id, true);
2769     $output .= stripslashes_safe($message);
2770     $output .= print_simple_box_end(true);
2772     if ($return) {
2773         return $output;
2774     } else {
2775         echo $output;
2776     }
2779 /**
2780  * Print the top portion of a standard themed box using a TABLE.  Yes, we know.
2781  * See bug 4943 for details on some accessibility work regarding this that didn't make it into 1.6.
2782  *
2783  * @param string $align string, alignment of the box, not the text (default center, left, right).
2784  * @param string $width string, width of the box, including % units, for example '100%'.
2785  * @param string $color string, background colour of the box, for example '#eee'.
2786  * @param int $padding integer, padding in pixels, specified without units.
2787  * @param string $class string, space-separated class names.
2788  */
2789 function print_simple_box_start($align='', $width='', $color='', $padding=5, $class='generalbox', $id='', $return=false) {
2791     $output = '';
2793     if ($color) {
2794         $color = 'bgcolor="'. $color .'"';
2795     }
2796     if ($align) {
2797         $align = 'align="'. $align .'"';
2798     }
2799     if ($width) {
2800         $width = 'width="'. $width .'"';
2801     }
2802     if ($id) {
2803         $id = 'id="'. $id .'"';
2804     }
2805     $output .= "<table $align $width $id class=\"$class\" border=\"0\" cellpadding=\"$padding\" cellspacing=\"0\">".
2806          "<tr><td $color class=\"$class"."content\">";
2808     if ($return) {
2809         return $output;
2810     } else {
2811         echo $output;
2812     }
2815 /**
2816  * Print the end portion of a standard themed box.
2817  */
2818 function print_simple_box_end($return=false) {
2819     $output = '</td></tr></table>';
2820     if ($return) {
2821         return $output;
2822     } else {
2823         echo $output;
2824     }
2828 /**
2829  * Print a self contained form with a single submit button.
2830  *
2831  * @param string $link ?
2832  * @param array $options ?
2833  * @param string $label ?
2834  * @param string $method ?
2835  * @todo Finish documenting this function
2836  */
2837 function print_single_button($link, $options, $label='OK', $method='get', $target='_self', $return=false) {
2838     $output = '';
2839     $output .= '<div class="singlebutton">';
2840     $output .= '<form action="'. $link .'" method="'. $method .'" target="'.$target.'">';
2841     if ($options) {
2842         foreach ($options as $name => $value) {
2843             $output .= '<input type="hidden" name="'. $name .'" value="'. $value .'" />';
2844         }
2845     }
2846     $output .= '<input type="submit" value="'. $label .'" /></form></div>';
2848     if ($return) {
2849         return $output;
2850     } else {
2851         echo $output;
2852     }
2855 /**
2856  * Print a spacer image with the option of including a line break.
2857  *
2858  * @param int $height ?
2859  * @param int $width ?
2860  * @param boolean $br ?
2861  * @todo Finish documenting this function
2862  */
2863 function print_spacer($height=1, $width=1, $br=true, $return=false) {
2864     global $CFG;
2865     $output = '';
2867     $output .= '<img class="spacer" height="'. $height .'" width="'. $width .'" src="'. $CFG->wwwroot .'/pix/spacer.gif" alt="" />';
2868     if ($br) {
2869         $output .= '<br />'."\n";
2870     }
2872     if ($return) {
2873         return $output;
2874     } else {
2875         echo $output;
2876     }
2879 /**
2880  * Given the path to a picture file in a course, or a URL,
2881  * this function includes the picture in the page.
2882  *
2883  * @param string $path ?
2884  * @param int $courseid ?
2885  * @param int $height ?
2886  * @param int $width ?
2887  * @param string $link ?
2888  * @todo Finish documenting this function
2889  */
2890 function print_file_picture($path, $courseid=0, $height='', $width='', $link='', $return=false) {
2891     global $CFG;
2892     $output = '';
2894     if ($height) {
2895         $height = 'height="'. $height .'"';
2896     }
2897     if ($width) {
2898         $width = 'width="'. $width .'"';
2899     }
2900     if ($link) {
2901         $output .= '<a href="'. $link .'">';
2902     }
2903     if (substr(strtolower($path), 0, 7) == 'http://') {
2904         $output .= '<img border="0" '.$height . $width .' src="'. $path .'" />';
2906     } else if ($courseid) {
2907         $output .= '<img border="0" '. $height . $width .' src="';
2908         if ($CFG->slasharguments) {        // Use this method if possible for better caching
2909             $output .= $CFG->wwwroot .'/file.php/'. $courseid .'/'. $path;
2910         } else {
2911             $output .= $CFG->wwwroot .'/file.php?file=/'. $courseid .'/'. $path;
2912         }
2913         $output .= '" />';
2914     } else {
2915         $output .= 'Error: must pass URL or course';
2916     }
2917     if ($link) {
2918         $output .= '</a>';
2919     }
2921     if ($return) {
2922         return $output;
2923     } else {
2924         echo $output;
2925     }
2928 /**
2929  * Print the specified user's avatar.
2930  *
2931  * @param int $userid ?
2932  * @param int $courseid ?
2933  * @param boolean $picture Print the user picture?
2934  * @param int $size Size in pixels.  Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatability
2935  * @param boolean $return If false print picture to current page, otherwise return the output as string
2936  * @param boolean $link Enclose printed image in a link to view specified course?
2937  * return string
2938  * @todo Finish documenting this function
2939  */
2940 function print_user_picture($userid, $courseid, $picture, $size=0, $return=false, $link=true, $target='') {
2941     global $CFG;
2943     if ($link) {
2944         if ($target) {
2945             $target=' target="_blank"';
2946         }
2947         $output = '<a '.$target.' href="'. $CFG->wwwroot .'/user/view.php?id='. $userid .'&amp;course='. $courseid .'">';
2948     } else {
2949         $output = '';
2950     }
2951     if (empty($size)) {
2952         $file = 'f2';
2953         $size = 35;
2954     } else if ($size === true or $size == 1) {
2955         $file = 'f1';
2956         $size = 100;
2957     } else if ($size >= 50) {
2958         $file = 'f1';
2959     } else {
2960         $file = 'f2';
2961     }
2962     $class = "userpicture";
2963     if ($picture) {  // Print custom user picture
2964         if ($CFG->slasharguments) {        // Use this method if possible for better caching
2965             $src =  $CFG->wwwroot .'/user/pix.php/'. $userid .'/'. $file .'.jpg"';
2966         } else {
2967             $src =  $CFG->wwwroot .'/user/pix.php?file=/'. $userid .'/'. $file .'.jpg"';
2968         }
2969     } else {         // Print default user pictures (use theme version if available)
2970         $class .= " defaultuserpic";
2971         $src =  "$CFG->pixpath/u/$file.png\"";
2972     }
2973     $output .= "<img class=\"$class\" align=\"middle\" src=\"$src".
2974                    " border=\"0\" width=\"$size\" height=\"$size\" alt=\"\" />";
2975     if ($link) {
2976         $output .= '</a>';
2977     }
2979     if ($return) {
2980         return $output;
2981     } else {
2982         echo $output;
2983     }
2986 /**
2987  * Prints a summary of a user in a nice little box.
2988  *
2989  * @uses $CFG
2990  * @uses $USER
2991  * @param user $user A {@link $USER} object representing a user
2992  * @param course $course A {@link $COURSE} object representing a course
2993  */
2994 function print_user($user, $course, $messageselect=false, $return=false) {
2996     global $CFG, $USER;
2998     $output = '';
3000     static $string;
3001     static $datestring;
3002     static $countries;
3003     static $isteacher;
3004     static $isadmin;
3006     if (empty($string)) {     // Cache all the strings for the rest of the page
3008         $string->email       = get_string('email');
3009         $string->location    = get_string('location');
3010         $string->lastaccess  = get_string('lastaccess');
3011         $string->activity    = get_string('activity');
3012         $string->unenrol     = get_string('unenrol');
3013         $string->loginas     = get_string('loginas');
3014         $string->fullprofile = get_string('fullprofile');
3015         $string->role        = get_string('role');
3016         $string->name        = get_string('name');
3017         $string->never       = get_string('never');
3019         $datestring->day     = get_string('day');
3020         $datestring->days    = get_string('days');
3021         $datestring->hour    = get_string('hour');
3022         $datestring->hours   = get_string('hours');
3023         $datestring->min     = get_string('min');
3024         $datestring->mins    = get_string('mins');
3025         $datestring->sec     = get_string('sec');
3026         $datestring->secs    = get_string('secs');
3028         $countries = get_list_of_countries();
3030         $isteacher = isteacher($course->id);
3031         $isadmin   = isadmin();
3032     }
3034 /// Get the hidden field list
3035     if ($isteacher || $isadmin) {
3036         $hiddenfields = array();
3037     } else {
3038         $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
3039     }
3041     $output .= '<table class="userinfobox">';
3042     $output .= '<tr>';
3043     $output .= '<td class="left side">';
3044     $output .= print_user_picture($user->id, $course->id, $user->picture, true, true);
3045     $output .= '</td>';
3046     $output .= '<td class="content">';
3047     $output .= '<div class="username">'.fullname($user, $isteacher).'</div>';
3048     $output .= '<div class="info">';
3049     if (!empty($user->role) and ($user->role <> $course->teacher)) {
3050         $output .= $string->role .': '. $user->role .'<br />';
3051     }
3052     if ($user->maildisplay == 1 or ($user->maildisplay == 2 and $course->category and !isguest()) or $isteacher) {
3053         $output .= $string->email .': <a href="mailto:'. $user->email .'">'. $user->email .'</a><br />';
3054     }
3055     if (($user->city or $user->country) and (!isset($hiddenfields['city']) or !isset($hiddenfields['country']))) {
3056         $output .= $string->location .': ';
3057         if ($user->city && !isset($hiddenfields['city'])) {
3058             $output .= $user->city;
3059         }
3060         if (!empty($countries[$user->country]) && !isset($hiddenfields['country'])) {
3061             if ($user->city && !isset($hiddenfields['city'])) {
3062                 $output .= ', ';
3063             }
3064             $output .= $countries[$user->country];
3065         }
3066         $output .= '<br />';
3067     }
3069     if (!isset($hiddenfields['lastaccess'])) {
3070         if ($user->lastaccess) {
3071             $output .= $string->lastaccess .': '. userdate($user->lastaccess);
3072             $output .= '&nbsp ('. format_time(time() - $user->lastaccess, $datestring) .')';
3073         } else {
3074             $output .= $string->lastaccess .': '. $string->never;
3075         }
3076     }
3077     $output .= '</div></td><td class="links">';
3078     //link to blogs
3079     if ($CFG->bloglevel > 0) {
3080         $output .= '<a href="'.$CFG->wwwroot.'/blog/index.php?userid='.$user->id.'">'.get_string('blogs','blog').'</a><br />';
3081     }
3083     if ($isteacher) {
3084         $timemidnight = usergetmidnight(time());
3085         $output .= '<a href="'. $CFG->wwwroot .'/course/user.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->activity .'</a><br />';
3086         if (!iscreator($user->id) or ($isadmin and !isadmin($user->id))) {  // Includes admins
3087             if ($course->category and isteacheredit($course->id) and isstudent($course->id, $user->id)) {  // Includes admins
3088                 $output .= '<a href="'. $CFG->wwwroot .'/course/unenrol.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->unenrol .'</a><br />';
3089             }
3090             if ($USER->id != $user->id) {
3091                 $output .= '<a href="'. $CFG->wwwroot .'/course/loginas.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->loginas .'</a><br />';
3092             }
3093         }
3094     }
3095     $output .= '<a href="'. $CFG->wwwroot .'/user/view.php?id='. $user->id .'&amp;course='. $course->id .'">'. $string->fullprofile .'...</a>';
3097     if (!empty($messageselect) && $isteacher) {
3098         $output .= '<br /><input type="checkbox" name="';
3099         if (isteacher($course->id, $user->id)) {
3100             $output .= 'teacher';
3101         } else {
3102             $output .= 'user';
3103         }
3104         $output .= $user->id.'" /> ';
3105     }
3107     $output .= '</td></tr></table>';
3109     if ($return) {
3110         return $output;
3111     } else {
3112         echo $output;
3113     }
3116 /**
3117  * Print a specified group's avatar.
3118  *
3119  * @param group $group A {@link group} object representing a group or array of groups
3120  * @param int $courseid ?
3121  * @param boolean $large ?
3122  * @param boolean $return ?
3123  * @param boolean $link ?
3124  * @return string
3125  * @todo Finish documenting this function
3126  */
3127 function print_group_picture($group, $courseid, $large=false, $return=false, $link=true) {
3128     global $CFG;
3130     if (is_array($group)) {
3131         $output = '';
3132         foreach($group as $g) {
3133             $output .= print_group_picture($g, $courseid, $large, true, $link);
3134         }
3135         if ($return) {
3136             return $output;
3137         } else {
3138             echo $output;
3139             return;
3140         }
3141     }
3143     static $isteacheredit;
3145     if (!isset($isteacheredit)) {
3146         $isteacheredit = isteacheredit($courseid);
3147     }
3149     if ($group->hidepicture and !$isteacheredit) {
3150         return '';
3151     }
3153     if ($link or $isteacheredit) {
3154         $output = '<a href="'. $CFG->wwwroot .'/user/index.php?id='. $courseid .'&amp;group='. $group->id .'">';
3155     } else {
3156         $output = '';
3157     }
3158     if ($large) {
3159         $file = 'f1';
3160         $size = 100;
3161     } else {
3162         $file = 'f2';
3163         $size = 35;
3164     }
3165     if ($group->picture) {  // Print custom group picture
3166         if ($CFG->slasharguments) {        // Use this method if possible for better caching
3167             $output .= '<img class="grouppicture" align="middle" src="'.$CFG->wwwroot.'/user/pixgroup.php/'.$group->id.'/'.$file.'.jpg"'.
3168                        ' border="0" width="'.$size.'" height="'.$size.'" alt="" title="'.s($group->name).'"/>';
3169         } else {
3170             $output .= '<img class="grouppicture" align="middle" src="'.$CFG->wwwroot.'/user/pixgroup.php?file=/'.$group->id.'/'.$file.'.jpg"'.
3171                        ' border="0" width="'.$size.'" height="'.$size.'" alt="" title="'.s($group->name).'"/>';
3172         }
3173     }
3174     if ($link or $isteacheredit) {
3175         $output .= '</a>';
3176     }
3178     if ($return) {
3179         return $output;
3180     } else {
3181         echo $output;
3182     }
3185 /**
3186  * Print a png image.
3187  *
3188  * @param string $url ?
3189  * @param int $sizex ?
3190  * @param int $sizey ?
3191  * @param boolean $return ?
3192  * @param string $parameters ?
3193  * @todo Finish documenting this function
3194  */
3195 function print_png($url, $sizex, $sizey, $return, $parameters='alt=""') {
3196     global $CFG;
3197     static $recentIE;
3199     if (!isset($recentIE)) {
3200         $recentIE = check_browser_version('MSIE', '5.0');
3201     }
3203     if ($recentIE) {  // work around the HORRIBLE bug IE has with alpha transparencies
3204         $output .= '<img src="'. $CFG->pixpath .'/spacer.gif" width="'. $sizex .'" height="'. $sizey .'"'.
3205                    ' border="0" class="png" style="width: '. $sizex .'px; height: '. $sizey .'px; '.
3206                    ' filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='.
3207                    "'$url', sizingMethod='scale') ".
3208                    ' '. $parameters .' />';
3209     } else {
3210         $output .= '<img src="'. $url .'" border="0" width="'. $sizex .'" height="'. $sizey .'" '.
3211                    ' '. $parameters .' />';
3212     }
3214     if ($return) {
3215         return $output;
3216     } else {
3217         echo $output;
3218     }
3221 /**
3222  * Print a nicely formatted table.
3223  *
3224  * @param array $table is an object with several properties.
3225  *     <ul<li>$table->head - An array of heading names.
3226  *     <li>$table->align - An array of column alignments
3227  *     <li>$table->size  - An array of column sizes
3228  *     <li>$table->wrap - An array of "nowrap"s or nothing
3229  *     <li>$table->data[] - An array of arrays containing the data.
3230  *     <li>$table->width  - A percentage of the page
3231  *     <li>$table->tablealign  - Align the whole table
3232  *     <li>$table->cellpadding  - Padding on each cell
3233  *     <li>$table->cellspacing  - Spacing between cells
3234  * </ul>
3235  * @param bool $return whether to return an output string or echo now
3236  * @return boolean or $string
3237  * @todo Finish documenting this function
3238  */
3239 function print_table($table, $return=false) {
3240     $output = '';
3242     if (isset($table->align)) {
3243         foreach ($table->align as $key => $aa) {
3244             if ($aa) {
3245                 $align[$key] = ' align="'. $aa .'"';
3246             } else {
3247                 $align[$key] = '';
3248             }
3249         }
3250     }
3251     if (isset($table->size)) {
3252         foreach ($table->size as $key => $ss) {
3253             if ($ss) {
3254                 $size[$key] = ' width="'. $ss .'"';
3255             } else {
3256                 $size[$key] = '';
3257             }
3258         }
3259     }
3260     if (isset($table->wrap)) {
3261         foreach ($table->wrap as $key => $ww) {
3262             if ($ww) {
3263                 $wrap[$key] = ' nowrap="nowrap" ';
3264             } else {
3265                 $wrap[$key] = '';
3266             }
3267         }
3268     }
3270     if (empty($table->width)) {
3271         $table->width = '80%';
3272     }
3274     if (empty($table->tablealign)) {
3275         $table->tablealign = 'center';
3276     }
3278     if (empty($table->cellpadding)) {
3279         $table->cellpadding = '5';
3280     }
3282     if (empty($table->cellspacing)) {
3283         $table->cellspacing = '1';
3284     }
3286     if (empty($table->class)) {
3287         $table->class = 'generaltable';
3288     }
3290     $tableid = empty($table->id) ? '' : 'id="'.$table->id.'"';
3292     $output .= '<table width="'.$table->width.'" border="0" align="'.$table->tablealign.'" ';
3293     $output .= " cellpadding=\"$table->cellpadding\" cellspacing=\"$table->cellspacing\" class=\"$table->class\" $tableid>\n";
3295     $countcols = 0;
3297     if (!empty($table->head)) {
3298         $countcols = count($table->head);
3299         $output .= '<tr>';
3300         foreach ($table->head as $key => $heading) {
3302             if (!isset($size[$key])) {
3303                 $size[$key] = '';
3304             }
3305             if (!isset($align[$key])) {
3306                 $align[$key] = '';
3307             }
3308             $output .= '<th valign="top" '. $align[$key].$size[$key] .' nowrap="nowrap" class="header c'.$key.'">'. $heading .'</th>';
3309         }
3310         $output .= '</tr>'."\n";
3311     }
3313     if (!empty($table->data)) {
3314         $oddeven = 1;
3315         foreach ($table->data as $key => $row) {
3316             $oddeven = $oddeven ? 0 : 1;
3317             $output .= '<tr class="r'.$oddeven.'">'."\n";
3318             if ($row == 'hr' and $countcols) {
3319                 $output .= '<td colspan="'. $countcols .'"><div class="tabledivider"></div></td>';
3320             } else {  /// it's a normal row of data
3321                 foreach ($row as $key => $item) {
3322                     if (!isset($size[$key])) {
3323                         $size[$key] = '';
3324                     }
3325                     if (!isset($align[$key])) {
3326                         $align[$key] = '';
3327                     }
3328                     if (!isset($wrap[$key])) {
3329                         $wrap[$key] = '';
3330                     }
3331                     $output .= '<td '. $align[$key].$size[$key].$wrap[$key] .' class="cell c'.$key.'">'. $item .'</td>';
3332                 }
3333             }
3334             $output .= '</tr>'."\n";
3335         }
3336     }
3337     $output .= '</table>'."\n";
3339     if ($return) {
3340         return $output;
3341     }
3343     echo $output;
3344     return true;
3347 /**
3348  * Creates a nicely formatted table and returns it.
3349  *
3350  * @param array $table is an object with several properties.
3351  *     <ul<li>$table->head - An array of heading names.
3352  *     <li>$table->align - An array of column alignments
3353  *     <li>$table->size  - An array of column sizes
3354  *     <li>$table->wrap - An array of "nowrap"s or nothing
3355  *     <li>$table->data[] - An array of arrays containing the data.
3356  *     <li>$table->class -  A css class name
3357  *     <li>$table->fontsize - Is the size of all the text
3358  *     <li>$table->tablealign  - Align the whole table
3359  *     <li>$table->width  - A percentage of the page
3360  *     <li>$table->cellpadding  - Padding on each cell
3361  *     <li>$table->cellspacing  - Spacing between cells
3362  * </ul>
3363  * @return string
3364  * @todo Finish documenting this function
3365  */
3366 function make_table($table) {
3368     if (isset($table->align)) {
3369         foreach ($table->align as $key => $aa) {
3370             if ($aa) {
3371                 $align[$key] = ' align="'. $aa .'"';
3372             } else {
3373                 $align[$key] = '';
3374             }
3375         }
3376     }
3377     if (isset($table->size)) {
3378         foreach ($table->size as $key => $ss) {
3379             if ($ss) {
3380                 $size[$key] = ' width="'. $ss .'"';
3381             } else {
3382                 $size[$key] = '';
3383             }
3384         }
3385     }
3386     if (isset($table->wrap)) {
3387         foreach ($table->wrap as $key => $ww) {
3388             if ($ww) {
3389                 $wrap[$key] = ' nowrap="nowrap" ';
3390             } else {
3391                 $wrap[$key] = '';
3392             }
3393         }
3394     }
3396     if (empty($table->width)) {
3397         $table->width = '80%';
3398     }
3400     if (empty($table->tablealign)) {
3401         $table->tablealign = 'center';
3402     }
3404     if (empty($table->cellpadding)) {
3405         $table->cellpadding = '5';
3406     }
3408     if (empty($table->cellspacing)) {
3409         $table->cellspacing = '1';
3410     }
3412     if (empty($table->class)) {
3413         $table->class = 'generaltable';
3414     }
3416     if (empty($table->fontsize)) {
3417         $fontsize = '';
3418     } else {
3419         $fontsize = '<font size="'. $table->fontsize .'">';
3420     }
3422     $output =  '<table width="'. $table->width .'" align="'. $table->tablealign .'" ';
3423     $output .= ' cellpadding="'. $table->cellpadding .'" cellspacing="'. $table->cellspacing .'" class="'. $table->class .'">'."\n";
3425     if (!empty($table->head)) {
3426         $output .= '<tr valign="top">';
3427         foreach ($table->head as $key => $heading) {
3428             if (!isset($size[$key])) {
3429                 $size[$key] = '';
3430             }
3431             if (!isset($align[$key])) {
3432                 $align[$key] = '';
3433             }
3434             $output .= '<th valign="top" '. $align[$key].$size[$key] .' nowrap="nowrap" class="'. $table->class .'header">'.$fontsize.$heading.'</th>';
3435         }
3436         $output .= '</tr>'."\n";
3437     }
3439     foreach ($table->data as $row) {
3440         $output .= '<tr valign="top">';
3441         foreach ($row as $key => $item) {
3442             if (!isset($size[$key])) {
3443                 $size[$key] = '';
3444             }
3445             if (!isset($align[$key])) {
3446                 $align[$key] = '';
3447             }
3448             if (!isset($wrap[$key])) {
3449                 $wrap[$key] = '';
3450             }
3451             $output .= '<td '. $align[$key].$size[$key].$wrap[$key] .' class="'. $table->class .'cell">'. $fontsize . $item .'</td>';
3452         }
3453         $output .= '</tr>'."\n";
3454     }
3455     $output .= '</table>'."\n";
3457     return $output;
3460 function print_recent_activity_note($time, $user, $isteacher, $text, $link, $return=false) {
3461     static $strftimerecent;
3463     $output = '';
3465     if (empty($strftimerecent)) {
3466         $strftimerecent = get_string('strftimerecent');
3467     }
3469     $date = userdate($time, $strftimerecent);
3470     $name = fullname($user, $isteacher);
3472     $output .= '<div class="head">';
3473     $output .= '<div class="date">'.$date.'</div> '.
3474          '<div class="name">'.fullname($user, $isteacher).'</div>';
3475     $output .= '</div>';
3476     $output .= '<div class="info"><a href="'.$link.'">'.format_string($text,true).'</a></div>';
3478     if ($return) {
3479         return $output;
3480     } else {
3481         echo $output;
3482     }
3486 /**
3487  * Prints a basic textarea field.
3488  *
3489  * @uses $CFG
3490  * @param boolean $usehtmleditor ?
3491  * @param int $rows ?
3492  * @param int $cols ?
3493  * @param null $width <b>Legacy field no longer used!</b>  Set to zero to get control over mincols
3494  * @param null $height <b>Legacy field no longer used!</b>  Set to zero to get control over minrows
3495  * @param string $name ?
3496  * @param string $value ?
3497  * @param int $courseid ?
3498  * @todo Finish documenting this function
3499  */
3500 function print_textarea($usehtmleditor, $rows, $cols, $width, $height, $name, $value='', $courseid=0, $return=false) {
3501 /// $width and height are legacy fields and no longer used as pixels like they used to be.
3502 /// However, you can set them to zero to override the mincols and minrows values below.
3504     global $CFG, $course;
3505     static $scriptcount; // For loading the htmlarea script only once.
3507     $mincols = 65;
3508     $minrows = 10;
3509     $str = '';
3510     
3511     if ( empty($CFG->editorsrc) ) { // for backward compatibility.
3512         if (empty($courseid)) {
3513             if (!empty($course->id)) {  // search for it in global context
3514                 $courseid = $course->id;
3515             }
3516         }
3518         if (empty($scriptcount)) {
3519             $scriptcount = 0;
3520         }
3522         if ($usehtmleditor) {
3524             if (!empty($courseid) and isteacher($courseid)) {
3525                 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3526                 $CFG->wwwroot .'/lib/editor/htmlarea/htmlarea.php?id='. $courseid .'"></script>'."\n" : '';
3527             } else {
3528                 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3529                 $CFG->wwwroot .'/lib/editor/htmlarea/htmlarea.php"></script>'."\n" : '';
3530             }
3531             $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3532             $CFG->wwwroot .'/lib/editor/htmlarea/lang/en.php"></script>'."\n" : '';
3533             $scriptcount++;
3535             if ($height) {    // Usually with legacy calls
3536                 if ($rows < $minrows) {
3537                     $rows = $minrows;
3538                 }
3539             }
3540             if ($width) {    // Usually with legacy calls
3541                 if ($cols < $mincols) {
3542                     $cols = $mincols;
3543                 }
3544             }
3545         }
3546     }
3547     $str .= '<textarea id="edit-'. $name .'" name="'. $name .'" rows="'. $rows .'" cols="'. $cols .'">';
3548     if ($usehtmleditor) {
3549         $str .= htmlspecialchars(stripslashes_safe($value)); // needed for editing of cleaned text!
3550     } else {
3551         $str .= s($value);
3552     }
3553     $str .= '</textarea>'."\n";
3554     
3555     if ($return) {
3556         return $str;
3557     }
3558     echo $str;
3561 /**
3562  * Legacy function, provided for backward compatability.
3563  * This method now simply calls {@link use_html_editor()}
3564  *
3565  * @deprecated Use {@link use_html_editor()} instead.
3566  * @param string $name Form element to replace with HTMl editor by name
3567  * @todo Finish documenting this function
3568  */
3569 function print_richedit_javascript($form, $name, $source='no') {
3570     use_html_editor($name);
3573 /**
3574  * Sets up the HTML editor on textareas in the current page.
3575  * If a field name is provided, then it will only be
3576  * applied to that field - otherwise it will be used
3577  * on every textarea in the page.
3578  *
3579  * In most cases no arguments need to be supplied
3580  *
3581  * @param string $name Form element to replace with HTMl editor by name
3582  */
3583 function use_html_editor($name='', $editorhidebuttons='') {
3584     $editor = 'editor_'.md5($name); //name might contain illegal characters
3585     echo '<script language="javascript" type="text/javascript" defer="defer">'."\n";
3586     echo "$editor = new HTMLArea('edit-$name');\n";
3587     echo "var config = $editor.config;\n";
3589     echo print_editor_config($editorhidebuttons);
3591     if (empty($name)) {
3592         echo "\nHTMLArea.replaceAll($editor.config);\n";
3593     } else {
3594         echo "\n$editor.generate();\n";
3595     }
3596     echo '</script>'."\n";
3599 function print_editor_config($editorhidebuttons='', $return=false) {
3600     global $CFG;
3601     
3602     $str = "config.pageStyle = \"body {";
3603     
3604     if (!(empty($CFG->editorbackgroundcolor))) {
3605         $str .= " background-color: $CFG->editorbackgroundcolor;";
3606     }
3608     if (!(empty($CFG->editorfontfamily))) {
3609         $str .= " font-family: $CFG->editorfontfamily;";
3610     }
3612     if (!(empty($CFG->editorfontsize))) {
3613         $str .= " font-size: $CFG->editorfontsize;";
3614     }
3616     $str .= " }\";\n";
3617     $str .= "config.killWordOnPaste = ";
3618     $str .= (empty($CFG->editorkillword)) ? "false":"true";
3619     $str .= ';'."\n";
3620     $str .= 'config.fontname = {'."\n";
3621     
3622     $fontlist = isset($CFG->editorfontlist) ? explode(';', $CFG->editorfontlist) : array();
3623     $i = 1;                     // Counter is used to get rid of the last comma.
3625     foreach ($fontlist as $fontline) {
3626         if (!empty($fontline)) {
3627             if ($i > 1) {
3628                 $str .= ','."\n";
3629             }
3630             list($fontkey, $fontvalue) = split(':', $fontline);
3631             $str .= '"'. $fontkey ."\":\t'". $fontvalue ."'";
3633             $i++;
3634         }
3635     }
3636     $str .= '};';
3638     if (!empty($editorhidebuttons)) {
3639         $str .= "\nconfig.hideSomeButtons(\" ". $editorhidebuttons ." \");\n";
3640     } else if (!empty($CFG->editorhidebuttons)) {
3641         $str .= "\nconfig.hideSomeButtons(\" ". $CFG->editorhidebuttons ." \");\n";
3642     }
3644     if (!empty($CFG->editorspelling) && !empty($CFG->aspellpath)) {
3645         $str .= print_speller_code($usehtmleditor=true, true);
3646     }
3647             
3648     if ($return) {
3649         return $str;
3650     }
3651     echo $str;
3654 /**
3655  * Returns a turn edit on/off button for course in a self contained form.
3656  * Used to be an icon, but it's now a simple form button
3657  *
3658  * @uses $CFG
3659  * @uses $USER
3660  * @param int $courseid The course  to update by id as found in 'course' table
3661  * @return string
3662  */
3663 function update_course_icon($courseid) {
3665     global $CFG, $USER;
3667     if (isteacheredit($courseid)) {
3668         if (!empty($USER->editing)) {
3669             $string = get_string('turneditingoff');
3670             $edit = '0';
3671         } else {
3672             $string = get_string('turneditingon');
3673             $edit = '1';
3674         }
3675         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/view.php\">".
3676             "<input type=\"hidden\" name=\"id\" value=\"$courseid\" />".
3677             "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
3678             "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />".
3679             "<input type=\"submit\" value=\"$string\" /></form>";
3680     }
3684 /**
3685  * Returns a turn edit on/off button for course in a self contained form.
3686  * Used to be an icon, but it's now a simple form button
3687  *
3688  * @uses $CFG
3689  * @uses $USER
3690  * @param int $courseid The course  to update by id as found in 'course' table
3691  * @return string
3692  */
3693 function update_mymoodle_icon() {
3695     global $CFG, $USER;
3697     if (!empty($USER->editing)) {
3698         $string = get_string('updatemymoodleoff');
3699         $edit = '0';
3700     } else {
3701         $string = get_string('updatemymoodleon');
3702         $edit = '1';
3703     }
3704     return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/my/index.php\">".
3705         "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
3706         "<input type=\"submit\" value=\"$string\" /></form>";
3709 /**
3710  * Prints the editing button on a module "view" page
3711  *
3712  * @uses $CFG
3713  * @param    type description
3714  * @todo Finish documenting this function
3715  */
3716 function update_module_button($moduleid, $courseid, $string) {
3717     global $CFG, $USER;
3720     if (isteacheredit($courseid)) {
3721         $string = get_string('updatethis', '', $string);
3722         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/mod.php\">".
3723                "<input type=\"hidden\" name=\"update\" value=\"$moduleid\" />".
3724                "<input type=\"hidden\" name=\"return\" value=\"true\" />".
3725                "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />".
3726                "<input type=\"submit\" value=\"$string\" /></form>";
3727     } else {
3728         return '';
3729     }
3732 /**
3733  * Prints the editing button on a category page
3734  *
3735  * @uses $CFG
3736  * @uses $USER
3737  * @param int $categoryid ?
3738  * @return string
3739  * @todo Finish documenting this function
3740  */
3741 function update_category_button($categoryid) {
3742     global $CFG, $USER;
3744     if (iscreator()) {
3745         if (!empty($USER->categoryediting)) {
3746             $string = get_string('turneditingoff');
3747             $edit = 'off';
3748         } else {
3749             $string = get_string('turneditingon');
3750             $edit = 'on';
3751         }
3752         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/category.php\">".
3753                "<input type=\"hidden\" name=\"id\" value=\"$categoryid\" />".
3754                "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
3755                "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />".
3756                "<input type=\"submit\" value=\"$string\" /></form>";
3757     }
3760 /**
3761  * Prints the editing button on categories listing
3762  *
3763  * @uses $CFG
3764  * @uses $USER
3765  * @return string
3766  */
3767 function update_categories_button() {
3768     global $CFG, $USER;
3770     if (isadmin()) {
3771         if (!empty($USER->categoryediting)) {
3772             $string = get_string('turneditingoff');
3773             $categoryedit = 'off';
3774         } else {
3775             $string = get_string('turneditingon');
3776             $categoryedit = 'on';
3777         }
3778         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/index.php\">".
3779                '<input type="hidden" name="categoryedit" value="'. $categoryedit .'" />'.
3780                '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />'.
3781                '<input type="submit" value="'. $string .'" /></form>';
3782     }
3785 /**
3786  * Prints the editing button on search results listing
3787  * For bulk move courses to another category
3788  */
3790 function update_categories_search_button($search,$page,$perpage) {
3791     global $CFG, $USER;
3793     if (isadmin()) {
3794         if (!empty($USER->categoryediting)) {
3795             $string = get_string("turneditingoff");
3796             $edit = "off";
3797             $perpage = 30;
3798         } else {
3799             $string = get_string("turneditingon");
3800             $edit = "on";
3801         }
3802         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/search.php\">".
3803                "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
3804                "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />".
3805                "<input type=\"hidden\" name=\"search\" value=\"".s($search, true)."\" />".
3806                "<input type=\"hidden\" name=\"page\" value=\"$page\" />".
3807                "<input type=\"hidden\" name=\"perpage\" value=\"$perpage\" />".
3808                "<input type=\"submit\" value=\"".s($string)."\" /></form>";
3809     }
3812 /**
3813  * Prints the editing button on group page
3814  *
3815  * @uses $CFG
3816  * @uses $USER
3817  * @param int $courseid The course group is associated with
3818  * @param int $groupid The group to update
3819  * @return string
3820  */
3821 function update_group_button($courseid, $groupid) {
3822     global $CFG, $USER;
3824     if (isteacheredit($courseid)) {
3825         $string = get_string('editgroupprofile');
3826         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/group.php\">".
3827                '<input type="hidden" name="id" value="'. $courseid .'" />'.
3828                '<input type="hidden" name="group" value="'. $groupid .'" />'.
3829                '<input type="hidden" name="edit" value="on" />'.
3830                '<input type="submit" value="'. $string .'" /></form>';
3831     }
3834 /**
3835  * Prints the editing button on groups page
3836  *
3837  * @uses $CFG
3838  * @uses $USER
3839  * @param int $courseid The id of the course to be edited
3840  * @return string
3841  * @todo Finish documenting this function
3842  */
3843 function update_groups_button($courseid) {
3844     global $CFG, $USER;
3846     if (isteacheredit($courseid)) {
3847         if (!empty($USER->groupsediting)) {
3848             $string = get_string('turneditingoff');
3849             $edit = 'off';
3850         } else {
3851             $string = get_string('turneditingon');
3852             $edit = 'on';
3853         }
3854         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/groups.php\">".
3855                "<input type=\"hidden\" name=\"id\" value=\"$courseid\" />".
3856                "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
3857                "<input type=\"submit\" value=\"$string\" /></form>";
3858     }
3861 /**
3862  * Prints an appropriate group selection menu
3863  *
3864  * @uses VISIBLEGROUPS
3865  * @param array $groups ?
3866  * @param int $groupmode ?
3867  * @param string $currentgroup ?
3868  * @param string $urlroot ?
3869  * @param boolean $showall: if set to 0, it is a student in separate groups, do not display all participants
3870  * @todo Finish documenting this function
3871  */
3872 function print_group_menu($groups, $groupmode, $currentgroup, $urlroot, $showall=1, $return=false) {
3874     $output = '';
3876 /// Add an "All groups" to the start of the menu
3877     if ($showall){
3878         $groupsmenu[0] = get_string('allparticipants');
3879     }
3880     foreach ($groups as $key => $groupname) {
3881         $groupsmenu[$key] = $groupname;
3882     }
3884     $output .= '<table><tr><td align="right">';
3885     if ($groupmode == VISIBLEGROUPS) {
3886         $output .= get_string('groupsvisible');
3887     } else {
3888         $output .= get_string('groupsseparate');
3889     }
3890     $output .= ':';
3891     $output .= '</td><td nowrap="nowrap" align="left">';
3892     $output .= popup_form($urlroot.'&amp;group=', $groupsmenu, 'selectgroup', $currentgroup, '', '', '', true, 'self');
3893     $output .= '</td></tr></table>';
3895     if ($return) {
3896         return $output;
3897     } else {
3898         echo $output;
3899     }
3903 /**
3904  * Given a course and a (current) coursemodule
3905  * This function returns a small popup menu with all the
3906  * course activity modules in it, as a navigation menu
3907  * The data is taken from the serialised array stored in
3908  * the course record
3909  *
3910  * @param course $course A {@link $COURSE} object.
3911  * @param course $cm A {@link $COURSE} object.
3912  * @param string $targetwindow ?
3913  * @return string
3914  * @todo Finish documenting this function
3915  */
3916 function navmenu($course, $cm=NULL, $targetwindow='self') {
3918     global $CFG, $THEME;
3920     if (empty($THEME->navmenuwidth)) {
3921         $width = 50;
3922     } else {
3923         $width = $THEME->navmenuwidth;
3924     }
3926     if ($cm) {
3927         $cm = $cm->id;
3928     }
3930     if ($course->format == 'weeks') {
3931         $strsection = get_string('week');
3932     } else {
3933         $strsection = get_string('topic');
3934     }
3935     $strjumpto = get_string('jumpto');
3937 /// Casting $course->modinfo to string prevents one notice when the field is null
3938     if (!$modinfo = unserialize((string)$course->modinfo)) {
3939         return '';
3940     }
3941     $isteacher = isteacher($course->id);
3942     $section = -1;
3943     $selected = '';
3944     $url = '';
3945     $previousmod = NULL;
3946     $backmod = NULL;
3947     $nextmod = NULL;
3948     $selectmod = NULL;
3949     $logslink = NULL;
3950     $flag = false;
3951     $menu = array();
3953     $sections = get_records('course_sections','course',$course->id,'section','section,visible,summary');
3955     if (!empty($THEME->makenavmenulist)) {   /// A hack to produce an XHTML navmenu list for use in themes
3956         $THEME->navmenulist = navmenulist($course, $sections, $modinfo,
3957                                           $isteacher, $strsection, $strjumpto, $width, $cm);
3958     }
3960     foreach ($modinfo as $mod) {
3961         if ($mod->mod == 'label') {
3962             continue;
3963         }
3965         if ($mod->section > $course->numsections) {   /// Don't show excess hidden sections
3966             break;
3967         }
3969         if ($mod->section > 0 and $section <> $mod->section) {
3970             $thissection = $sections[$mod->section];
3972             if ($thissection->visible or !$course->hiddensections or $isteacher) {
3973                 $thissection->summary = strip_tags(format_string($thissection->summary,true));
3974                 if ($course->format == 'weeks' or empty($thissection->summary)) {
3975                     $menu[] = '-------------- '. $strsection ." ". $mod->section .' --------------';
3976                 } else {
3977                     if (strlen($thissection->summary) < ($width-3)) {
3978                         $menu[] = '-- '.$thissection->summary;
3979                     } else {
3980                         $menu[] = '-- '.substr($thissection->summary, 0, $width).'...';
3981                     }
3982                 }
3983             }
3984         }
3986         $section = $mod->section;
3988         //Only add visible or teacher mods to jumpmenu
3989         if ($mod->visible or $isteacher) {
3990             $url = $mod->mod .'/view.php?id='. $mod->cm;
3991             if ($flag) { // the current mod is the "next" mod
3992                 $nextmod = $mod;
3993                 $flag = false;
3994             }
3995             if ($cm == $mod->cm) {
3996                 $selected = $url;
3997                 $selectmod = $mod;
3998                 $backmod = $previousmod;
3999                 $flag = true; // set flag so we know to use next mod for "next"
4000                 $mod->name = $strjumpto;
4001                 $strjumpto = '';
4002             } else {
4003                 $mod->name = strip_tags(format_string(urldecode($mod->name),true));
4004                 if (strlen($mod->name) > ($width+5)) {
4005                     $mod->name = substr($mod->name, 0, $width).'...';
4006                 }
4007                 if (!$mod->visible) {
4008                     $mod->name = '('.$mod->name.')';
4009                 }
4010             }
4011             $menu[$url] = $mod->name;
4012             $previousmod = $mod;
4013         }
4014     }
4015     if ($selectmod and $isteacher) {
4016         $logslink = "<td><a target=\"$CFG->framename\" href=".
4017                     "\"$CFG->wwwroot/course/report/log/index.php?chooselog=1&amp;user=0&amp;date=0&amp;id=$course->id&amp;modid=$selectmod->cm\">".
4018                     "<img border=\"0\" height=\"16\" width=\"16\" src=\"$CFG->pixpath/i/log.gif\" alt=\"\" /></a></td>";
4020     }
4021     if ($backmod) {
4022         $backmod = "<form action=\"$CFG->wwwroot/mod/$backmod->mod/view.php\" target=\"$CFG->framename\">".
4023                    "<input type=\"hidden\" name=\"id\" value=\"$backmod->cm\" />".
4024                    "<input type=\"submit\" value=\"&lt;\" /></form>";
4025     }
4026     if ($nextmod) {
4027         $nextmod = "<form action=\"$CFG->wwwroot/mod/$nextmod->mod/view.php\" target=\"$CFG->framename\">".
4028                    "<input type=\"hidden\" name=\"id\" value=\"$nextmod->cm\" />".
4029                    "<input type=\"submit\" value=\"&gt;\" /></form>";
4030     }
4032     return '<table><tr>'.$logslink .'<td>'. $backmod .'</td><td>' .
4033             popup_form($CFG->wwwroot .'/mod/', $menu, 'navmenu', $selected, $strjumpto,
4034                        '', '', true, $targetwindow).
4035             '</td><td>'. $nextmod .'</td></tr></table>';
4038 /**
4039  * Given a course
4040  * This function returns a small popup menu with all the
4041  * course activity modules in it, as a navigation menu
4042  * outputs a simple list structure in XHTML
4043  * The data is taken from the serialised array stored in
4044  * the course record
4045  *
4046  * @param course $course A {@link $COURSE} object.
4047  * @return string
4048  * @todo Finish documenting this function
4049  */
4050 function navmenulist($course, $sections, $modinfo, $isteacher, $strsection, $strjumpto, $width=50, $cmid=0) {
4052     global $CFG;
4054     $section = -1;
4055     $selected = '';
4056     $url = '';
4057     $previousmod = NULL;
4058     $backmod = NULL;
4059     $nextmod = NULL;
4060     $selectmod = NULL;
4061     $logslink = NULL;
4062     $flag = false;
4063     $menu = array();
4065     $menu[] = '<ul class="navmenulist"><li class="jumpto section"><span>'.$strjumpto.'</span><ul>';
4066     foreach ($modinfo as $mod) {
4067         if ($mod->mod == 'label') {
4068             continue;
4069         }
4071         if ($mod->section > $course->numsections) {   /// Don't show excess hidden sections
4072             break;
4073         }
4075         if ($mod->section >= 0 and $section <> $mod->section) {
4076             $thissection = $sections[$mod->section];
4078             if ($thissection->visible or !$course->hiddensections or $isteacher) {
4079                 $thissection->summary = strip_tags(format_string($thissection->summary,true));
4080                 if (!empty($doneheading)) {
4081                     $menu[] = '</ul></li>';
4082                 }
4083                 if ($course->format == 'weeks' or empty($thissection->summary)) {
4084                     $item = $strsection ." ". $mod->section;
4085                 } else {
4086                     if (strlen($thissection->summary) < ($width-3)) {
4087                         $item = $thissection->summary;
4088                     } else {
4089                         $item = substr($thissection->summary, 0, $width).'...';
4090                     }
4091                 }
4092                 $menu[] = '<li class="section"><span>'.$item.'</span>';
4093                 $menu[] = '<ul>';
4094                 $doneheading = true;
4095             }
4096         }
4098         $section = $mod->section;
4100         //Only add visible or teacher mods to jumpmenu
4101         if ($mod->visible or $isteacher) {
4102             $url = $mod->mod .'/view.php?id='. $mod->cm;
4103             if ($flag) { // the current mod is the "next" mod
4104                 $nextmod = $mod;
4105                 $flag = false;
4106             }
4107             $mod->name = strip_tags(format_string(urldecode($mod->name),true));
4108             if (strlen($mod->name) > ($width+5)) {
4109                 $mod->name = substr($mod->name, 0, $width).'...';
4110             }
4111             if (!$mod->visible) {
4112                 $mod->name = '('.$mod->name.')';
4113             }
4114             $class = 'activity '.$mod->mod;
4115             $class .= ($cmid == $mod->cm) ? ' selected' : '';
4116             $menu[] = '<li class="'.$class.'">'.
4117                       '<img src="'.$CFG->modpixpath.'/'.$mod->mod.'/icon.gif" border="0" />'.
4118                       '<a href="'.$CFG->wwwroot.'/mod/'.$url.'">'.$mod->name.'</a></li>';
4119             $previousmod = $mod;
4120         }
4121     }
4122     if ($doneheading) {
4123         $menu[] = '</ul></li>';
4124     }
4125     $menu[] = '</ul></li></ul>';
4127     return implode("\n", $menu);
4130 /**
4131  * Prints form items with the names $day, $month and $year
4132  *
4133  * @param string $day   fieldname
4134  * @param string $month  fieldname
4135  * @param string $year  fieldname
4136  * @param int $currenttime A default timestamp in GMT
4137  * @param boolean $return 
4138  */
4139 function print_date_selector($day, $month, $year, $currenttime=0, $return=false) {
4141     if (!$currenttime) {
4142         $currenttime = time();
4143     }
4144     $currentdate = usergetdate($currenttime);
4146     for ($i=1; $i<=31; $i++) {
4147         $days[$i] = $i;
4148     }
4149     for ($i=1; $i<=12; $i++) {
4150         $months[$i] = userdate(gmmktime(12,0,0,$i,1,2000), "%B");
4151     }
4152     for ($i=1970; $i<=2020; $i++) {
4153         $years[$i] = $i;
4154     }
4155     return choose_from_menu($days,   $day,   $currentdate['mday'], '', '', '0', $return)
4156           .choose_from_menu($months, $month, $currentdate['mon'],  '', '', '0', $return)
4157           .choose_from_menu($years,  $year,  $currentdate['year'], '', '', '0', $return);
4161 /**
4162  *Prints form items with the names $hour and $minute
4163  *
4164  * @param string $hour  fieldname
4165  * @param string ? $minute  fieldname
4166  * @param $currenttime A default timestamp in GMT
4167  * @param int $step minute spacing
4168  * @param boolean $return 
4169  */
4170 function print_time_selector($hour, $minute, $currenttime=0, $step=5, $return=false) {
4172     if (!$currenttime) {
4173         $currenttime = time();
4174     }
4175     $currentdate = usergetdate($currenttime);
4176     if ($step != 1) {
4177         $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
4178     }
4179     for ($i=0; $i<=23; $i++) {
4180         $hours[$i] = sprintf("%02d",$i);
4181     }
4182     for ($i=0; $i<=59; $i+=$step) {
4183         $minutes[$i] = sprintf("%02d",$i);
4184     }
4186     return choose_from_menu($hours,   $hour,   $currentdate['hours'],   '','','0',$return)
4187           .choose_from_menu($minutes, $minute, $currentdate['minutes'], '','','0',$return);
4190 /**
4191  * Prints time limit value selector
4192  *
4193  * @uses $CFG
4194  * @param int $timelimit default
4195  * @param string $unit 
4196  * @param string $name 
4197  * @param boolean $return 
4198  */
4199 function print_timer_selector($timelimit = 0, $unit = '', $name = 'timelimit', $return=false) {
4201     global $CFG;
4203     if ($unit) {
4204         $unit = ' '.$unit;
4205     }
4207     // Max timelimit is sessiontimeout - 10 minutes.
4208     $maxvalue = ($CFG->sessiontimeout / 60) - 10;
4210     for ($i=1; $i<=$maxvalue; $i++) {
4211         $minutes[$i] = $i.$unit;
4212     }
4213     return choose_from_menu($minutes, $name, $timelimit, get_string('none'), '','','0',$return);
4216 /**
4217  * Prints a grade menu (as part of an existing form) with help
4218  * Showing all possible numerical grades and scales
4219  *
4220  * @uses $CFG
4221  * @param int $courseid ?
4222  * @param string $name ?
4223  * @param string $current ?
4224  * @param boolean $includenograde ?
4225  * @todo Finish documenting this function
4226  */
4227 function print_grade_menu($courseid, $name, $current, $includenograde=true, $return=false) {
4229     global $CFG;
4231     $output = '';
4232     $strscale = get_string('scale');
4233     $strscales = get_string('scales');
4235     $scales = get_scales_menu($courseid);
4236     foreach ($scales as $i => $scalename) {
4237         $grades[-$i] = $strscale .': '. $scalename;
4238     }
4239     if ($includenograde) {
4240         $grades[0] = get_string('nograde');
4241     }
4242     for ($i=100; $i>=1; $i--) {
4243         $grades[$i] = $i;
4244     }
4245     $output .= choose_from_menu($grades, $name, $current, '', '', 0, true);
4247     $linkobject = '<span class="helplink"><img height="17" width="17" alt="'.$strscales.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
4248     $output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true', 'ratingscales',
4249                                      $linkobject, 400, 500, $strscales, 'none', true);
4251     if ($return) {
4252         return $output;
4253     } else {
4254         echo $output;
4255     }
4258 /**
4259  * Prints a scale menu (as part of an existing form) including help button
4260  * Just like {@link print_grade_menu()} but without the numeric grades
4261  *
4262  * @param int $courseid ?
4263  * @param string $name ?
4264  * @param string $current ?
4265  * @todo Finish documenting this function
4266  */
4267 function print_scale_menu($courseid, $name, $current, $return=false) {
4269     global $CFG;
4271     $output = '';
4272     $strscales = get_string('scales');
4273     $output .= choose_from_menu(get_scales_menu($courseid), $name, $current, '', '', 0, true);
4275     $linkobject = '<span class="helplink"><img height="17" width="17" alt="'.$strscales.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
4276     $output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true', 'ratingscales',
4277                                      $linkobject, 400, 500, $strscales, 'none', true);
4278     if ($return) {
4279         return $output;
4280     } else {
4281         echo $output;
4282     }
4285 /**
4286  * Prints a help button about a scale
4287  *
4288  * @uses $CFG
4289  * @param id $courseid ?
4290  * @param object $scale ?
4291  * @todo Finish documenting this function
4292  */
4293 function print_scale_menu_helpbutton($courseid, $scale, $return=false) {
4295     global $CFG;
4297     $output = '';
4298     $strscales = get_string('scales');
4300     $linkobject = '<span class="helplink"><img height="17" width="17" alt="'.$scale->name.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
4301     $output .= link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true&amp;scaleid='. $scale->id, 'ratingscale',
4302                                      $linkobject, 400, 500, $scale->name, 'none', true);
4303     if ($return) {
4304         return $output;
4305     } else {
4306         echo $output;
4307     }
4310 /**
4311  * Print an error page displaying an error message.
4312  * Old method, don't call directly in new code - use print_error instead.
4313  *
4314  *
4315  * @uses $SESSION
4316  * @uses $CFG
4317  * @param string $message The message to display to the user about the error.
4318  * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
4319  */
4320 function error ($message, $link='') {
4321     global $CFG, $SESSION;
4323     if (! defined('HEADER_PRINTED')) {
4324         //header not yet printed
4325         @header('HTTP/1.0 404 Not Found');
4326         print_header(get_string('error'));
4327     }
4329     echo '<br />';
4331     $message = clean_text($message);   // In case nasties are in here
4333     print_simple_box($message, '', '', '', '', 'errorbox');
4335     // in case we are logging upgrade in admin/index.php stop it
4336     if (function_exists('upgrade_log_finish')) {
4337         upgrade_log_finish();
4338     }
4340     if (!$link) {
4341         if ( !empty($SESSION->fromurl) ) {
4342             $link = $SESSION->fromurl;
4343             unset($SESSION->fromurl);
4344         } else {
4345             $link = $CFG->wwwroot .'/';
4346         }
4347     }
4348     print_continue($link);
4349     print_footer();
4350     for ($i=0;$i<512;$i++) {  // Padding to help IE work with 404
4351         echo ' ';
4352     }
4354     die;
4357 /**
4358  * Print an error page displaying an error message.  New method - use this for new code.
4359  *
4360  * @uses $SESSION
4361  * @uses $CFG
4362  * @param string $errorcode The name of the string from error.php to print
4363  * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
4364  * @param object $a Extra words and phrases that might be required in the error string
4365  */
4366 function print_error ($errorcode, $module='', $link='', $a=NULL) {
4368     global $CFG;
4370     if (empty($module) || $module == 'moodle' || $module == 'core') {
4371         $module = 'error';
4372         $modulelink = 'moodle';
4373     } else {
4374         $modulelink = $module;
4375     }
4377     if (!empty($CFG->errordocroot)) {
4378         $errordocroot = $CFG->errordocroot;
4379     } else if (!empty($CFG->docroot)) {
4380         $errordocroot = $CFG->docroot;
4381     } else {
4382         $errordocroot = 'http://docs.moodle.org';
4383     }
4385     $message = '<p class="errormessage">'.get_string($errorcode, $module, $a).'</p>'.
4386                '<p class="errorcode">'.
4387                '<a href="'.$errordocroot.'/en/error/'.$modulelink.'/'.$errorcode.'">'.
4388                  get_string('moreinformation').'</a></p>';
4389     error($message, $link);
4393 /**
4394  * Print a help button.
4395  *
4396  * @uses $CFG
4397  * @param string $page  The keyword that defines a help page
4398  * @param