e9b1df4ab7d8730ec701da3aa03a4cf77f62e947
[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/
76 /**
77  * Allowed tags - string of html tags that can be tested against for safe html tags
78  * @global string $ALLOWED_TAGS
79  */
80 $ALLOWED_TAGS =
81 '<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>';
83 /**
84  * Allowed protocols - array of protocols that are safe to use in links and so on
85  * @global string $ALLOWED_PROTOCOLS
86  */
87 $ALLOWED_PROTOCOLS = array('http', 'https', 'ftp', 'news', 'mailto', 'rtsp', 'teamspeak', 'gopher', 'mms',
88                            'color', 'callto', 'cursor', 'text-align', 'font-size', 'font-weight', 'font-style',
89                            'border', 'margin', 'padding');   // CSS as well to get through kses
92 /// Functions
94 /**
95  * Add quotes to HTML characters
96  *
97  * Returns $var with HTML characters (like "<", ">", etc.) properly quoted.
98  * This function is very similar to {@link p()}
99  *
100  * @param string $var the string potentially containing HTML characters
101  * @return string
102  */
103 function s($var) {
104     if ($var == '0') {  // for integer 0, boolean false, string '0'
105         return '0';
106     }
107     return preg_replace("/&amp;(#\d+);/i", "&$1;", htmlspecialchars(stripslashes_safe($var)));
110 /**
111  * Add quotes to HTML characters
112  *
113  * Prints $var with HTML characters (like "<", ">", etc.) properly quoted.
114  * This function is very similar to {@link s()}
115  *
116  * @param string $var the string potentially containing HTML characters
117  * @return string
118  */
119 function p($var) {
120     echo s($var);
124 /**
125  * Ensure that a variable is set
126  *
127  * Return $var if it is defined, otherwise return $default,
128  * This function is very similar to {@link optional_variable()}
129  *
130  * @param    mixed $var the variable which may be unset
131  * @param    mixed $default the value to return if $var is unset
132  * @return   mixed
133  */
134 function nvl(&$var, $default='') {
136     return isset($var) ? $var : $default;
139 /**
140  * Remove query string from url
141  *
142  * Takes in a URL and returns it without the querystring portion
143  *
144  * @param string $url the url which may have a query string attached
145  * @return string
146  */
147  function strip_querystring($url) {
149     if ($commapos = strpos($url, '?')) {
150         return substr($url, 0, $commapos);
151     } else {
152         return $url;
153     }
156 /**
157  * Returns the URL of the HTTP_REFERER, less the querystring portion
158  * @return string
159  */
160 function get_referer() {
162     return strip_querystring(nvl($_SERVER['HTTP_REFERER']));
166 /**
167  * Returns the name of the current script, WITH the querystring portion.
168  * this function is necessary because PHP_SELF and REQUEST_URI and SCRIPT_NAME
169  * return different things depending on a lot of things like your OS, Web
170  * server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.)
171  * <b>NOTE:</b> This function returns false if the global variables needed are not set.
172  *
173  * @return string
174  */
175  function me() {
177     if (!empty($_SERVER['REQUEST_URI'])) {
178         return $_SERVER['REQUEST_URI'];
180     } else if (!empty($_SERVER['PHP_SELF'])) {
181         if (!empty($_SERVER['QUERY_STRING'])) {
182             return $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
183         }
184         return $_SERVER['PHP_SELF'];
186     } else if (!empty($_SERVER['SCRIPT_NAME'])) {
187         if (!empty($_SERVER['QUERY_STRING'])) {
188             return $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
189         }
190         return $_SERVER['SCRIPT_NAME'];
192     } else if (!empty($_SERVER['URL'])) {     // May help IIS (not well tested)
193         if (!empty($_SERVER['QUERY_STRING'])) {
194             return $_SERVER['URL'] .'?'. $_SERVER['QUERY_STRING'];
195         }
196         return $_SERVER['URL'];
198     } else {
199         notify('Warning: Could not find any of these web server variables: $REQUEST_URI, $PHP_SELF, $SCRIPT_NAME or $URL');
200         return false;
201     }
204 /**
205  * Like {@link me()} but returns a full URL
206  * @see me()
207  * @return string
208  */
209 function qualified_me() {
211     global $CFG;
213     if (!empty($CFG->wwwroot)) {
214         $url = parse_url($CFG->wwwroot);
215     }
217     if (!empty($url['host'])) {
218         $hostname = $url['host'];
219     } else if (!empty($_SERVER['SERVER_NAME'])) {
220         $hostname = $_SERVER['SERVER_NAME'];
221     } else if (!empty($_ENV['SERVER_NAME'])) {
222         $hostname = $_ENV['SERVER_NAME'];
223     } else if (!empty($_SERVER['HTTP_HOST'])) {
224         $hostname = $_SERVER['HTTP_HOST'];
225     } else if (!empty($_ENV['HTTP_HOST'])) {
226         $hostname = $_ENV['HTTP_HOST'];
227     } else {
228         notify('Warning: could not find the name of this server!');
229         return false;
230     }
232     if (!empty($url['port'])) {
233         $hostname .= ':'.$url['port'];
234     } else if (!empty($_SERVER['SERVER_PORT'])) {
235         if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
236             $hostname .= ':'.$_SERVER['SERVER_PORT'];
237         }
238     }
240     if (isset($_SERVER['HTTPS'])) {
241         $protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
242     } else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS']
243         $protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://';
244     } else {
245         $protocol = 'http://';
246     }
248     $url_prefix = $protocol.$hostname;
249     return $url_prefix . me();
252 /**
253  * Determine if a web referer is valid
254  *
255  * Returns true if the referer is the same as the goodreferer. If
256  * the referer to test is not specified, use {@link qualified_me()}.
257  * If the admin has not set secure forms ($CFG->secureforms) then
258  * this function returns true regardless of a match.
259  *
260  * @uses $CFG
261  * @param string $goodreferer the url to compare to referer
262  * @return boolean
263  */
264 function match_referer($goodreferer = '') {
265     global $CFG;
267     if (empty($CFG->secureforms)) {    // Don't bother checking referer
268         return true;
269     }
271     if ($goodreferer == 'nomatch') {   // Don't bother checking referer
272         return true;
273     }
275     if (empty($goodreferer)) {
276         $goodreferer = qualified_me();
277     }
279     $referer = get_referer();
281     return (($referer == $goodreferer) or ($referer == $CFG->wwwroot .'/') or ($referer == $CFG->wwwroot .'/index.php'));
284 /**
285  * Determine if there is data waiting to be processed from a form
286  *
287  * Used on most forms in Moodle to check for data
288  * Returns the data as an object, if it's found.
289  * This object can be used in foreach loops without
290  * casting because it's cast to (array) automatically
291  *
292  * Checks that submitted POST data exists, and also
293  * checks the referer against the given url (it uses
294  * the current page if none was specified.
295  *
296  * @uses $CFG
297  * @param string $url the url to compare to referer for secure forms
298  * @return boolean
299  */
300 function data_submitted($url='') {
303     global $CFG;
305     if (empty($_POST)) {
306         return false;
308     } else {
309         if (match_referer($url)) {
310             return (object)$_POST;
311         } else {
312             if ($CFG->debug > 10) {
313                 notice('The form did not come from this page! (referer = '. get_referer() .')');
314             }
315             return false;
316         }
317     }
320 /**
321  * Moodle replacement for php stripslashes() function
322  *
323  * The standard php stripslashes() removes ALL backslashes
324  * even from strings - so  C:\temp becomes C:temp - this isn't good.
325  * This function should work as a fairly safe replacement
326  * to be called on quoted AND unquoted strings (to be sure)
327  *
328  * @param string the string to remove unsafe slashes from
329  * @return string
330  */
331 function stripslashes_safe($string) {
333     $string = str_replace("\\'", "'", $string);
334     $string = str_replace('\\"', '"', $string);
335     $string = str_replace('\\\\', '\\', $string);
336     return $string;
339 /**
340  * Recursive implementation of stripslashes()
341  *
342  * This function will allow you to strip the slashes from a variable.
343  * If the variable is an array or object, slashes will be stripped
344  * from the items (or properties) it contains, even if they are arrays
345  * or objects themselves.
346  *
347  * @param mixed the variable to remove slashes from
348  * @return mixed
349  */
350 function stripslashes_recursive($var) {
351     if(is_object($var)) {
352         $properties = get_object_vars($var);
353         foreach($properties as $property => $value) {
354             $var->$property = stripslashes_recursive($value);
355         }
356     }
357     else if(is_array($var)) {
358         foreach($var as $property => $value) {
359             $var[$property] = stripslashes_recursive($value);
360         }
361     }
362     else if(is_string($var)) {
363         $var = stripslashes($var);
364     }
365     return $var;
368 /**
369  * Given some normal text this function will break up any
370  * long words to a given size by inserting the given character
371  *
372  * It's multibyte savvy and doesn't change anything inside html tags.
373  *
374  * @param string $string the string to be modified
375  * @param int $maxsize maximum length of the string to be returned
376  * @param string $cutchar the string used to represent word breaks
377  * @return string
378  */
379 function break_up_long_words($string, $maxsize=20, $cutchar=' ') {
381 /// Loading the textlib singleton instance. We are going to need it.
382     $textlib = textlib_get_instance();
384 /// First of all, save all the tags inside the text to skip them
385     $tags = array();
386     filter_save_tags($string,$tags);
388 /// Process the string adding the cut when necessary
389     $output = '';
390     $length = $textlib->strlen($string, current_charset());
391     $wordlength = 0;
393     for ($i=0; $i<$length; $i++) {
394         $char = $textlib->substr($string, $i, 1, current_charset());
395         if ($char == ' ' or $char == "\t" or $char == "\n" or $char == "\r" or $char == "<" or $char == ">") {
396             $wordlength = 0;
397         } else {
398             $wordlength++;
399             if ($wordlength > $maxsize) {
400                 $output .= $cutchar;
401                 $wordlength = 0;
402             }
403         }
404         $output .= $char;
405     }
407 /// Finally load the tags back again
408     if (!empty($tags)) {
409         $output = str_replace(array_keys($tags), $tags, $output);
410     }
412     return $output;
415 /**
416  * This does a search and replace, ignoring case
417  * This function is only used for versions of PHP older than version 5
418  * which do not have a native version of this function.
419  * Taken from the PHP manual, by bradhuizenga @ softhome.net
420  *
421  * @param string $find the string to search for
422  * @param string $replace the string to replace $find with
423  * @param string $string the string to search through
424  * return string
425  */
426 if (!function_exists('str_ireplace')) {    /// Only exists in PHP 5
427     function str_ireplace($find, $replace, $string) {
429         if (!is_array($find)) {
430             $find = array($find);
431         }
433         if(!is_array($replace)) {
434             if (!is_array($find)) {
435                 $replace = array($replace);
436             } else {
437                 // this will duplicate the string into an array the size of $find
438                 $c = count($find);
439                 $rString = $replace;
440                 unset($replace);
441                 for ($i = 0; $i < $c; $i++) {
442                     $replace[$i] = $rString;
443                 }
444             }
445         }
447         foreach ($find as $fKey => $fItem) {
448             $between = explode(strtolower($fItem),strtolower($string));
449             $pos = 0;
450             foreach($between as $bKey => $bItem) {
451                 $between[$bKey] = substr($string,$pos,strlen($bItem));
452                 $pos += strlen($bItem) + strlen($fItem);
453             }
454             $string = implode($replace[$fKey],$between);
455         }
456         return ($string);
457     }
460 /**
461  * Locate the position of a string in another string
462  *
463  * This function is only used for versions of PHP older than version 5
464  * which do not have a native version of this function.
465  * Taken from the PHP manual, by dmarsh @ spscc.ctc.edu
466  *
467  * @param string $haystack The string to be searched
468  * @param string $needle The string to search for
469  * @param int $offset The position in $haystack where the search should begin.
470  */
471 if (!function_exists('stripos')) {    /// Only exists in PHP 5
472     function stripos($haystack, $needle, $offset=0) {
474         return strpos(strtoupper($haystack), strtoupper($needle), $offset);
475     }
478 /**
479  * Load a template from file
480  *
481  * Returns a (big) string containing the contents of a template file with all
482  * the variables interpolated.  all the variables must be in the $var[] array or
483  * object (whatever you decide to use).
484  *
485  * <b>WARNING: do not use this on big files!!</b>
486  *
487  * @param string $filename Location on the server's filesystem where template can be found.
488  * @param mixed $var Passed in by reference. An array or object which will be loaded with data from the template file.
489  */
490 function read_template($filename, &$var) {
492     $temp = str_replace("\\", "\\\\", implode(file($filename), ''));
493     $temp = str_replace('"', '\"', $temp);
494     eval("\$template = \"$temp\";");
495     return $template;
498 /**
499  * Set a variable's value depending on whether or not it already has a value.
500  *
501  * If variable is set, set it to the set_value otherwise set it to the
502  * unset_value.  used to handle checkboxes when you are expecting them from
503  * a form
504  *
505  * @param mixed $var Passed in by reference. The variable to check.
506  * @param mixed $set_value The value to set $var to if $var already has a value.
507  * @param mixed $unset_value The value to set $var to if $var does not already have a value.
508  */
509 function checked(&$var, $set_value = 1, $unset_value = 0) {
511     if (empty($var)) {
512         $var = $unset_value;
513     } else {
514         $var = $set_value;
515     }
518 /**
519  * Prints the word "checked" if a variable is true, otherwise prints nothing,
520  * used for printing the word "checked" in a checkbox form element.
521  *
522  * @param boolean $var Variable to be checked for true value
523  * @param string $true_value Value to be printed if $var is true
524  * @param string $false_value Value to be printed if $var is false
525  */
526 function frmchecked(&$var, $true_value = 'checked', $false_value = '') {
528     if ($var) {
529         echo $true_value;
530     } else {
531         echo $false_value;
532     }
535 /**
536  * This function will create a HTML link that will work on both
537  * Javascript and non-javascript browsers.
538  * Relies on the Javascript function openpopup in javascript.php
539  *
540  * $url must be relative to home page  eg /mod/survey/stuff.php
541  * @param string $url Web link relative to home page
542  * @param string $name Name to be assigned to the popup window
543  * @param string $linkname Text to be displayed as web link
544  * @param int $height Height to assign to popup window
545  * @param int $width Height to assign to popup window
546  * @param string $title Text to be displayed as popup page title
547  * @param string $options List of additional options for popup window
548  * @todo Add code examples and list of some options that might be used.
549  * @param boolean $return Should the link to the popup window be returned as a string (true) or printed immediately (false)?
550  * @return string
551  * @uses $CFG
552  */
553 function link_to_popup_window ($url, $name='popup', $linkname='click here',
554                                $height=400, $width=500, $title='Popup window', $options='none', $return=false) {
556     global $CFG;
558     if ($options == 'none') {
559         $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
560     }
561     $fullscreen = 0;
563     if (!(strpos($url,$CFG->wwwroot) === false)) { // some log url entries contain _SERVER[HTTP_REFERRER] in which case wwwroot is already there.
564         $url = substr($url, strlen($CFG->wwwroot)+1);
565     }
567     $link = '<a target="'. $name .'" title="'. $title .'" href="'. $CFG->wwwroot . $url .'" '.
568            "onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\">$linkname</a>";
569     if ($return) {
570         return $link;
571     } else {
572         echo $link;
573     }
576 /**
577  * This function will print a button submit form element
578  * that will work on both Javascript and non-javascript browsers.
579  * Relies on the Javascript function openpopup in javascript.php
580  *
581  * $url must be relative to home page  eg /mod/survey/stuff.php
582  * @param string $url Web link relative to home page
583  * @param string $name Name to be assigned to the popup window
584  * @param string $linkname Text to be displayed as web link
585  * @param int $height Height to assign to popup window
586  * @param int $width Height to assign to popup window
587  * @param string $title Text to be displayed as popup page title
588  * @param string $options List of additional options for popup window
589  * @param string $return If true, return as a string, otherwise print
590  * @return string
591  * @uses $CFG
592  */
593 function button_to_popup_window ($url, $name='popup', $linkname='click here',
594                                  $height=400, $width=500, $title='Popup window', $options='none', $return=false,
595                                  $id='', $class='') {
597     global $CFG;
599     if ($options == 'none') {
600         $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
601     }
603     if ($id) {
604         $id = ' id="'.$id.'" ';
605     }
606     if ($class) {
607         $class = ' class="'.$class.'" ';
608     }
609     $fullscreen = 0;
611     $button = '<input type="button" name="'.$name.'" title="'. $title .'" value="'. $linkname .' ..." '.$id.$class.
612               "onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\" />\n";
613     if ($return) {
614         return $button;
615     } else {
616         echo $button;
617     }
621 /**
622  * Prints a simple button to close a window
623  */
624 function close_window_button($name='closewindow') {
626     echo '<center>' . "\n";
627     echo '<script type="text/javascript">' . "\n";
628     echo '<!--' . "\n";
629     echo "document.write('<form>');\n";
630     echo "document.write('<input type=\"button\" onclick=\"self.close();\" value=\"".get_string("closewindow")."\" />');\n";
631     echo "document.write('<\/form>');\n";
632     echo '-->' . "\n";
633     echo '</script>' . "\n";
634     echo '<noscript>' . "\n";
635     print_string($name);
636     echo '</noscript>' . "\n";
637     echo '</center>' . "\n";
640 /*
641  * Try and close the current window immediately using Javascript
642  */
643 function close_window($delay=0) {
644     echo '<script language="JavaScript" type="text/javascript">'."\n";
645     echo '<!--'."\n";
646     if ($delay) {
647         sleep($delay);
648     }
649     echo 'self.close();'."\n";
650     echo '-->'."\n";
651     echo '</script>'."\n";
652     exit;
656 /**
657  * Given an array of value, creates a popup menu to be part of a form
658  * $options["value"]["label"]
659  *
660  * @param    type description
661  * @todo Finish documenting this function
662  */
663 function choose_from_menu ($options, $name, $selected='', $nothing='choose', $script='',
664                            $nothingvalue='0', $return=false, $disabled=false, $tabindex=0) {
666     if ($nothing == 'choose') {
667         $nothing = get_string('choose') .'...';
668     }
670     $attributes = ($script) ? 'onchange="'. $script .'"' : '';
671     if ($disabled) {
672         $attributes .= ' disabled="disabled"';
673     }
675     if ($tabindex) {
676         $attributes .= ' tabindex="'.$tabindex.'"';
677     }
679     $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
680     if ($nothing) {
681         $output .= '   <option value="'. $nothingvalue .'"'. "\n";
682         if ($nothingvalue === $selected) {
683             $output .= ' selected="selected"';
684         }
685         $output .= '>'. $nothing .'</option>' . "\n";
686     }
687     if (!empty($options)) {
688         foreach ($options as $value => $label) {
689             $output .= '   <option value="'. $value .'"';
690             if ((string)$value == (string)$selected) {
691                 $output .= ' selected="selected"';
692             }
693             if ($label === '') {
694                 $output .= '>'. $value .'</option>' . "\n";
695             } else {
696                 $output .= '>'. $label .'</option>' . "\n";
697             }
698         }
699     }
700     $output .= '</select>' . "\n";
702     if ($return) {
703         return $output;
704     } else {
705         echo $output;
706     }
709 /**
710  * Just like choose_from_menu, but takes a nested array (2 levels) and makes a dropdown menu
711  * including option headings with the first level.
712  */
713 function choose_from_menu_nested($options,$name,$selected='',$nothing='choose',$script = '',
714                                  $nothingvalue=0,$return=false,$disabled=false,$tabindex=0) {
716    if ($nothing == 'choose') {
717         $nothing = get_string('choose') .'...';
718     }
720     $attributes = ($script) ? 'onchange="'. $script .'"' : '';
721     if ($disabled) {
722         $attributes .= ' disabled="disabled"';
723     }
725     if ($tabindex) {
726         $attributes .= ' tabindex="'.$tabindex.'"';
727     }
729     $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
730     if ($nothing) {
731         $output .= '   <option value="'. $nothingvalue .'"'. "\n";
732         if ($nothingvalue === $selected) {
733             $output .= ' selected="selected"';
734         }
735         $output .= '>'. $nothing .'</option>' . "\n";
736     }
737     if (!empty($options)) {
738         foreach ($options as $section => $values) {
739             $output .= '   <optgroup label="'.$section.'">'."\n";
740             foreach ($values 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             $output .= '   </optgroup>'."\n";
752         }
753     }
754     $output .= '</select>' . "\n";
756     if ($return) {
757         return $output;
758     } else {
759         echo $output;
760     }
764 /**
765  * Given an array of values, creates a group of radio buttons to be part of a form
766  *
767  * @param array  $options  An array of value-label pairs for the radio group (values as keys)
768  * @param string $name     Name of the radiogroup (unique in the form)
769  * @param string $checked  The value that is already checked
770  */
771 function choose_from_radio ($options, $name, $checked='') {
773     static $idcounter = 0;
775     if (!$name) {
776         $name = 'unnamed';
777     }
779     $output = '<span class="radiogroup '.$name."\">\n";
781     if (!empty($options)) {
782         $currentradio = 0;
783         foreach ($options as $value => $label) {
784             $htmlid = 'auto-rb'.sprintf('%04d', ++$idcounter);
785             $output .= ' <span class="radioelement '.$name.' rb'.$currentradio."\">";
786             $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="radio" value="'.$value.'"';
787             if ($value == $checked) {
788                 $output .= ' checked="checked"';
789             }
790             if ($label === '') {
791                 $output .= ' /> <label for="'.$htmlid.'">'.  $value .'</label></span>' .  "\n";
792             } else {
793                 $output .= ' /> <label for="'.$htmlid.'">'.  $label .'</label></span>' .  "\n";
794             }
795             $currentradio = ($currentradio + 1) % 2;
796         }
797     }
799     $output .= '</span>' .  "\n";
801     echo $output;
804 /** Display an standard html checkbox with an optional label
805  *
806  * @param string  $name    The name of the checkbox
807  * @param string  $value   The valus that the checkbox will pass when checked
808  * @param boolean $checked The flag to tell the checkbox initial state
809  * @param string  $label   The label to be showed near the checkbox
810  * @param string  $alt     The info to be inserted in the alt tag
811  */
812 function print_checkbox ($name, $value, $checked = true, $label = '', $alt = '', $script='',$return=false) {
814     static $idcounter = 0;
816     if (!$name) {
817         $name = 'unnamed';
818     }
820     if (!$alt) {
821         $alt = 'checkbox';
822     }
824     if ($checked) {
825         $strchecked = ' checked="checked"';
826     }
828     $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
829     $output  = '<span class="checkbox '.$name."\">";
830     $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="checkbox" value="'.$value.'" alt="'.$alt.'"'.$strchecked.' '.((!empty($script)) ? ' onClick="'.$script.'" ' : '').' />';
831     if(!empty($label)) {
832         $output .= ' <label for="'.$htmlid.'">'.$label.'</label>';
833     }
834     $output .= '</span>'."\n";
836     if (empty($return)) {
837         echo $output;
838     } else {
839         return $output;
840     }
844 /** Display an standard html text field with an optional label
845  *
846  * @param string  $name    The name of the text field
847  * @param string  $value   The value of the text field
848  * @param string  $label   The label to be showed near the text field
849  * @param string  $alt     The info to be inserted in the alt tag
850  */
851 function print_textfield ($name, $value, $alt = '',$size=50,$maxlength= 0,$return=false) {
853     static $idcounter = 0;
855     if (empty($name)) {
856         $name = 'unnamed';
857     }
859     if (empty($alt)) {
860         $alt = 'textfield';
861     }
863     if (!empty($maxlength)) {
864         $maxlength = ' maxlength="'.$maxlength.'" ';
865     }
867     $htmlid = 'auto-tf'.sprintf('%04d', ++$idcounter);
868     $output  = '<span class="textfield '.$name."\">";
869     $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="text" value="'.$value.'" size="'.$size.'" '.$maxlength.' alt="'.$alt.'" />';
871     $output .= '</span>'."\n";
873     if (empty($return)) {
874         echo $output;
875     } else {
876         return $output;
877     }
882 /**
883  * Implements a complete little popup form
884  *
885  * @uses $CFG
886  * @param string $common  The URL up to the point of the variable that changes
887  * @param array $options  Alist of value-label pairs for the popup list
888  * @param string $formname Name must be unique on the page
889  * @param string $selected The option that is already selected
890  * @param string $nothing The label for the "no choice" option
891  * @param string $help The name of a help page if help is required
892  * @param string $helptext The name of the label for the help button
893  * @param boolean $return Indicates whether the function should return the text
894  *         as a string or echo it directly to the page being rendered
895  * @param string $targetwindow The name of the target page to open the linked page in.
896  * @return string If $return is true then the entire form is returned as a string.
897  * @todo Finish documenting this function<br>
898  */
899 function popup_form($common, $options, $formname, $selected='', $nothing='choose', $help='', $helptext='', $return=false, $targetwindow='self') {
901     global $CFG;
902     static $go, $choose;   /// Locally cached, in case there's lots on a page
904     if (empty($options)) {
905         return '';
906     }
908     if (!isset($go)) {
909         $go = get_string('go');
910     }
912     if ($nothing == 'choose') {
913         if (!isset($choose)) {
914             $choose = get_string('choose');
915         }
916         $nothing = $choose.'...';
917     }
919     $startoutput = '<form action="'.$CFG->wwwroot.'/course/jumpto.php"'.
920                         ' method="get"'.
921                         ' target="'.$CFG->framename.'"'.
922                         ' name="'.$formname.'"'.
923                         ' class="popupform">';
925     $output = '<select name="jump" onchange="'.$targetwindow.'.location=document.'.$formname.
926                        '.jump.options[document.'.$formname.'.jump.selectedIndex].value;">'."\n";
928     if ($nothing != '') {
929         $output .= "   <option value=\"javascript:void(0)\">$nothing</option>\n";
930     }
932     $inoptgroup = false;
933     foreach ($options as $value => $label) {
935         if (substr($label,0,2) == '--') { /// we are starting a new optgroup
937             /// Check to see if we already have a valid open optgroup
938             /// XHTML demands that there be at least 1 option within an optgroup
939             if ($inoptgroup and (count($optgr) > 1) ) {
940                 $output .= implode('', $optgr);
941                 $output .= '   </optgroup>';
942             }
944             unset($optgr);
945             $optgr = array();
947             $optgr[]  = '   <optgroup label="'. substr($label,2) .'">';   // Plain labels
949             $inoptgroup = true; /// everything following will be in an optgroup
950             continue;
952         } else {
953            if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()]))
954             {
955                 $url=sid_process_url( $common . $value );
956             } else
957             {
958                 $url=$common . $value;
959             }
960             $optstr = '   <option value="' . $url . '"';
962             if ($value == $selected) {
963                 $optstr .= ' selected="selected"';
964             }
966             if ($label) {
967                 $optstr .= '>'. $label .'</option>' . "\n";
968             } else {
969                 $optstr .= '>'. $value .'</option>' . "\n";
970             }
972             if ($inoptgroup) {
973                 $optgr[] = $optstr;
974             } else {
975                 $output .= $optstr;
976             }
977         }
979     }
981     /// catch the final group if not closed
982     if ($inoptgroup and count($optgr) > 1) {
983         $output .= implode('', $optgr);
984         $output .= '    </optgroup>';
985     }
987     $output .= '</select>';
988     $output .= '<noscript id="noscript'.$formname.'" style="display: inline;">';
989     $output .= '<input type="submit" value="'.$go.'" /></noscript>';
990     $output .= '<script type="text/javascript">'.
991                "\n<!--\n".
992                'document.getElementById("noscript'.$formname.'").style.display = "none";'.
993                "\n-->\n".'</script>';
994     $output .= '</form>' . "\n";
996     if ($help) {
997         $button = helpbutton($help, $helptext, 'moodle', true, false, '', true);
998     } else {
999         $button = '';
1000     }
1002     if ($return) {
1003         return $startoutput.$button.$output;
1004     } else {
1005         echo $startoutput.$button.$output;
1006     }
1010 /**
1011  * Prints some red text
1012  *
1013  * @param string $error The text to be displayed in red
1014  */
1015 function formerr($error) {
1017     if (!empty($error)) {
1018         echo '<font color="#ff0000">'. $error .'</font>';
1019     }
1022 /**
1023  * Validates an email to make sure it makes sense.
1024  *
1025  * @param string $address The email address to validate.
1026  * @return boolean
1027  */
1028 function validate_email($address) {
1030     return (ereg('^[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+'.
1031                   '@'.
1032                   '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
1033                   '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$',
1034                   $address));
1037 /**
1038  * Extracts file argument either from file parameter or PATH_INFO
1039  *
1040  * @param string $scriptname name of the calling script
1041  * @return string file path (only safe characters)
1042  */
1043 function get_file_argument($scriptname) {
1044     global $_SERVER;
1046     $relativepath = FALSE;
1048     // first try normal parameter (compatible method == no relative links!)
1049     $relativepath = optional_param('file', FALSE, PARAM_PATH);
1050     if ($relativepath === '/testslasharguments') {
1051         echo 'test -1: Incorrect use - try "file.php/testslasharguments" instead'; //indicate fopen/fread works for health center
1052         die;
1053     }
1055     // then try extract file from PATH_INFO (slasharguments method)
1056     if (!$relativepath and !empty($_SERVER['PATH_INFO'])) {
1057         $path_info = $_SERVER['PATH_INFO'];
1058         // check that PATH_INFO works == must not contain the script name
1059         if (!strpos($path_info, $scriptname)) {
1060             $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1061             if ($relativepath === '/testslasharguments') {
1062                 echo 'test 1: Slasharguments test passed.'; //indicate ok for health center
1063                 die;
1064             }
1065         }
1066     }
1068     // now if both fail try the old way
1069     // (for compatibility with misconfigured or older buggy php implementations)
1070     if (!$relativepath) {
1071         $arr = explode($scriptname, me());
1072         if (!empty($arr[1])) {
1073             $path_info = strip_querystring($arr[1]);
1074             $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1075             if ($relativepath === '/testslasharguments') {
1076                 echo 'test 2:Slasharguments test passed (compatibility hack).'; //indicate ok for health center
1077                 die;
1078             }
1079         }
1080     }
1082     return $relativepath;
1085 /**
1086  * Check for bad characters ?
1087  *
1088  * @param string $string ?
1089  * @param int $allowdots ?
1090  * @todo Finish documenting this function - more detail needed in description as well as details on arguments
1091  */
1092 function detect_munged_arguments($string, $allowdots=1) {
1093     if (substr_count($string, '..') > $allowdots) {   // Sometimes we allow dots in references
1094         return true;
1095     }
1096     if (ereg('[\|\`]', $string)) {  // check for other bad characters
1097         return true;
1098     }
1099     if (empty($string) or $string == '/') {
1100         return true;
1101     }
1103     return false;
1106 /**
1107  * Searches the current environment variables for some slash arguments
1108  *
1109  * @param string $file ?
1110  * @todo Finish documenting this function
1111  */
1112 function get_slash_arguments($file='file.php') {
1114     if (!$string = me()) {
1115         return false;
1116     }
1118     $pathinfo = explode($file, $string);
1120     if (!empty($pathinfo[1])) {
1121         return addslashes($pathinfo[1]);
1122     } else {
1123         return false;
1124     }
1127 /**
1128  * Extracts arguments from "/foo/bar/something"
1129  * eg http://mysite.com/script.php/foo/bar/something
1130  *
1131  * @param string $string ?
1132  * @param int $i ?
1133  * @return array|string
1134  * @todo Finish documenting this function
1135  */
1136 function parse_slash_arguments($string, $i=0) {
1138     if (detect_munged_arguments($string)) {
1139         return false;
1140     }
1141     $args = explode('/', $string);
1143     if ($i) {     // return just the required argument
1144         return $args[$i];
1146     } else {      // return the whole array
1147         array_shift($args);  // get rid of the empty first one
1148         return $args;
1149     }
1152 /**
1153  * Just returns an array of text formats suitable for a popup menu
1154  *
1155  * @uses FORMAT_MOODLE
1156  * @uses FORMAT_HTML
1157  * @uses FORMAT_PLAIN
1158  * @uses FORMAT_MARKDOWN
1159  * @return array
1160  */
1161 function format_text_menu() {
1163     return array (FORMAT_MOODLE => get_string('formattext'),
1164                   FORMAT_HTML   => get_string('formathtml'),
1165                   FORMAT_PLAIN  => get_string('formatplain'),
1166                   FORMAT_MARKDOWN  => get_string('formatmarkdown'));
1169 /**
1170  * Given text in a variety of format codings, this function returns
1171  * the text as safe HTML.
1172  *
1173  * @uses $CFG
1174  * @uses FORMAT_MOODLE
1175  * @uses FORMAT_HTML
1176  * @uses FORMAT_PLAIN
1177  * @uses FORMAT_WIKI
1178  * @uses FORMAT_MARKDOWN
1179  * @param string $text The text to be formatted. This is raw text originally from user input.
1180  * @param int $format Identifier of the text format to be used
1181  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1182  * @param  array $options ?
1183  * @param int $courseid ?
1184  * @return string
1185  * @todo Finish documenting this function
1186  */
1187 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL ) {
1189     global $CFG, $course;
1191     if (!isset($options->noclean)) {
1192         $options->noclean=false;
1193     }
1194     if (!isset($options->smiley)) {
1195         $options->smiley=true;
1196     }
1197     if (!isset($options->filter)) {
1198         $options->filter=true;
1199     }
1200     if (!isset($options->para)) {
1201         $options->para=true;
1202     }
1203     if (!isset($options->newlines)) {
1204         $options->newlines=true;
1205     }
1207     if (empty($courseid)) {
1208         if (!empty($course->id)) {         // An ugly hack for better compatibility
1209             $courseid = $course->id;
1210         }
1211     }
1213     if (!empty($CFG->cachetext)) {
1214         $time = time() - $CFG->cachetext;
1215         $md5key = md5($text.'-'.$courseid.$options->noclean.$options->smiley.$options->filter.$options->para.$options->newlines);
1216         if ($cacheitem = get_record_select('cache_text', "md5key = '$md5key' AND timemodified > '$time'")) {
1217             return $cacheitem->formattedtext;
1218         }
1219     }
1221     $CFG->currenttextiscacheable = true;   // Default status - can be changed by any filter
1223     switch ($format) {
1224         case FORMAT_HTML:
1225             if (!empty($options->smiley)) {
1226                 replace_smilies($text);
1227             }
1228             if (empty($options->noclean)) {
1229                 $text = clean_text($text, $format);
1230             }
1231             if (!empty($options->filter)) {
1232                 $text = filter_text($text, $courseid);
1233             }
1234             break;
1236         case FORMAT_PLAIN:
1237             $text = s($text);
1238             $text = rebuildnolinktag($text);
1239             $text = str_replace('  ', '&nbsp; ', $text);
1240             $text = nl2br($text);
1241             break;
1243         case FORMAT_WIKI:
1244             // this format is deprecated
1245             $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle.  You should not be seeing
1246                      this message as all texts should have been converted to Markdown format instead.
1247                      Please post a bug report to http://moodle.org/bugs with information about where you
1248                      saw this message.</p>'.s($text);
1249             break;
1251         case FORMAT_MARKDOWN:
1252             $text = markdown_to_html($text);
1253             if (!empty($options->smiley)) {
1254                 replace_smilies($text);
1255             }
1256             if (empty($options->noclean)) {
1257                 $text = clean_text($text, $format);
1258             }
1260             if (!empty($options->filter)) {
1261                 $text = filter_text($text, $courseid);
1262             }
1263             break;
1265         default:  // FORMAT_MOODLE or anything else
1266             $text = text_to_html($text, $options->smiley, $options->para, $options->newlines);
1267             if (empty($options->noclean)) {
1268                 $text = clean_text($text, $format);
1269             }
1271             if (!empty($options->filter)) {
1272                 $text = filter_text($text, $courseid);
1273             }
1274             break;
1275     }
1277     if (!empty($CFG->cachetext) and $CFG->currenttextiscacheable) {
1278         $newrecord->md5key = $md5key;
1279         $newrecord->formattedtext = addslashes($text);
1280         $newrecord->timemodified = time();
1281         @insert_record('cache_text', $newrecord);
1282     }
1284     return $text;
1287 /** Converts the text format from the value to the 'internal'
1288  *  name or vice versa. $key can either be the value or the name
1289  *  and you get the other back.
1290  *
1291  *  @param mixed int 0-4 or string one of 'moodle','html','plain','markdown'
1292  *  @return mixed as above but the other way around!
1293  */
1294 function text_format_name( $key ) {
1295   $lookup = array();
1296   $lookup[FORMAT_MOODLE] = 'moodle';
1297   $lookup[FORMAT_HTML] = 'html';
1298   $lookup[FORMAT_PLAIN] = 'plain';
1299   $lookup[FORMAT_MARKDOWN] = 'markdown';
1300   $value = "error";
1301   if (!is_numeric($key)) {
1302     $key = strtolower( $key );
1303     $value = array_search( $key, $lookup );
1304   }
1305   else {
1306     if (isset( $lookup[$key] )) {
1307       $value =  $lookup[ $key ];
1308     }
1309   }
1310   return $value;
1313 /** Given a simple string, this function returns the string
1314  *  processed by enabled filters if $CFG->filterall is enabled
1315  *
1316  *  @param string  $string     The string to be filtered.
1317  *  @param boolean $striplinks To strip any link in the result text.
1318  *  @param int     $courseid   Current course as filters can, potentially, use it
1319  *  @return string
1320  */
1321 function format_string ($string, $striplinks = false, $courseid=NULL ) {
1323     global $CFG, $course;
1325     //We'll use a in-memory cache here to speed up repeated strings
1326     static $strcache;
1328     //Calculate md5
1329     $md5 = md5($string.'<+>'.$striplinks);
1331     //Fetch from cache if possible
1332     if(isset($strcache[$md5])) {
1333         return $strcache[$md5];
1334     }
1336     if (empty($courseid)) {
1337         if (!empty($course->id)) {         // An ugly hack for better compatibility
1338             $courseid = $course->id;       // (copied from format_text)
1339         }
1340     }
1342     if (!empty($CFG->filterall)) {
1343         $string = filter_text($string, $courseid);
1344     }
1346     if ($striplinks) {  //strip links in string
1347         $string = preg_replace('/(<a[^>]+?>)(.+?)(<\/a>)/is','$2',$string);
1348     }
1350     //Store to cache
1351     $strcache[$md5] = $string;
1353     return $string;
1356 /**
1357  * Given text in a variety of format codings, this function returns
1358  * the text as plain text suitable for plain email.
1359  *
1360  * @uses FORMAT_MOODLE
1361  * @uses FORMAT_HTML
1362  * @uses FORMAT_PLAIN
1363  * @uses FORMAT_WIKI
1364  * @uses FORMAT_MARKDOWN
1365  * @param string $text The text to be formatted. This is raw text originally from user input.
1366  * @param int $format Identifier of the text format to be used
1367  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1368  * @return string
1369  */
1370 function format_text_email($text, $format) {
1372     switch ($format) {
1374         case FORMAT_PLAIN:
1375             return $text;
1376             break;
1378         case FORMAT_WIKI:
1379             $text = wiki_to_html($text);
1380         /// This expression turns links into something nice in a text format. (Russell Jungwirth)
1381         /// From: http://php.net/manual/en/function.eregi-replace.php and simplified
1382             $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1383             return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1384             break;
1386         case FORMAT_HTML:
1387             return html_to_text($text);
1388             break;
1390         case FORMAT_MOODLE:
1391         case FORMAT_MARKDOWN:
1392         default:
1393             $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1394             return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1395             break;
1396     }
1399 /**
1400  * Given some text in HTML format, this function will pass it
1401  * through any filters that have been defined in $CFG->textfilterx
1402  * The variable defines a filepath to a file containing the
1403  * filter function.  The file must contain a variable called
1404  * $textfilter_function which contains the name of the function
1405  * with $courseid and $text parameters
1406  *
1407  * @param string $text The text to be passed through format filters
1408  * @param int $courseid ?
1409  * @return string
1410  * @todo Finish documenting this function
1411  */
1412 function filter_text($text, $courseid=NULL) {
1414     global $CFG;
1416     require_once($CFG->libdir.'/filterlib.php');
1417     if (!empty($CFG->textfilters)) {
1418         $textfilters = explode(',', $CFG->textfilters);
1419         foreach ($textfilters as $textfilter) {
1420             if (is_readable($CFG->dirroot .'/'. $textfilter .'/filter.php')) {
1421                 include_once($CFG->dirroot .'/'. $textfilter .'/filter.php');
1422                 $functionname = basename($textfilter).'_filter';
1423                 if (function_exists($functionname)) {
1424                     $text = $functionname($courseid, $text);
1425                 }
1426             }
1427         }
1428     }
1430     /// <nolink> tags removed for XHTML compatibility
1431     $text = str_replace('<nolink>', '', $text);
1432     $text = str_replace('</nolink>', '', $text);
1434     return $text;
1437 /**
1438  * Given raw text (eg typed in by a user), this function cleans it up
1439  * and removes any nasty tags that could mess up Moodle pages.
1440  *
1441  * @uses FORMAT_MOODLE
1442  * @uses FORMAT_PLAIN
1443  * @uses ALLOWED_TAGS
1444  * @param string $text The text to be cleaned
1445  * @param int $format Identifier of the text format to be used
1446  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1447  * @return string The cleaned up text
1448  */
1449 function clean_text($text, $format=FORMAT_MOODLE) {
1451     global $ALLOWED_TAGS;
1453     switch ($format) {
1454         case FORMAT_PLAIN:
1455             return $text;
1457         default:
1459         /// Fix non standard entity notations
1460             $text = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $text);
1461             $text = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $text);
1463         /// Remove tags that are not allowed
1464             $text = strip_tags($text, $ALLOWED_TAGS);
1466         /// Clean up embedded scripts and , using kses
1467             $text = cleanAttributes($text);
1469         /// Remove script events
1470             $text = eregi_replace("([^a-z])language([[:space:]]*)=", "\\1Xlanguage=", $text);
1471             $text = eregi_replace("([^a-z])on([a-z]+)([[:space:]]*)=", "\\1Xon\\2=", $text);
1473             return $text;
1474     }
1477 /**
1478  * This function takes a string and examines it for HTML tags.
1479  * If tags are detected it passes the string to a helper function {@link cleanAttributes2()}
1480  *  which checks for attributes and filters them for malicious content
1481  *         17/08/2004              ::          Eamon DOT Costello AT dcu DOT ie
1482  *
1483  * @param string $str The string to be examined for html tags
1484  * @return string
1485  */
1486 function cleanAttributes($str){
1487     $result = preg_replace_callback(
1488             '%(<[^>]*(>|$)|>)%m', #search for html tags
1489             "cleanAttributes2",
1490             $str
1491             );
1492     return  $result;
1495 /**
1496  * This function takes a string with an html tag and strips out any unallowed
1497  * protocols e.g. javascript:
1498  * It calls ancillary functions in kses which are prefixed by kses
1499 *        17/08/2004              ::          Eamon DOT Costello AT dcu DOT ie
1500  *
1501  * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st
1502  *              element the html to be cleared
1503  * @return string
1504  */
1505 function cleanAttributes2($htmlArray){
1507     global $CFG, $ALLOWED_PROTOCOLS;
1508     require_once($CFG->libdir .'/kses.php');
1510     $htmlTag = $htmlArray[1];
1511     if (substr($htmlTag, 0, 1) != '<') {
1512         return '&gt;';  //a single character ">" detected
1513     }
1514     if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) {
1515         return ''; // It's seriously malformed
1516     }
1517     $slash = trim($matches[1]); //trailing xhtml slash
1518     $elem = $matches[2];    //the element name
1519     $attrlist = $matches[3]; // the list of attributes as a string
1521     $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
1523     $attStr = '';
1524     foreach ($attrArray as $arreach) {
1525         $arreach['name'] = strtolower($arreach['name']);
1526         if ($arreach['name'] == 'style') {
1527             $value = $arreach['value'];
1528             while (true) {
1529                 $prevvalue = $value;
1530                 $value = kses_no_null($value);
1531                 $value = preg_replace("/\/\*.*\*\//Us", '', $value);
1532                 $value = kses_decode_entities($value);
1533                 $value = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $value);
1534                 $value = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $value);
1535                 if ($value === $prevvalue) {
1536                     $arreach['value'] = $value;
1537                     break;
1538                 }
1539             }
1540             $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']);
1541             $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']);
1542         }
1543         $attStr .=  ' '.$arreach['name'].'="'.$arreach['value'].'" ';
1544     }
1546     // Remove last space from attribute list
1547     $attStr = rtrim($attStr);
1549     $xhtml_slash = '';
1550     if (preg_match('%/\s*$%', $attrlist)) {
1551         $xhtml_slash = ' /';
1552     }
1553     return '<'. $slash . $elem . $attStr . $xhtml_slash .'>';
1556 /**
1557  * Replaces all known smileys in the text with image equivalents
1558  *
1559  * @uses $CFG
1560  * @param string $text Passed by reference. The string to search for smily strings.
1561  * @return string
1562  */
1563 function replace_smilies(&$text) {
1564 ///
1565     global $CFG;
1567 /// this builds the mapping array only once
1568     static $runonce = false;
1569     static $e = array();
1570     static $img = array();
1571     static $emoticons = array(
1572         ':-)'  => 'smiley',
1573         ':)'   => 'smiley',
1574         ':-D'  => 'biggrin',
1575         ';-)'  => 'wink',
1576         ':-/'  => 'mixed',
1577         'V-.'  => 'thoughtful',
1578         ':-P'  => 'tongueout',
1579         'B-)'  => 'cool',
1580         '^-)'  => 'approve',
1581         '8-)'  => 'wideeyes',
1582         ':o)'  => 'clown',
1583         ':-('  => 'sad',
1584         ':('   => 'sad',
1585         '8-.'  => 'shy',
1586         ':-I'  => 'blush',
1587         ':-X'  => 'kiss',
1588         '8-o'  => 'surprise',
1589         'P-|'  => 'blackeye',
1590         '8-['  => 'angry',
1591         'xx-P' => 'dead',
1592         '|-.'  => 'sleepy',
1593         '}-]'  => 'evil',
1594         );
1596     if ($runonce == false) {  /// After the first time this is not run again
1597         foreach ($emoticons as $emoticon => $image){
1598             $alttext = get_string($image, 'pix');
1600             $e[] = $emoticon;
1601             $img[] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $CFG->pixpath .'/s/'. $image .'.gif" />';
1602         }
1603         $runonce = true;
1604     }
1606     // Exclude from transformations all the code inside <script> tags
1607     // Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
1608     // Based on code from glossary fiter by Williams Castillo.
1609     //       - Eloy
1611     // Detect all the <script> zones to take out
1612     $excludes = array();
1613     preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
1615     // Take out all the <script> zones from text
1616     foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
1617         $excludes['<+'.$key.'+>'] = $value;
1618     }
1619     if ($excludes) {
1620         $text = str_replace($excludes,array_keys($excludes),$text);
1621     }
1623 /// this is the meat of the code - this is run every time
1624     $text = str_replace($e, $img, $text);
1626     // Recover all the <script> zones to text
1627     if ($excludes) {
1628         $text = str_replace(array_keys($excludes),$excludes,$text);
1629     }
1632 /**
1633  * Given plain text, makes it into HTML as nicely as possible.
1634  * May contain HTML tags already
1635  *
1636  * @uses $CFG
1637  * @param string $text The string to convert.
1638  * @param boolean $smiley Convert any smiley characters to smiley images?
1639  * @param boolean $para If true then the returned string will be wrapped in paragraph tags
1640  * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
1641  * @return string
1642  */
1644 function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
1645 ///
1647     global $CFG;
1649 /// Remove any whitespace that may be between HTML tags
1650     $text = eregi_replace(">([[:space:]]+)<", "><", $text);
1652 /// Remove any returns that precede or follow HTML tags
1653     $text = eregi_replace("([\n\r])<", " <", $text);
1654     $text = eregi_replace(">([\n\r])", "> ", $text);
1656     convert_urls_into_links($text);
1658 /// Make returns into HTML newlines.
1659     if ($newlines) {
1660         $text = nl2br($text);
1661     }
1663 /// Turn smileys into images.
1664     if ($smiley) {
1665         replace_smilies($text);
1666     }
1668 /// Wrap the whole thing in a paragraph tag if required
1669     if ($para) {
1670         return '<p>'.$text.'</p>';
1671     } else {
1672         return $text;
1673     }
1676 /**
1677  * Given Markdown formatted text, make it into XHTML using external function
1678  *
1679  * @uses $CFG
1680  * @param string $text The markdown formatted text to be converted.
1681  * @return string Converted text
1682  */
1683 function markdown_to_html($text) {
1684     global $CFG;
1686     require_once($CFG->libdir .'/markdown.php');
1688     return Markdown($text);
1691 /**
1692  * Given HTML text, make it into plain text using external function
1693  *
1694  * @uses $CFG
1695  * @param string $html The text to be converted.
1696  * @return string
1697  */
1698 function html_to_text($html) {
1700     global $CFG;
1702     require_once($CFG->libdir .'/html2text.php');
1704     return html2text($html);
1707 /**
1708  * Given some text this function converts any URLs it finds into HTML links
1709  *
1710  * @param string $text Passed in by reference. The string to be searched for urls.
1711  */
1712 function convert_urls_into_links(&$text) {
1713 /// Make lone URLs into links.   eg http://moodle.com/
1714     $text = eregi_replace("([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])",
1715                           "\\1<a href=\"\\2://\\3\\4\" target=\"_blank\">\\2://\\3\\4</a>", $text);
1717 /// eg www.moodle.com
1718     $text = eregi_replace("([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])",
1719                           "\\1<a href=\"http://www.\\2\\3\" target=\"_blank\">www.\\2\\3</a>", $text);
1722 /**
1723  * This function will highlight search words in a given string
1724  * It cares about HTML and will not ruin links.  It's best to use
1725  * this function after performing any conversions to HTML.
1726  * Function found here: http://forums.devshed.com/t67822/scdaa2d1c3d4bacb4671d075ad41f0854.html
1727  *
1728  * @param string $needle The string to search for
1729  * @param string $haystack The string to search for $needle in
1730  * @param int $case ?
1731  * @return string
1732  * @todo Finish documenting this function
1733  */
1734 function highlight($needle, $haystack, $case=0,
1735                     $left_string='<span class="highlight">', $right_string='</span>') {
1736     if (empty($needle)) {
1737         return $haystack;
1738     }
1740     //$list_of_words = eregi_replace("[^-a-zA-Z0-9&.']", " ", $needle);  // bug 3101
1741     $list_of_words = $needle;
1742     $list_array = explode(' ', $list_of_words);
1743     for ($i=0; $i<sizeof($list_array); $i++) {
1744         if (strlen($list_array[$i]) == 1) {
1745             $list_array[$i] = '';
1746         }
1747     }
1748     $list_of_words = implode(' ', $list_array);
1749     $list_of_words_cp = $list_of_words;
1750     $final = array();
1751     preg_match_all('/<(.+?)>/is',$haystack,$list_of_words);
1753     foreach (array_unique($list_of_words[0]) as $key=>$value) {
1754         $final['<|'.$key.'|>'] = $value;
1755     }
1757     $haystack = str_replace($final,array_keys($final),$haystack);
1758     $list_of_words_cp = eregi_replace(' +', '|', $list_of_words_cp);
1760     if ($list_of_words_cp{0}=='|') {
1761         $list_of_words_cp{0} = '';
1762     }
1763     if ($list_of_words_cp{strlen($list_of_words_cp)-1}=='|') {
1764         $list_of_words_cp{strlen($list_of_words_cp)-1}='';
1765     }
1767     $list_of_words_cp = trim($list_of_words_cp);
1769     if ($list_of_words_cp) {
1771       $list_of_words_cp = "(". $list_of_words_cp .")";
1773       if (!$case){
1774         $haystack = eregi_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1775       } else {
1776         $haystack = ereg_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1777       }
1778     }
1779     $haystack = str_replace(array_keys($final),$final,$haystack);
1781     return $haystack;
1784 /**
1785  * This function will highlight instances of $needle in $haystack
1786  * It's faster that the above function and doesn't care about
1787  * HTML or anything.
1788  *
1789  * @param string $needle The string to search for
1790  * @param string $haystack The string to search for $needle in
1791  * @return string
1792  */
1793 function highlightfast($needle, $haystack) {
1795     $parts = explode(strtolower($needle), strtolower($haystack));
1797     $pos = 0;
1799     foreach ($parts as $key => $part) {
1800         $parts[$key] = substr($haystack, $pos, strlen($part));
1801         $pos += strlen($part);
1803         $parts[$key] .= '<span class="highlight">'.substr($haystack, $pos, strlen($needle)).'</span>';
1804         $pos += strlen($needle);
1805     }
1807     return (join('', $parts));
1811 /// STANDARD WEB PAGE PARTS ///////////////////////////////////////////////////
1813 /**
1814  * Print a standard header
1815  *
1816  * @uses $USER
1817  * @uses $CFG
1818  * @uses $SESSION
1819  * @param string $title Appears at the top of the window
1820  * @param string $heading Appears at the top of the page
1821  * @param string $navigation Premade navigation string (for use as breadcrumbs links)
1822  * @param string $focus Indicates form element to get cursor focus on load eg  inputform.password
1823  * @param string $meta Meta tags to be added to the header
1824  * @param boolean $cache Should this page be cacheable?
1825  * @param string $button HTML code for a button (usually for module editing)
1826  * @param string $menu HTML code for a popup menu
1827  * @param boolean $usexml use XML for this page
1828  * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
1829  */
1830 function print_header ($title='', $heading='', $navigation='', $focus='', $meta='',
1831                        $cache=true, $button='&nbsp;', $menu='', $usexml=false, $bodytags='') {
1833     global $USER, $CFG, $THEME, $SESSION, $ME, $SITE, $HTTPSPAGEREQUIRED;
1835 /// This is an ugly hack to be replaced later by a proper global $COURSE
1836     global $course;
1837     if (!empty($course->lang)) {
1838         $CFG->courselang = $course->lang;
1839     }
1840     if (!empty($course->theme)) {
1841         if (!empty($CFG->allowcoursethemes)) {
1842             $CFG->coursetheme = $course->theme;
1843             theme_setup();
1844         }
1845     }
1847 /// We have to change some URLs in styles if we are in a $HTTPSPAGEREQUIRED page
1848     if (!empty($HTTPSPAGEREQUIRED)) {
1849         $CFG->themewww = str_replace('http', 'https', $CFG->themewww);
1850         $CFG->pixpath = str_replace('http', 'https', $CFG->pixpath);
1851         $CFG->modpixpath = str_replace('http', 'https', $CFG->modpixpath);
1852         foreach ($CFG->stylesheets as $key => $stylesheet) {
1853             $CFG->stylesheets[$key] = str_replace('http', 'https', $stylesheet);
1854         }
1855     }
1857 /// Add the required stylesheets
1858     $stylesheetshtml = '';
1859     foreach ($CFG->stylesheets as $stylesheet) {
1860         $stylesheetshtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
1861     }
1862     $meta = $stylesheetshtml.$meta;
1865     if ($navigation == 'home') {
1866         $home = true;
1867         $navigation = '';
1868     } else {
1869         $home = false;
1870     }
1872 /// This is another ugly hack to make navigation elements available to print_footer later
1873     $THEME->title      = $title;
1874     $THEME->heading    = $heading;
1875     $THEME->navigation = $navigation;
1876     $THEME->button     = $button;
1877     $THEME->menu       = $menu;
1878     $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
1880     if ($button == '') {
1881         $button = '&nbsp;';
1882     }
1884     if (!$menu and $navigation) {
1885         if (empty($CFG->loginhttps)) {
1886             $wwwroot = $CFG->wwwroot;
1887         } else {
1888             $wwwroot = str_replace('http','https',$CFG->wwwroot);
1889         }
1890         if (isset($course->id)) {
1891             $menu = user_login_string($course);
1892         } else {
1893             $menu = user_login_string($SITE);
1894         }
1895     }
1897     if (isset($SESSION->justloggedin)) {
1898         unset($SESSION->justloggedin);
1899         if (!empty($CFG->displayloginfailures)) {
1900             if (!empty($USER->username) and !isguest()) {
1901                 if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
1902                     $menu .= '&nbsp;<font size="1">';
1903                     if (empty($count->accounts)) {
1904                         $menu .= get_string('failedloginattempts', '', $count);
1905                     } else {
1906                         $menu .= get_string('failedloginattemptsall', '', $count);
1907                     }
1908                     if (isadmin()) {
1909                         $menu .= ' (<a href="'.$CFG->wwwroot.'/course/report/log/index.php'.
1910                                              '?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
1911                     }
1912                     $menu .= '</font>';
1913                 }
1914             }
1915         }
1916     }
1919     $encoding = current_charset();
1920     if (!empty($CFG->courselang)) {
1921         moodle_setlocale();
1922     }
1923     $SESSION->encoding = $encoding;
1925     $meta = '<meta http-equiv="content-type" content="text/html; charset='. $encoding .'" />'. "\n". $meta ."\n";
1926     if (!$usexml) {
1927         @header('Content-type: text/html; charset='.$encoding);
1928     }
1930     if ( get_string('thisdirection') == 'rtl' ) {
1931         $direction = ' dir="rtl"';
1932     } else {
1933         $direction = ' dir="ltr"';
1934     }
1936     if ($cache) {  // Allow caching on "back" (but not on normal clicks)
1937         @header('Cache-Control: private, pre-check=0, post-check=0, max-age=0');
1938         @header('Pragma: no-cache');
1939         @header('Expires: ');
1940     } else {       // Do everything we can to always prevent clients and proxies caching
1941         @header('Cache-Control: no-store, no-cache, must-revalidate');
1942         @header('Cache-Control: post-check=0, pre-check=0', false);
1943         @header('Pragma: no-cache');
1944         @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
1945         @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
1947         $meta .= "\n<meta http-equiv=\"pragma\" content=\"no-cache\" />";
1948         $meta .= "\n<meta http-equiv=\"expires\" content=\"0\" />";
1949     }
1950     @header('Accept-Ranges: none');
1952     if ($usexml) {       // Added by Gustav Delius / Mad Alex for MathML output
1953                          // Modified by Julian Sedding
1954         $currentlanguage = current_language();
1955         $mathplayer = preg_match("/MathPlayer/i", $_SERVER['HTTP_USER_AGENT']);
1956         if(!$mathplayer) {
1957             header('Content-Type: application/xhtml+xml');
1958         }
1959         echo '<?xml version="1.0" ?>'."\n";
1960         if (!empty($CFG->xml_stylesheets)) {
1961             $stylesheets = explode(';', $CFG->xml_stylesheets);
1962             foreach ($stylesheets as $stylesheet) {
1963                 echo '<?xml-stylesheet type="text/xsl" href="'. $CFG->wwwroot .'/'. $stylesheet .'" ?>' . "\n";
1964             }
1965         }
1966         echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1';
1967         if (!empty($CFG->xml_doctype_extra)) {
1968             echo ' plus '. $CFG->xml_doctype_extra;
1969         }
1970         echo '//' . strtoupper($currentlanguage) . '" "'. $CFG->xml_dtd .'">'."\n";
1971         $direction = " xmlns=\"http://www.w3.org/1999/xhtml\"
1972                        xmlns:math=\"http://www.w3.org/1998/Math/MathML\"
1973                        xml:lang=\"en\"
1974                        xmlns:xlink=\"http://www.w3.org/1999/xlink\"
1975                        $direction";
1976         if($mathplayer) {
1977             $meta .= '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n";
1978             $meta .= '<!--comment required to prevent this becoming an empty tag-->'."\n";
1979             $meta .= '</object>'."\n";
1980             $meta .= '<?import namespace="math" implementation="#mathplayer" ?>' . "\n";
1981         }
1982     }
1984     // Clean up the title
1986     $title = str_replace('"', '&quot;', $title);
1987     $title = strip_tags($title);
1989     // Create class and id for this page
1991     page_id_and_class($pageid, $pageclass);
1993     if (isset($course->id)) {
1994         $pageclass .= ' course-'.$course->id;
1995     } else {
1996         $pageclass .= ' course-'.SITEID;
1997     }
1999     if (!empty($USER->editing)) {
2000         $pageclass .= ' editing';
2001     }
2003     $bodytags .= ' class="'.$pageclass.'" id="'.$pageid.'"';
2005     include ($CFG->themedir.current_theme().'/header.html');
2007     if (!empty($CFG->messaging)) {
2008         echo message_popup_window();
2009     }
2012 /**
2013  * This version of print_header is simpler because the course name does not have to be
2014  * provided explicitly in the strings. It can be used on the site page as in courses
2015  * Eventually all print_header could be replaced by print_header_simple
2016  *
2017  * @param string $title Appears at the top of the window
2018  * @param string $heading Appears at the top of the page
2019  * @param string $navigation Premade navigation string (for use as breadcrumbs links)
2020  * @param string $focus Indicates form element to get cursor focus on load eg  inputform.password
2021  * @param string $meta Meta tags to be added to the header
2022  * @param boolean $cache Should this page be cacheable?
2023  * @param string $button HTML code for a button (usually for module editing)
2024  * @param string $menu HTML code for a popup menu
2025  * @param boolean $usexml use XML for this page
2026  * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
2027  */
2028 function print_header_simple($title='', $heading='', $navigation='', $focus='', $meta='',
2029                        $cache=true, $button='&nbsp;', $menu='', $usexml=false, $bodytags='') {
2031     global $course,$CFG;                // The same hack is used in print_header
2033     $shortname ='';
2034     if ($course->category) {
2035         $shortname = '<a href="'.$CFG->wwwroot.'/course/view.php?id='. $course->id .'">'. $course->shortname .'</a> ->';
2036     }
2038     print_header($course->shortname .': '. $title, $course->fullname .' '. $heading, $shortname .' '. $navigation, $focus, $meta,
2039                        $cache, $button, $menu, $usexml, $bodytags);
2043 /**
2044  * Can provide a course object to make the footer contain a link to
2045  * to the course home page, otherwise the link will go to the site home
2046  *
2047  * @uses $CFG
2048  * @uses $USER
2049  * @param course $course {@link $COURSE} object containing course information
2050  * @param ? $usercourse ?
2051  * @todo Finish documenting this function
2052  */
2053 function print_footer($course=NULL, $usercourse=NULL) {
2054     global $USER, $CFG, $THEME;
2056 /// Course links
2057     if ($course) {
2058         if (is_string($course) && $course == 'none') {          // Don't print any links etc
2059             $homelink = '';
2060             $loggedinas = '';
2061             $home  = false;
2062         } else if (is_string($course) && $course == 'home') {   // special case for site home page - please do not remove
2063             $course = get_site();
2064             $homelink  = '<div class="sitelink">'.
2065                '<a title="moodle '. $CFG->release .' ('. $CFG->version .')" href="http://moodle.org/" target="_blank">'.
2066                '<br /><img width="100" height="30" src="pix/moodlelogo.gif" border="0" alt="moodlelogo" /></a></div>';
2067             $home  = true;
2068         } else {
2069             $homelink = '<div class="homelink"><a target="'.$CFG->framename.'" href="'.$CFG->wwwroot.
2070                         '/course/view.php?id='.$course->id.'">'.$course->shortname.'</a></div>';
2071             $home  = false;
2072         }
2073     } else {
2074         $course = get_site();  // Set course as site course by default
2075         $homelink = '<div class="homelink"><a target="'.$CFG->framename.'" href="'.$CFG->wwwroot.'/">'.get_string('home').'</a></div>';
2076         $home  = false;
2077     }
2079 /// Set up some other navigation links (passed from print_header by ugly hack)
2080     $menu        = isset($THEME->menu) ? str_replace('navmenu', 'navmenufooter', $THEME->menu) : '';
2081     $title       = isset($THEME->title) ? $THEME->title : '';
2082     $button      = isset($THEME->button) ? $THEME->button : '';
2083     $heading     = isset($THEME->heading) ? $THEME->heading : '';
2084     $navigation  = isset($THEME->navigation) ? $THEME->navigation : '';
2085     $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
2088 /// Set the user link if necessary
2089     if (!$usercourse and is_object($course)) {
2090         $usercourse = $course;
2091     }
2093     if (!isset($loggedinas)) {
2094         $loggedinas = user_login_string($usercourse, $USER);
2095     }
2097     if ($loggedinas == $menu) {
2098         $menu = '';
2099     }
2101 /// Provide some performance info if required
2102     $performanceinfo = '';
2103     if (defined('MDL_PERF') || $CFG->debug > 7 || $CFG->perfdebug > 7) {
2104         $perf = get_performance_info();
2105         if (defined('MDL_PERFTOLOG')) {
2106             error_log("PERF: " . $perf['txt']);
2107         }
2108         if (defined('MDL_PERFTOFOOT') || $CFG->debug > 7 || $CFG->perfdebug > 7) {
2109             $performanceinfo = $perf['html'];
2110         }
2111     }
2114 /// Include the actual footer file
2116     include ($CFG->themedir.current_theme().'/footer.html');
2120 /**
2121  * Returns the name of the current theme
2122  *
2123  * @uses $CFG
2124  * @param $USER
2125  * @param $SESSION
2126  * @return string
2127  */
2128 function current_theme() {
2129     global $CFG, $USER, $SESSION, $course;
2131     if (!empty($CFG->pagetheme)) {  // Page theme is for special page-only themes set by code
2132         return $CFG->pagetheme;
2134     } else if (!empty($CFG->coursetheme) and !empty($CFG->allowcoursethemes)) {  // Course themes override others
2135         return $CFG->coursetheme;
2137     } else if (!empty($SESSION->theme)) {    // Session theme can override other settings
2138         return $SESSION->theme;
2140     } else if (!empty($USER->theme) and !empty($CFG->allowuserthemes)) {    // User theme can override site theme
2141         return $USER->theme;
2143     } else {
2144         return $CFG->theme;
2145     }
2149 /**
2150  * This function is called by stylesheets to set up the header
2151  * approriately as well as the current path
2152  *
2153  * @uses $CFG
2154  * @param int $lastmodified ?
2155  * @param int $lifetime ?
2156  * @param string $thename ?
2157  */
2158 function style_sheet_setup($lastmodified=0, $lifetime=300, $themename='', $forceconfig='', $lang='') {
2160     global $CFG, $THEME;
2162     header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmodified) . ' GMT');
2163     header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
2164     header('Cache-control: max_age = '. $lifetime);
2165     header('Pragma: ');
2166     header('Content-type: text/css');  // Correct MIME type
2168     $DEFAULT_SHEET_LIST = array('styles_layout', 'styles_fonts', 'styles_color');
2170     if (empty($themename)) {
2171         $themename = current_theme();  // So we have something.  Normally not needed.
2172     } else {
2173         $themename = clean_param($themename, PARAM_SAFEDIR);
2174     }
2176     if (!empty($forceconfig)) {        // Page wants to use the config from this theme instead
2177         unset($THEME);
2178         include($CFG->themedir.$forceconfig.'/'.'config.php');
2179     }
2181 /// If this is the standard theme calling us, then find out what sheets we need
2183     if ($themename == 'standard') {
2184         if (!isset($THEME->standardsheets) or $THEME->standardsheets === true) { // Use all the sheets we have
2185             $THEME->sheets = $DEFAULT_SHEET_LIST;
2186         } else if (empty($THEME->standardsheets)) {                              // We can stop right now!
2187             echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2188             exit;
2189         } else {                                                                 // Use the provided subset only
2190             $THEME->sheets = $THEME->standardsheets;
2191         }
2193 /// If we are a parent theme, then check for parent definitions
2195     } else if (!empty($THEME->parent) && $themename == $THEME->parent) {
2196         if (!isset($THEME->parentsheets) or $THEME->parentsheets === true) {     // Use all the sheets we have
2197             $THEME->sheets = $DEFAULT_SHEET_LIST;
2198         } else if (empty($THEME->parentsheets)) {                                // We can stop right now!
2199             echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2200             exit;
2201         } else {                                                                 // Use the provided subset only
2202             $THEME->sheets = $THEME->parentsheets;
2203         }
2204     }
2206 /// Work out the last modified date for this theme
2208     foreach ($THEME->sheets as $sheet) {
2209         if (file_exists($CFG->themedir.$themename.'/'.$sheet.'.css')) {
2210             $sheetmodified = filemtime($CFG->themedir.$themename.'/'.$sheet.'.css');
2211             if ($sheetmodified > $lastmodified) {
2212                 $lastmodified = $sheetmodified;
2213             }
2214         }
2215     }
2218 /// Get a list of all the files we want to include
2219     $files = array();
2221     foreach ($THEME->sheets as $sheet) {
2222         $files[] = array($CFG->themedir, $themename.'/'.$sheet.'.css');
2223     }
2225     if ($themename == 'standard') {          // Add any standard styles included in any modules
2226         if (!empty($THEME->modsheets)) {     // Search for styles.php within activity modules
2227             if ($mods = get_list_of_plugins('mod')) {
2228                 foreach ($mods as $mod) {
2229                     if (file_exists($CFG->dirroot.'/mod/'.$mod.'/styles.php')) {
2230                         $files[] = array($CFG->dirroot, '/mod/'.$mod.'/styles.php');
2231                     }
2232                 }
2233             }
2234         }
2236         if (!empty($THEME->blocksheets)) {     // Search for styles.php within block modules
2237             if ($mods = get_list_of_plugins('blocks')) {
2238                 foreach ($mods as $mod) {
2239                     if (file_exists($CFG->dirroot.'/blocks/'.$mod.'/styles.php')) {
2240                         $files[] = array($CFG->dirroot, '/blocks/'.$mod.'/styles.php');
2241                     }
2242                 }
2243             }
2244         }
2246         if (!empty($THEME->langsheets)) {     // Search for styles.php within the current language
2247             if (file_exists($CFG->dirroot.'/lang/'.$lang.'/styles.php')) {
2248                 $files[] = array($CFG->dirroot, '/lang/'.$lang.'/styles.php');
2249             }
2250         }
2251     }
2254     if ($files) {
2255     /// Produce a list of all the files first
2256         echo '/**************************************'."\n";
2257         echo ' * THEME NAME: '.$themename."\n *\n";
2258         echo ' * Files included in this sheet:'."\n *\n";
2259         foreach ($files as $file) {
2260             echo ' *   '.$file[1]."\n";
2261         }
2262         echo ' **************************************/'."\n\n";
2265     /// Actually output all the files in order.
2266         foreach ($files as $file) {
2267             echo '/***** '.$file[1].' start *****/'."\n\n";
2268             @include_once($file[0].$file[1]);
2269             echo '/***** '.$file[1].' end *****/'."\n\n";
2270         }
2271     }
2273     return $CFG->themewww.$themename;   // Only to help old themes (1.4 and earlier)
2277 function theme_setup($theme = '', $params=NULL) {
2278 /// Sets up global variables related to themes
2280     global $CFG, $THEME, $SESSION, $USER;
2282     if (empty($theme)) {
2283         $theme = current_theme();
2284     }
2286 /// Load up the theme config
2287     $THEME = NULL;   // Just to be sure
2288     include($CFG->themedir. $theme .'/config.php');  // Main config for current theme
2290 /// Put together the parameters
2291     if (!$params) {
2292         $params = array();
2293     }
2294     if ($theme != $CFG->theme) {
2295         $params[] = 'forceconfig='.$theme;
2296     }
2298 /// Force language too if required
2299     if (!empty($THEME->langsheets)) {
2300         $params[] = 'lang='.current_language();
2301     }
2303 /// Convert params to string
2304     if ($params) {
2305         $paramstring = '?'.implode('&', $params);
2306     } else {
2307         $paramstring = '';
2308     }
2310 /// Set up image paths
2311     if (empty($THEME->custompix)) {    // Could be set in the above file
2312         $CFG->pixpath = $CFG->wwwroot .'/pix';
2313         $CFG->modpixpath = $CFG->wwwroot .'/mod';
2314     } else {
2315         $CFG->pixpath = $CFG->themewww . $theme .'/pix';
2316         $CFG->modpixpath = $CFG->themewww . $theme .'/pix/mod';
2317     }
2319 /// Header and footer paths
2320     $CFG->header = $CFG->themedir . $theme .'/header.html';
2321     $CFG->footer = $CFG->themedir . $theme .'/footer.html';
2323 /// Define stylesheet loading order
2324     $CFG->stylesheets = array();
2325     if ($theme != 'standard') {    /// The standard sheet is always loaded first
2326         $CFG->stylesheets[] = $CFG->themewww.'standard/styles.php'.$paramstring;
2327     }
2328     if (!empty($THEME->parent)) {  /// Parent stylesheets are loaded next
2329         $CFG->stylesheets[] = $CFG->themewww.$THEME->parent.'/styles.php'.$paramstring;
2330     }
2331     $CFG->stylesheets[] = $CFG->themewww.$theme.'/styles.php'.$paramstring;
2336 /**
2337  * Returns text to be displayed to the user which reflects their login status
2338  *
2339  * @uses $CFG
2340  * @uses $USER
2341  * @param course $course {@link $COURSE} object containing course information
2342  * @param user $user {@link $USER} object containing user information
2343  * @return string
2344  */
2345 function user_login_string($course=NULL, $user=NULL) {
2346     global $USER, $CFG, $SITE;
2348     if (empty($user) and isset($USER->id)) {
2349         $user = $USER;
2350     }
2352     if (empty($course)) {
2353         $course = $SITE;
2354     }
2356     if (isset($user->realuser)) {
2357         if ($realuser = get_record('user', 'id', $user->realuser)) {
2358             $fullname = fullname($realuser, true);
2359             $realuserinfo = " [<a target=\"{$CFG->framename}\"
2360             href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&amp;return=1\">$fullname</a>] ";
2361         }
2362     } else {
2363         $realuserinfo = '';
2364     }
2366     if (empty($CFG->loginhttps)) {
2367         $wwwroot = $CFG->wwwroot;
2368     } else {
2369         $wwwroot = str_replace('http','https',$CFG->wwwroot);
2370     }
2372     if (isset($user->id) and $user->id) {
2373         $fullname = fullname($user, true);
2374         $username = "<a target=\"{$CFG->framename}\" href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>";
2375         $instudentview = (!empty($USER->studentview)) ? get_string('instudentview') : '';
2376         if (isguest($user->id)) {
2377             $loggedinas = $realuserinfo.get_string('loggedinasguest').
2378                       " (<a target=\"{$CFG->framename}\" href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
2379         } else {
2380             $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '.$instudentview.
2381                       " (<a target=\"{$CFG->framename}\" href=\"$CFG->wwwroot/login/logout.php\">".get_string('logout').'</a>)';
2382         }
2383     } else {
2384         $loggedinas = get_string('loggedinnot', 'moodle').
2385                       " (<a target=\"{$CFG->framename}\" href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
2386     }
2387     return '<div class="logininfo">'.$loggedinas.'</div>';
2390 /**
2391  * Prints breadcrumbs links
2392  *
2393  * @uses $CFG
2394  * @param string $navigation The breadcrumbs string to be printed
2395  */
2396 function print_navigation ($navigation) {
2397    global $CFG, $USER;
2399    if ($navigation) {
2400        //Accessibility: breadcrumb links now in a list, &raquo; replaced with image.
2401            $nav_text = get_string('youarehere','access');
2402            echo '<span class="accesshide">'.$nav_text.':</span><ul>';
2403        if (! $site = get_site()) {
2404            $site->shortname = get_string('home');
2405        }
2406        $navigation = '<li title="'.$nav_text.'"><img src="'.$CFG->pixpath.'/a/r_breadcrumb.gif" class="resize" alt="" /> '
2407            .str_replace('->', '</li><li title="'.$nav_text.'"><img src="'.$CFG->pixpath.'/a/r_breadcrumb.gif" class="resize" alt="" /> ', $navigation)."</li>\n";
2408        echo '<li class="first"><a target="'. $CFG->framename .'" href="'. $CFG->wwwroot.((!isadmin() && !empty($USER->id) && !empty($CFG->mymoodleredirect) && !isguest())
2409                                                                        ? '/my' : '') .'/">'. $site->shortname ."</a></li>\n". $navigation;
2410        echo "</ul>\n";  
2411    }
2414 /**
2415  * Prints a string in a specified size  (retained for backward compatibility)
2416  *
2417  * @param string $text The text to be displayed
2418  * @param int $size The size to set the font for text display.
2419  */
2420 function print_headline($text, $size=2) {
2421     print_heading($text, 'left', $size);
2424 /**
2425  * Prints text in a format for use in headings.
2426  *
2427  * @param string $text The text to be displayed
2428  * @param string $align The alignment of the printed paragraph of text
2429  * @param int $size The size to set the font for text display.
2430  */
2431 function print_heading($text, $align='', $size=2, $class='main') {
2432     if ($align) {
2433         $align = ' align="'.$align.'"';
2434     }
2435     if ($class) {
2436         $class = ' class="'.$class.'"';
2437     }
2438     echo "<h$size $align $class>".stripslashes_safe($text)."</h$size>";
2441 /**
2442  * Centered heading with attached help button (same title text)
2443  * and optional icon attached
2444  *
2445  * @param string $text The text to be displayed
2446  * @param string $helppage The help page to link to
2447  * @param string $module The module whose help should be linked to
2448  * @param string $icon Image to display if needed
2449  */
2450 function print_heading_with_help($text, $helppage, $module='moodle', $icon='') {
2451     echo '<h2 class="main help">'.$icon.stripslashes_safe($text);
2452     helpbutton($helppage, $text, $module);
2453     echo '</h2>';
2457 function print_heading_block($heading, $class='') {
2458     //Accessibility: 'headingblock' is now H1, see theme/standard/styles_*.css: ??
2459     echo '<h2 class="headingblock header '.$class.'">'.stripslashes($heading).'</h2>';
2463 /**
2464  * Print a link to continue on to another page.
2465  *
2466  * @uses $CFG
2467  * @param string $link The url to create a link to.
2468  */
2469 function print_continue($link) {
2471     global $CFG;
2473     if (!$link) {
2474         $link = $_SERVER['HTTP_REFERER'];
2475     }
2477     echo '<div class="continuebutton">';
2478     print_single_button($link, NULL, get_string('continue'), 'post', $CFG->framename);
2479     echo '</div>'."\n";
2482 /**
2483  * Print a message in a standard themed box.
2484  *
2485  * @param string $message ?
2486  * @param string $align ?
2487  * @param string $width ?
2488  * @param string $color ?
2489  * @param int $padding ?
2490  * @param string $class ?
2491  * @todo Finish documenting this function
2492  */
2493 function print_simple_box($message, $align='', $width='', $color='', $padding=5, $class='generalbox', $id='') {
2494     print_simple_box_start($align, $width, $color, $padding, $class, $id);
2495     echo stripslashes_safe($message);
2496     print_simple_box_end();
2499 /**
2500  * Print the top portion of a standard themed box.
2501  *
2502  * @param string $align ?
2503  * @param string $width ?
2504  * @param string $color ?
2505  * @param int $padding ?
2506  * @param string $class ?
2507  * @todo Finish documenting this function
2508  */
2509 function print_simple_box_start($align='', $width='', $color='', $padding=5, $class='generalbox', $id='') {
2511     if ($color) {
2512         $color = 'bgcolor="'. $color .'"';
2513     }
2514     if ($align) {
2515         $align = 'align="'. $align .'"';
2516     }
2517     if ($width) {
2518         $width = 'width="'. $width .'"';
2519     }
2520     if ($id) {
2521         $id = 'id="'. $id .'"';
2522     }
2523     echo "<table $align $width $id class=\"$class\" border=\"0\" cellpadding=\"$padding\" cellspacing=\"0\">".
2524          "<tr><td $color class=\"$class"."content\">";
2527 /**
2528  * Print the end portion of a standard themed box.
2529  */
2530 function print_simple_box_end() {
2531     echo '</td></tr></table>';
2534 /**
2535  * Print a self contained form with a single submit button.
2536  *
2537  * @param string $link ?
2538  * @param array $options ?
2539  * @param string $label ?
2540  * @param string $method ?
2541  * @todo Finish documenting this function
2542  */
2543 function print_single_button($link, $options, $label='OK', $method='get', $target='_self') {
2544     echo '<div class="singlebutton">';
2545     echo '<form action="'. $link .'" method="'. $method .'" target="'.$target.'">';
2546     if ($options) {
2547         foreach ($options as $name => $value) {
2548             echo '<input type="hidden" name="'. $name .'" value="'. $value .'" />';
2549         }
2550     }
2551     echo '<input type="submit" value="'. $label .'" /></form></div>';
2554 /**
2555  * Print a spacer image with the option of including a line break.
2556  *
2557  * @param int $height ?
2558  * @param int $width ?
2559  * @param boolean $br ?
2560  * @todo Finish documenting this function
2561  */
2562 function print_spacer($height=1, $width=1, $br=true) {
2563     global $CFG;
2564     echo '<img class="spacer" height="'. $height .'" width="'. $width .'" src="'. $CFG->wwwroot .'/pix/spacer.gif" alt="" />';
2565     if ($br) {
2566         echo '<br />'."\n";
2567     }
2570 /**
2571  * Given the path to a picture file in a course, or a URL,
2572  * this function includes the picture in the page.
2573  *
2574  * @param string $path ?
2575  * @param int $courseid ?
2576  * @param int $height ?
2577  * @param int $width ?
2578  * @param string $link ?
2579  * @todo Finish documenting this function
2580  */
2581 function print_file_picture($path, $courseid=0, $height='', $width='', $link='') {
2582     global $CFG;
2584     if ($height) {
2585         $height = 'height="'. $height .'"';
2586     }
2587     if ($width) {
2588         $width = 'width="'. $width .'"';
2589     }
2590     if ($link) {
2591         echo '<a href="'. $link .'">';
2592     }
2593     if (substr(strtolower($path), 0, 7) == 'http://') {
2594         echo '<img border="0" '.$height . $width .' src="'. $path .'" />';
2596     } else if ($courseid) {
2597         echo '<img border="0" '. $height . $width .' src="';
2598         if ($CFG->slasharguments) {        // Use this method if possible for better caching
2599             echo $CFG->wwwroot .'/file.php/'. $courseid .'/'. $path;
2600         } else {
2601             echo $CFG->wwwroot .'/file.php?file=/'. $courseid .'/'. $path;
2602         }
2603         echo '" />';
2604     } else {
2605         echo 'Error: must pass URL or course';
2606     }
2607     if ($link) {
2608         echo '</a>';
2609     }
2612 /**
2613  * Print the specified user's avatar.
2614  *
2615  * @param int $userid ?
2616  * @param int $courseid ?
2617  * @param boolean $picture Print the user picture?
2618  * @param int $size Size in pixels.  Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatability
2619  * @param boolean $returnstring If false print picture to current page, otherwise return the output as string
2620  * @param boolean $link Enclose printed image in a link to view specified course?
2621  * return string
2622  * @todo Finish documenting this function
2623  */
2624 function print_user_picture($userid, $courseid, $picture, $size=0, $returnstring=false, $link=true, $target='') {
2625     global $CFG;
2627     if ($link) {
2628         if ($target) {
2629             $target=' target="_blank"';
2630         }
2631         $output = '<a '.$target.' href="'. $CFG->wwwroot .'/user/view.php?id='. $userid .'&amp;course='. $courseid .'">';
2632     } else {
2633         $output = '';
2634     }
2635     if (empty($size)) {
2636         $file = 'f2';
2637         $size = 35;
2638     } else if ($size === true or $size == 1) {
2639         $file = 'f1';
2640         $size = 100;
2641     } else if ($size >= 50) {
2642         $file = 'f1';
2643     } else {
2644         $file = 'f2';
2645     }
2646     $class = "userpicture";
2647     if ($picture) {  // Print custom user picture
2648         if ($CFG->slasharguments) {        // Use this method if possible for better caching
2649             $src =  $CFG->wwwroot .'/user/pix.php/'. $userid .'/'. $file .'.jpg"';
2650         } else {
2651             $src =  $CFG->wwwroot .'/user/pix.php?file=/'. $userid .'/'. $file .'.jpg"';
2652         }
2653     } else {         // Print default user pictures (use theme version if available)
2654         $class .= " defaultuserpic";
2655         $src =  "$CFG->pixpath/u/$file.png\"";
2656     }
2657     $output .= "<img class=\"$class\" align=\"middle\" src=\"$src".
2658                    " border=\"0\" width=\"$size\" height=\"$size\" alt=\"\" />";
2659     if ($link) {
2660         $output .= '</a>';
2661     }
2663     if ($returnstring) {
2664         return $output;
2665     } else {
2666         echo $output;
2667     }
2670 /**
2671  * Prints a summary of a user in a nice little box.
2672  *
2673  * @uses $CFG
2674  * @uses $USER
2675  * @param user $user A {@link $USER} object representing a user
2676  * @param course $course A {@link $COURSE} object representing a course
2677  */
2678 function print_user($user, $course, $messageselect=false) {
2680     global $CFG, $USER;
2682     static $string;
2683     static $datestring;
2684     static $countries;
2685     static $isteacher;
2686     static $isadmin;
2688     if (empty($string)) {     // Cache all the strings for the rest of the page
2690         $string->email       = get_string('email');
2691         $string->location    = get_string('location');
2692         $string->lastaccess  = get_string('lastaccess');
2693         $string->activity    = get_string('activity');
2694         $string->unenrol     = get_string('unenrol');
2695         $string->loginas     = get_string('loginas');
2696         $string->fullprofile = get_string('fullprofile');
2697         $string->role        = get_string('role');
2698         $string->name        = get_string('name');
2699         $string->never       = get_string('never');
2701         $datestring->day     = get_string('day');
2702         $datestring->days    = get_string('days');
2703         $datestring->hour    = get_string('hour');
2704         $datestring->hours   = get_string('hours');
2705         $datestring->min     = get_string('min');
2706         $datestring->mins    = get_string('mins');
2707         $datestring->sec     = get_string('sec');
2708         $datestring->secs    = get_string('secs');
2710         $countries = get_list_of_countries();
2712         $isteacher = isteacher($course->id);
2713         $isadmin   = isadmin();
2714     }
2716 /// Get the hidden field list
2717     if ($isteacher || $isadmin) {
2718         $hiddenfields = array();
2719     } else {
2720         $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
2721     }
2723     echo '<table class="userinfobox">';
2724     echo '<tr>';
2725     echo '<td class="left side">';
2726     print_user_picture($user->id, $course->id, $user->picture, true);
2727     echo '</td>';
2728     echo '<td class="content">';
2729     echo '<div class="username">'.fullname($user, $isteacher).'</div>';
2730     echo '<div class="info">';
2731     if (!empty($user->role) and ($user->role <> $course->teacher)) {
2732         echo $string->role .': '. $user->role .'<br />';
2733     }
2734     if ($user->maildisplay == 1 or ($user->maildisplay == 2 and $course->category and !isguest()) or $isteacher) {
2735         echo $string->email .': <a href="mailto:'. $user->email .'">'. $user->email .'</a><br />';
2736     }
2737     if (($user->city or $user->country) and (!isset($hiddenfields['city']) or !isset($hiddenfields['country']))) {
2738         echo $string->location .': ';
2739         if ($user->city && !isset($hiddenfields['city'])) {
2740             echo $user->city;
2741         }
2742         if (!empty($countries[$user->country]) && !isset($hiddenfields['country'])) {
2743             if ($user->city && !isset($hiddenfields['city'])) {
2744                 echo ', ';
2745             }
2746             echo $countries[$user->country];
2747         }
2748         echo '<br />';
2749     }
2750     if (!isset($hiddenfields['lastaccess'])) {
2751         if ($user->lastaccess) {
2752             echo $string->lastaccess .': '. userdate($user->lastaccess);
2753             echo '&nbsp ('. format_time(time() - $user->lastaccess, $datestring) .')';
2754         } else {
2755             echo $string->lastaccess .': '. $string->never;
2756         }
2757     }
2758     echo '</div></td><td class="links">';
2760     if ($isteacher) {
2761         $timemidnight = usergetmidnight(time());
2762         echo '<a href="'. $CFG->wwwroot .'/course/user.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->activity .'</a><br />';
2763         if (!iscreator($user->id) or ($isadmin and !isadmin($user->id))) {  // Includes admins
2764             if ($course->category and isteacheredit($course->id) and isstudent($course->id, $user->id)) {  // Includes admins
2765                 echo '<a href="'. $CFG->wwwroot .'/course/unenrol.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->unenrol .'</a><br />';
2766             }
2767             if ($USER->id != $user->id) {
2768                 echo '<a href="'. $CFG->wwwroot .'/course/loginas.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->loginas .'</a><br />';
2769             }
2770         }
2771     }
2772     echo '<a href="'. $CFG->wwwroot .'/user/view.php?id='. $user->id .'&amp;course='. $course->id .'">'. $string->fullprofile .'...</a>';
2774     if (!empty($messageselect) && $isteacher) {
2775         echo '<br /><input type="checkbox" name="';
2776         if (isteacher($course->id, $user->id)) {
2777             echo 'teacher';
2778         } else {
2779             echo 'user';
2780         }
2781         echo $user->id.'" /> ';
2782     }
2784     echo '</td></tr></table>';
2787 /**
2788  * Print a specified group's avatar.
2789  *
2790  * @param group $group A {@link group} object representing a group
2791  * @param int $courseid ?
2792  * @param boolean $large ?
2793  * @param boolean $returnstring ?
2794  * @param boolean $link ?
2795  * @return string
2796  * @todo Finish documenting this function
2797  */
2798 function print_group_picture($group, $courseid, $large=false, $returnstring=false, $link=true) {
2799     global $CFG;
2801     static $isteacheredit;
2803     if (!isset($isteacheredit)) {
2804         $isteacheredit = isteacheredit($courseid);
2805     }
2807     if ($group->hidepicture and !$isteacheredit) {
2808         return '';
2809     }
2811     if ($link or $isteacheredit) {
2812         $output = '<a href="'. $CFG->wwwroot .'/user/index.php?id='. $courseid .'&amp;group='. $group->id .'">';
2813     } else {
2814         $output = '';
2815     }
2816     if ($large) {
2817         $file = 'f1';
2818         $size = 100;
2819     } else {
2820         $file = 'f2';
2821         $size = 35;
2822     }
2823     if ($group->picture) {  // Print custom group picture
2824         if ($CFG->slasharguments) {        // Use this method if possible for better caching
2825             $output .= '<img class="grouppicture" align="middle" src="'.$CFG->wwwroot.'/user/pixgroup.php/'.$group->id.'/'.$file.'.jpg"'.
2826                        ' border="0" width="'.$size.'" height="'.$size.'" alt="" title="'.s($group->name).'"/>';
2827         } else {
2828             $output .= '<img class="grouppicture" align="middle" src="'.$CFG->wwwroot.'/user/pixgroup.php?file=/'.$group->id.'/'.$file.'.jpg"'.
2829                        ' border="0" width="'.$size.'" height="'.$size.'" alt="" title="'.s($group->name).'"/>';
2830         }
2831     }
2832     if ($link or $isteacheredit) {
2833         $output .= '</a>';
2834     }
2836     if ($returnstring) {
2837         return $output;
2838     } else {
2839         echo $output;
2840     }
2843 /**
2844  * Print a png image.
2845  *
2846  * @param string $url ?
2847  * @param int $sizex ?
2848  * @param int $sizey ?
2849  * @param boolean $returnstring ?
2850  * @param string $parameters ?
2851  * @todo Finish documenting this function
2852  */
2853 function print_png($url, $sizex, $sizey, $returnstring, $parameters='alt=""') {
2854     global $CFG;
2855     static $recentIE;
2857     if (!isset($recentIE)) {
2858         $recentIE = check_browser_version('MSIE', '5.0');
2859     }
2861     if ($recentIE) {  // work around the HORRIBLE bug IE has with alpha transparencies
2862         $output .= '<img src="'. $CFG->pixpath .'/spacer.gif" width="'. $sizex .'" height="'. $sizey .'"'.
2863                    ' border="0" class="png" style="width: '. $sizex .'px; height: '. $sizey .'px; '.
2864                    ' filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='.
2865                    "'$url', sizingMethod='scale') ".
2866                    ' '. $parameters .' />';
2867     } else {
2868         $output .= '<img src="'. $url .'" border="0" width="'. $sizex .'" height="'. $sizey .'" '.
2869                    ' '. $parameters .' />';
2870     }
2872     if ($returnstring) {
2873         return $output;
2874     } else {
2875         echo $output;
2876     }
2879 /**
2880  * Print a nicely formatted table.
2881  *
2882  * @param array $table is an object with several properties.
2883  *     <ul<li>$table->head - An array of heading names.
2884  *     <li>$table->align - An array of column alignments
2885  *     <li>$table->size  - An array of column sizes
2886  *     <li>$table->wrap - An array of "nowrap"s or nothing
2887  *     <li>$table->data[] - An array of arrays containing the data.
2888  *     <li>$table->width  - A percentage of the page
2889  *     <li>$table->cellpadding  - Padding on each cell
2890  *     <li>$table->cellspacing  - Spacing between cells
2891  * </ul>
2892  * @return boolean
2893  * @todo Finish documenting this function
2894  */
2895 function print_table($table) {
2897     if (isset($table->align)) {
2898         foreach ($table->align as $key => $aa) {
2899             if ($aa) {
2900                 $align[$key] = ' align="'. $aa .'"';
2901             } else {
2902                 $align[$key] = '';
2903             }
2904         }
2905     }
2906     if (isset($table->size)) {
2907         foreach ($table->size as $key => $ss) {
2908             if ($ss) {
2909                 $size[$key] = ' width="'. $ss .'"';
2910             } else {
2911                 $size[$key] = '';
2912             }
2913         }
2914     }
2915     if (isset($table->wrap)) {
2916         foreach ($table->wrap as $key => $ww) {
2917             if ($ww) {
2918                 $wrap[$key] = ' nowrap="nowrap" ';
2919             } else {
2920                 $wrap[$key] = '';
2921             }
2922         }
2923     }
2925     if (empty($table->width)) {
2926         $table->width = '80%';
2927     }
2929     if (empty($table->cellpadding)) {
2930         $table->cellpadding = '5';
2931     }
2933     if (empty($table->cellspacing)) {
2934         $table->cellspacing = '1';
2935     }
2937     if (empty($table->class)) {
2938         $table->class = 'generaltable';
2939     }
2941     $tableid = empty($table->id) ? '' : 'id="'.$table->id.'"';
2943     print_simple_box_start('center', $table->width, '#ffffff', 0);
2944     echo '<table width="100%" border="0" align="center" ';
2945     echo " cellpadding=\"$table->cellpadding\" cellspacing=\"$table->cellspacing\" class=\"$table->class\" $tableid>\n";
2947     $countcols = 0;
2949     if (!empty($table->head)) {
2950         $countcols = count($table->head);
2951         echo '<tr>';
2952         foreach ($table->head as $key => $heading) {
2954             if (!isset($size[$key])) {
2955                 $size[$key] = '';
2956             }
2957             if (!isset($align[$key])) {
2958                 $align[$key] = '';
2959             }
2960             echo '<th valign="top" '. $align[$key].$size[$key] .' nowrap="nowrap" class="header c'.$key.'">'. $heading .'</th>';
2961         }
2962         echo '</tr>'."\n";
2963     }
2965     if (!empty($table->data)) {
2966         $oddeven = 1;
2967         foreach ($table->data as $key => $row) {
2968             $oddeven = $oddeven ? 0 : 1;
2969             echo '<tr class="r'.$oddeven.'">'."\n";
2970             if ($row == 'hr' and $countcols) {
2971                 echo '<td colspan="'. $countcols .'"><div class="tabledivider"></div></td>';
2972             } else {  /// it's a normal row of data
2973                 foreach ($row as $key => $item) {
2974                     if (!isset($size[$key])) {
2975                         $size[$key] = '';
2976                     }
2977                     if (!isset($align[$key])) {
2978                         $align[$key] = '';
2979                     }
2980                     if (!isset($wrap[$key])) {
2981                         $wrap[$key] = '';
2982                     }
2983                     echo '<td '. $align[$key].$size[$key].$wrap[$key] .' class="cell c'.$key.'">'. $item .'</td>';
2984                 }
2985             }
2986             echo '</tr>'."\n";
2987         }
2988     }
2989     echo '</table>'."\n";
2990     print_simple_box_end();
2992     return true;
2995 /**
2996  * Creates a nicely formatted table and returns it.
2997  *
2998  * @param array $table is an object with several properties.
2999  *     <ul<li>$table->head - An array of heading names.
3000  *     <li>$table->align - An array of column alignments
3001  *     <li>$table->size  - An array of column sizes
3002  *     <li>$table->wrap - An array of "nowrap"s or nothing
3003  *     <li>$table->data[] - An array of arrays containing the data.
3004  *     <li>$table->class -  A css class name
3005  *     <li>$table->fontsize - Is the size of all the text
3006  *     <li>$table->tablealign  - Align the whole table
3007  *     <li>$table->width  - A percentage of the page
3008  *     <li>$table->cellpadding  - Padding on each cell
3009  *     <li>$table->cellspacing  - Spacing between cells
3010  * </ul>
3011  * @return string
3012  * @todo Finish documenting this function
3013  */
3014 function make_table($table) {
3016     if (isset($table->align)) {
3017         foreach ($table->align as $key => $aa) {
3018             if ($aa) {
3019                 $align[$key] = ' align="'. $aa .'"';
3020             } else {
3021                 $align[$key] = '';
3022             }
3023         }
3024     }
3025     if (isset($table->size)) {
3026         foreach ($table->size as $key => $ss) {
3027             if ($ss) {
3028                 $size[$key] = ' width="'. $ss .'"';
3029             } else {
3030                 $size[$key] = '';
3031             }
3032         }
3033     }
3034     if (isset($table->wrap)) {
3035         foreach ($table->wrap as $key => $ww) {
3036             if ($ww) {
3037                 $wrap[$key] = ' nowrap="nowrap" ';
3038             } else {
3039                 $wrap[$key] = '';
3040             }
3041         }
3042     }
3044     if (empty($table->width)) {
3045         $table->width = '80%';
3046     }
3048     if (empty($table->tablealign)) {
3049         $table->tablealign = 'center';
3050     }
3052     if (empty($table->cellpadding)) {
3053         $table->cellpadding = '5';
3054     }
3056     if (empty($table->cellspacing)) {
3057         $table->cellspacing = '1';
3058     }
3060     if (empty($table->class)) {
3061         $table->class = 'generaltable';
3062     }
3064     if (empty($table->fontsize)) {
3065         $fontsize = '';
3066     } else {
3067         $fontsize = '<font size="'. $table->fontsize .'">';
3068     }
3070     $output =  '<table width="'. $table->width .'" align="'. $table->tablealign .'" ';
3071     $output .= ' cellpadding="'. $table->cellpadding .'" cellspacing="'. $table->cellspacing .'" class="'. $table->class .'">'."\n";
3073     if (!empty($table->head)) {
3074         $output .= '<tr valign="top">';
3075         foreach ($table->head as $key => $heading) {
3076             if (!isset($size[$key])) {
3077                 $size[$key] = '';
3078             }
3079             if (!isset($align[$key])) {
3080                 $align[$key] = '';
3081             }
3082             $output .= '<th valign="top" '. $align[$key].$size[$key] .' nowrap="nowrap" class="'. $table->class .'header">'.$fontsize.$heading.'</th>';
3083         }
3084         $output .= '</tr>'."\n";
3085     }
3087     foreach ($table->data as $row) {
3088         $output .= '<tr valign="top">';
3089         foreach ($row as $key => $item) {
3090             if (!isset($size[$key])) {
3091                 $size[$key] = '';
3092             }
3093             if (!isset($align[$key])) {
3094                 $align[$key] = '';
3095             }
3096             if (!isset($wrap[$key])) {
3097                 $wrap[$key] = '';
3098             }
3099             $output .= '<td '. $align[$key].$size[$key].$wrap[$key] .' class="'. $table->class .'cell">'. $fontsize . $item .'</td>';
3100         }
3101         $output .= '</tr>'."\n";
3102     }
3103     $output .= '</table>'."\n";
3105     return $output;
3108 function print_recent_activity_note($time, $user, $isteacher, $text, $link) {
3109     static $strftimerecent;
3111     if (empty($strftimerecent)) {
3112         $strftimerecent = get_string('strftimerecent');
3113     }
3115     $date = userdate($time, $strftimerecent);
3116     $name = fullname($user, $isteacher);
3118     echo '<div class="head">';
3119     echo '<div class="date">'.$date.'</div> '.
3120          '<div class="name">'.fullname($user, $isteacher).'</div>';
3121     echo '</div>';
3122     echo '<div class="info"><a href="'.$link.'">'.format_string($text,true).'</a></div>';
3126 /**
3127  * Prints a basic textarea field.
3128  *
3129  * @uses $CFG
3130  * @param boolean $usehtmleditor ?
3131  * @param int $rows ?
3132  * @param int $cols ?
3133  * @param null $width <b>Legacy field no longer used!</b>  Set to zero to get control over mincols
3134  * @param null $height <b>Legacy field no longer used!</b>  Set to zero to get control over minrows
3135  * @param string $name ?
3136  * @param string $value ?
3137  * @param int $courseid ?
3138  * @todo Finish documenting this function
3139  */
3140 function print_textarea($usehtmleditor, $rows, $cols, $width, $height, $name, $value='', $courseid=0) {
3141 /// $width and height are legacy fields and no longer used as pixels like they used to be.
3142 /// However, you can set them to zero to override the mincols and minrows values below.
3144     global $CFG, $course;
3145     static $scriptcount; // For loading the htmlarea script only once.
3147     $mincols = 65;
3148     $minrows = 10;
3150     if ( empty($CFG->editorsrc) ) { // for backward compatibility.
3151         if (empty($courseid)) {
3152             if (!empty($course->id)) {  // search for it in global context
3153                 $courseid = $course->id;
3154             }
3155         }
3157         if (empty($scriptcount)) {
3158             $scriptcount = 0;
3159         }
3161         if ($usehtmleditor) {
3163             if (!empty($courseid) and isteacher($courseid)) {
3164                 echo ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3165                 $CFG->wwwroot .'/lib/editor/htmlarea/htmlarea.php?id='. $courseid .'"></script>'."\n" : '';
3166             } else {
3167                 echo ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3168                 $CFG->wwwroot .'/lib/editor/htmlarea/htmlarea.php"></script>'."\n" : '';
3169             }
3170             echo ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3171             $CFG->wwwroot .'/lib/editor/htmlarea/lang/en.php"></script>'."\n" : '';
3172             $scriptcount++;
3174             if ($height) {    // Usually with legacy calls
3175                 if ($rows < $minrows) {
3176                     $rows = $minrows;
3177                 }
3178             }
3179             if ($width) {    // Usually with legacy calls
3180                 if ($cols < $mincols) {
3181                     $cols = $mincols;
3182                 }
3183             }
3184         }
3185     }
3186     echo '<textarea id="edit-'. $name .'" name="'. $name .'" rows="'. $rows .'" cols="'. $cols .'">';
3187     if ($usehtmleditor) {
3188         echo htmlspecialchars(stripslashes_safe($value)); // needed for editing of cleaned text!
3189     } else {
3190         p ($value);
3191     }
3192     echo '</textarea>'."\n";
3195 /**
3196  * Legacy function, provided for backward compatability.
3197  * This method now simply calls {@link use_html_editor()}
3198  *
3199  * @deprecated Use {@link use_html_editor()} instead.
3200  * @param string $name Form element to replace with HTMl editor by name
3201  * @todo Finish documenting this function
3202  */
3203 function print_richedit_javascript($form, $name, $source='no') {
3204     use_html_editor($name);
3207 /**
3208  * Sets up the HTML editor on textareas in the current page.
3209  * If a field name is provided, then it will only be
3210  * applied to that field - otherwise it will be used
3211  * on every textarea in the page.
3212  *
3213  * In most cases no arguments need to be supplied
3214  *
3215  * @param string $name Form element to replace with HTMl editor by name
3216  */
3217 function use_html_editor($name='', $editorhidebuttons='') {
3218     echo '<script language="javascript" type="text/javascript" defer="defer">'."\n";
3219     print_editor_config($editorhidebuttons);
3220     if (empty($name)) {
3221         echo "\n".'HTMLArea.replaceAll(config);'."\n";
3222     } else {
3223         echo "\nHTMLArea.replace('edit-$name', config);\n";
3224     }
3225     echo '</script>'."\n";
3228 /**
3229  * Returns a turn edit on/off button for course in a self contained form.
3230  * Used to be an icon, but it's now a simple form button
3231  *
3232  * @uses $CFG
3233  * @uses $USER
3234  * @param int $courseid The course  to update by id as found in 'course' table
3235  * @return string
3236  */
3237 function update_course_icon($courseid) {
3239     global $CFG, $USER;
3241     if (isteacheredit($courseid)) {
3242         if (!empty($USER->editing)) {
3243             $string = get_string('turneditingoff');
3244             $edit = 'off';
3245         } else {
3246             $string = get_string('turneditingon');
3247             $edit = 'on';
3248         }
3249         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/view.php\">".
3250             "<input type=\"hidden\" name=\"id\" value=\"$courseid\" />".
3251             "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
3252             "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />".
3253             "<input type=\"submit\" value=\"$string\" /></form>";
3254     }
3257 /**
3258  * Returns a turn student view on/off button for course in a self contained form.
3259  *
3260  * @uses $CFG
3261  * @uses $USER
3262  * @param int $courseid The course  to update by id as found in 'course' table
3263  * @return string
3264  */
3265 function update_studentview_button($courseid) {
3267     global $CFG, $USER;
3269     if (isteacheredit($courseid,0,true)) {
3270         if (!empty($USER->studentview)) {
3271             $svstring = get_string('studentviewoff');
3272             $svedit = 'off';
3273         } else {
3274             $svstring = get_string('studentviewon');
3275             $svedit = 'on';
3276         }
3277         $button = "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/view.php\">".
3278             "<input type=\"hidden\" name=\"id\" value=\"$courseid\" />".
3279             "<input type=\"hidden\" name=\"studentview\" value=\"$svedit\" />".
3280             "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />".
3281             "<input type=\"submit\" value=\"$svstring\" /></form>";
3282         return $button;
3283     }
3286 /**
3287  * Returns a turn edit on/off button for course in a self contained form.
3288  * Used to be an icon, but it's now a simple form button
3289  *
3290  * @uses $CFG
3291  * @uses $USER
3292  * @param int $courseid The course  to update by id as found in 'course' table
3293  * @return string
3294  */
3295 function update_mymoodle_icon() {
3297     global $CFG, $USER;
3299     if (!empty($USER->editing)) {
3300         $string = get_string('updatemymoodleoff');
3301         $edit = 'off';
3302     } else {
3303         $string = get_string('updatemymoodleon');
3304         $edit = 'on';
3305     }
3306     return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/my/index.php\">".
3307         "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
3308         "<input type=\"submit\" value=\"$string\" /></form>";
3311 /**
3312  * Prints the editing button on a module "view" page
3313  *
3314  * @uses $CFG
3315  * @param    type description
3316  * @todo Finish documenting this function
3317  */
3318 function update_module_button($moduleid, $courseid, $string) {
3319     global $CFG, $USER;
3321     // do not display if studentview is on
3322     if (!empty($USER->studentview)) {
3323         return '';
3324     }
3326     if (isteacheredit($courseid)) {
3327         $string = get_string('updatethis', '', $string);
3328         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/mod.php\">".
3329                "<input type=\"hidden\" name=\"update\" value=\"$moduleid\" />".
3330                "<input type=\"hidden\" name=\"return\" value=\"true\" />".
3331                "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />".
3332                "<input type=\"submit\" value=\"$string\" /></form>";
3333     } else {
3334         return '';
3335     }
3338 /**
3339  * Prints the editing button on a category page
3340  *
3341  * @uses $CFG
3342  * @uses $USER
3343  * @param int $categoryid ?
3344  * @return string
3345  * @todo Finish documenting this function
3346  */
3347 function update_category_button($categoryid) {
3348     global $CFG, $USER;
3350     if (iscreator()) {
3351         if (!empty($USER->categoryediting)) {
3352             $string = get_string('turneditingoff');
3353             $edit = 'off';
3354         } else {
3355             $string = get_string('turneditingon');
3356             $edit = 'on';
3357         }
3358         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/category.php\">".
3359                "<input type=\"hidden\" name=\"id\" value=\"$categoryid\" />".
3360                "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
3361                "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />".
3362                "<input type=\"submit\" value=\"$string\" /></form>";
3363     }
3366 /**
3367  * Prints the editing button on categories listing
3368  *
3369  * @uses $CFG
3370  * @uses $USER
3371  * @return string
3372  */
3373 function update_categories_button() {
3374     global $CFG, $USER;
3376     if (isadmin()) {
3377         if (!empty($USER->categoriesediting)) {
3378             $string = get_string('turneditingoff');
3379             $edit = 'off';
3380         } else {
3381             $string = get_string('turneditingon');
3382             $edit = 'on';
3383         }
3384         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/index.php\">".
3385                '<input type="hidden" name="edit" value="'. $edit .'" />'.
3386                '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />'.
3387                '<input type="submit" value="'. $string .'" /></form>';
3388     }
3391 /**
3392  * Prints the editing button on search results listing
3393  * For bulk move courses to another category
3394  */
3396 function update_categories_search_button($search,$page,$perpage) {
3397     global $CFG, $USER;
3399     if (isadmin()) {
3400         if (!empty($USER->categoriessearchediting)) {
3401             $string = get_string("turneditingoff");
3402             $edit = "off";
3403             $perpage = 30;
3404         } else {
3405             $string = get_string("turneditingon");
3406             $edit = "on";
3407         }
3408         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/search.php\">".
3409                "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
3410                "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />".
3411                "<input type=\"hidden\" name=\"search\" value=\"$search\" />".
3412                "<input type=\"hidden\" name=\"page\" value=\"$page\" />".
3413                "<input type=\"hidden\" name=\"perpage\" value=\"$perpage\" />".
3414                "<input type=\"submit\" value=\"$string\" /></form>";
3415     }
3418 /**
3419  * Prints the editing button on group page
3420  *
3421  * @uses $CFG
3422  * @uses $USER
3423  * @param int $courseid The course group is associated with
3424  * @param int $groupid The group to update
3425  * @return string
3426  */
3427 function update_group_button($courseid, $groupid) {
3428     global $CFG, $USER;
3430     if (isteacheredit($courseid)) {
3431         $string = get_string('editgroupprofile');
3432         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/group.php\">".
3433                '<input type="hidden" name="id" value="'. $courseid .'" />'.
3434                '<input type="hidden" name="group" value="'. $groupid .'" />'.
3435                '<input type="hidden" name="edit" value="on" />'.
3436                '<input type="submit" value="'. $string .'" /></form>';
3437     }
3440 /**
3441  * Prints the editing button on groups page
3442  *
3443  * @uses $CFG
3444  * @uses $USER
3445  * @param int $courseid The id of the course to be edited
3446  * @return string
3447  * @todo Finish documenting this function
3448  */
3449 function update_groups_button($courseid) {
3450     global $CFG, $USER;
3452     if (isteacheredit($courseid)) {
3453         if (!empty($USER->groupsediting)) {
3454             $string = get_string('turneditingoff');
3455             $edit = 'off';
3456         } else {
3457             $string = get_string('turneditingon');
3458             $edit = 'on';
3459         }
3460         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/groups.php\">".
3461                "<input type=\"hidden\" name=\"id\" value=\"$courseid\" />".
3462                "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
3463                "<input type=\"submit\" value=\"$string\" /></form>";
3464     }
3467 /**
3468  * Prints an appropriate group selection menu
3469  *
3470  * @uses VISIBLEGROUPS
3471  * @param array $groups ?
3472  * @param int $groupmode ?
3473  * @param string $currentgroup ?
3474  * @param string $urlroot ?
3475  * @param boolean $showall: if set to 0, it is a student in separate groups, do not display all participants
3476  * @todo Finish documenting this function
3477  */
3478 function print_group_menu($groups, $groupmode, $currentgroup, $urlroot, $showall=1) {
3480 /// Add an "All groups" to the start of the menu
3481     if ($showall){
3482         $groupsmenu[0] = get_string('allparticipants');
3483     }
3484     foreach ($groups as $key => $groupname) {
3485         $groupsmenu[$key] = $groupname;
3486     }
3488     echo '<table><tr><td align="right">';
3489     if ($groupmode == VISIBLEGROUPS) {
3490         print_string('groupsvisible');
3491     } else {
3492         print_string('groupsseparate');
3493     }
3494     echo ':';
3495     echo '</td><td nowrap="nowrap" align="left">';
3496     popup_form($urlroot.'&amp;group=', $groupsmenu, 'selectgroup', $currentgroup, '', '', '', false, 'self');
3497     echo '</td></tr></table>';
3501 /**
3502  * Given a course and a (current) coursemodule
3503  * This function returns a small popup menu with all the
3504  * course activity modules in it, as a navigation menu
3505  * The data is taken from the serialised array stored in
3506  * the course record
3507  *
3508  * @param course $course A {@link $COURSE} object.
3509  * @param course $cm A {@link $COURSE} object.
3510  * @param string $targetwindow ?
3511  * @return string
3512  * @todo Finish documenting this function
3513  */
3514 function navmenu($course, $cm=NULL, $targetwindow='self') {
3516     global $CFG, $THEME;
3518     if (empty($THEME->navmenuwidth)) {
3519         $width = 50;
3520     } else {
3521         $width = $THEME->navmenuwidth;
3522     }
3524     if ($cm) {
3525         $cm = $cm->id;
3526     }
3528     if ($course->format == 'weeks') {
3529         $strsection = get_string('week');
3530     } else {
3531         $strsection = get_string('topic');
3532     }
3533     $strjumpto = get_string('jumpto');
3535     if (!$modinfo = unserialize($course->modinfo)) {
3536         return '';
3537     }
3538     $isteacher = isteacher($course->id);
3539     $section = -1;
3540     $selected = '';
3541     $url = '';
3542     $previousmod = NULL;
3543     $backmod = NULL;
3544     $nextmod = NULL;
3545     $selectmod = NULL;
3546     $logslink = NULL;
3547     $flag = false;
3548     $menu = array();
3550     $sections = get_records('course_sections','course',$course->id,'section','section,visible,summary');
3552     if (!empty($THEME->makenavmenulist)) {   /// A hack to produce an XHTML navmenu list for use in themes
3553         $THEME->navmenulist = navmenulist($course, $sections, $modinfo,
3554                                           $isteacher, $strsection, $strjumpto, $width, $cm);
3555     }
3557     foreach ($modinfo as $mod) {
3558         if ($mod->mod == 'label') {
3559             continue;
3560         }
3562         if ($mod->section > $course->numsections) {   /// Don't show excess hidden sections
3563             break;
3564         }
3566         if ($mod->section > 0 and $section <> $mod->section) {
3567             $thissection = $sections[$mod->section];
3569             if ($thissection->visible or !$course->hiddensections or $isteacher) {
3570                 $thissection->summary = strip_tags(format_string($thissection->summary,true));
3571                 if ($course->format == 'weeks' or empty($thissection->summary)) {
3572                     $menu[] = '-------------- '. $strsection ." ". $mod->section .' --------------';
3573                 } else {
3574                     if (strlen($thissection->summary) < ($width-3)) {
3575                         $menu[] = '-- '.$thissection->summary;
3576                     } else {
3577                         $menu[] = '-- '.substr($thissection->summary, 0, $width).'...';
3578                     }
3579                 }
3580             }
3581         }
3583         $section = $mod->section;
3585         //Only add visible or teacher mods to jumpmenu
3586         if ($mod->visible or $isteacher) {
3587             $url = $mod->mod .'/view.php?id='. $mod->cm;
3588             if ($flag) { // the current mod is the "next" mod
3589                 $nextmod = $mod;
3590                 $flag = false;
3591             }
3592             if ($cm == $mod->cm) {
3593                 $selected = $url;
3594                 $selectmod = $mod;
3595                 $backmod = $previousmod;
3596                 $flag = true; // set flag so we know to use next mod for "next"
3597                 $mod->name = $strjumpto;
3598                 $strjumpto = '';
3599             } else {
3600                 $mod->name = strip_tags(format_string(urldecode($mod->name),true));
3601                 if (strlen($mod->name) > ($width+5)) {
3602                     $mod->name = substr($mod->name, 0, $width).'...';
3603                 }
3604                 if (!$mod->visible) {
3605                     $mod->name = '('.$mod->name.')';
3606                 }
3607             }
3608             $menu[$url] = $mod->name;
3609             $previousmod = $mod;
3610         }
3611     }
3612     if ($selectmod and $isteacher) {
3613         $logslink = "<td><a target=\"$CFG->framename\" href=".
3614                     "\"$CFG->wwwroot/course/report/log/index.php?chooselog=1&amp;user=0&amp;date=0&amp;id=$course->id&amp;modid=$selectmod->cm\">".
3615                     "<img border=\"0\" height=\"16\" width=\"16\" src=\"$CFG->pixpath/i/log.gif\" alt=\"\" /></a></td>";
3617     }
3618     if ($backmod) {
3619         $backmod = "<form action=\"$CFG->wwwroot/mod/$backmod->mod/view.php\" target=\"$CFG->framename\">".
3620                    "<input type=\"hidden\" name=\"id\" value=\"$backmod->cm\" />".
3621                    "<input type=\"submit\" value=\"&lt;\" /></form>";
3622     }
3623     if ($nextmod) {
3624         $nextmod = "<form action=\"$CFG->wwwroot/mod/$nextmod->mod/view.php\" target=\"$CFG->framename\">".
3625                    "<input type=\"hidden\" name=\"id\" value=\"$nextmod->cm\" />".
3626                    "<input type=\"submit\" value=\"&gt;\" /></form>";
3627     }
3629     return '<table><tr>'.$logslink .'<td>'. $backmod .'</td><td>' .
3630             popup_form($CFG->wwwroot .'/mod/', $menu, 'navmenu', $selected, $strjumpto,
3631                        '', '', true, $targetwindow).
3632             '</td><td>'. $nextmod .'</td></tr></table>';
3636 /**
3637  * Given a course
3638  * This function returns a small popup menu with all the
3639  * course activity modules in it, as a navigation menu
3640  * outputs a simple list structure in XHTML
3641  * The data is taken from the serialised array stored in
3642  * the course record
3643  *
3644  * @param course $course A {@link $COURSE} object.
3645  * @return string
3646  * @todo Finish documenting this function
3647  */
3648 function navmenulist($course, $sections, $modinfo, $isteacher, $strsection, $strjumpto, $width=50, $cmid=0) {
3650     global $CFG;
3652     $section = -1;
3653     $selected = '';
3654     $url = '';
3655     $previousmod = NULL;
3656     $backmod = NULL;
3657     $nextmod = NULL;
3658     $selectmod = NULL;
3659     $logslink = NULL;
3660     $flag = false;
3661     $menu = array();
3663     $menu[] = '<ul class="navmenulist"><li class="jumpto section"><span>'.$strjumpto.'</span><ul>';
3664     foreach ($modinfo as $mod) {
3665         if ($mod->mod == 'label') {
3666             continue;
3667         }
3669         if ($mod->section > $course->numsections) {   /// Don't show excess hidden sections
3670             break;
3671         }
3673         if ($mod->section >= 0 and $section <> $mod->section) {
3674             $thissection = $sections[$mod->section];
3676             if ($thissection->visible or !$course->hiddensections or $isteacher) {
3677                 $thissection->summary = strip_tags(format_string($thissection->summary,true));
3678                 if (!empty($doneheading)) {
3679                     $menu[] = '</ul></li>';
3680                 }
3681                 if ($course->format == 'weeks' or empty($thissection->summary)) {
3682                     $item = $strsection ." ". $mod->section;
3683                 } else {
3684                     if (strlen($thissection->summary) < ($width-3)) {
3685                         $item = $thissection->summary;
3686                     } else {
3687                         $item = substr($thissection->summary, 0, $width).'...';
3688                     }
3689                 }
3690                 $menu[] = '<li class="section"><span>'.$item.'</span>';
3691                 $menu[] = '<ul>';
3692                 $doneheading = true;
3693             }
3694         }
3696         $section = $mod->section;
3698         //Only add visible or teacher mods to jumpmenu
3699         if ($mod->visible or $isteacher) {
3700             $url = $mod->mod .'/view.php?id='. $mod->cm;
3701             if ($flag) { // the current mod is the "next" mod
3702                 $nextmod = $mod;
3703                 $flag = false;
3704             }
3705             $mod->name = strip_tags(format_string(urldecode($mod->name),true));
3706             if (strlen($mod->name) > ($width+5)) {
3707                 $mod->name = substr($mod->name, 0, $width).'...';
3708             }
3709             if (!$mod->visible) {
3710                 $mod->name = '('.$mod->name.')';
3711             }
3712             $class = 'activity '.$mod->mod;
3713             $class .= ($cmid == $mod->cm) ? ' selected' : '';
3714             $menu[] = '<li class="'.$class.'">'.
3715                       '<img src="'.$CFG->modpixpath.'/'.$mod->mod.'/icon.gif" border="0" />'.
3716                       '<a href="'.$CFG->wwwroot.'/mod/'.$url.'">'.$mod->name.'</a></li>';
3717             $previousmod = $mod;
3718         }
3719     }
3720     if ($doneheading) {
3721         $menu[] = '</ul></li>';
3722     }
3723     $menu[] = '</ul></li></ul>';
3725     return implode("\n", $menu);
3730 /**
3731  * Prints form items with the names $day, $month and $year
3732  *
3733  * @param int $day ?
3734  * @param int $month ?
3735  * @param int $year ?
3736  * @param int $currenttime A default timestamp in GMT
3737  * @todo Finish documenting this function
3738  */
3739 function print_date_selector($day, $month, $year, $currenttime=0) {
3741     if (!$currenttime) {
3742         $currenttime = time();
3743     }
3744     $currentdate = usergetdate($currenttime);
3746     for ($i=1; $i<=31; $i++) {
3747         $days[$i] = $i;
3748     }
3749     for ($i=1; $i<=12; $i++) {
3750         $months[$i] = userdate(gmmktime(12,0,0,$i,1,2000), "%B");
3751     }
3752     for ($i=2000; $i<=2010; $i++) {
3753         $years[$i] = $i;
3754     }
3755     choose_from_menu($days,   $day,   $currentdate['mday'], '');
3756     choose_from_menu($months, $month, $currentdate['mon'],  '');
3757     choose_from_menu($years,  $year,  $currentdate['year'], '');
3760 /**
3761  *Prints form items with the names $hour and $minute
3762  *
3763  * @param ? $hour ?
3764  * @param ? $minute ?
3765  * @param $currenttime A default timestamp in GMT
3766  * @param int $step ?
3767  * @todo Finish documenting this function
3768  */
3769 function print_time_selector($hour, $minute, $currenttime=0, $step=5 ,$return=false) {
3771     if (!$currenttime) {
3772         $currenttime = time();
3773     }
3774     $currentdate = usergetdate($currenttime);
3775     if ($step != 1) {
3776         $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
3777     }
3778     for ($i=0; $i<=23; $i++) {
3779         $hours[$i] = sprintf("%02d",$i);
3780     }
3781     for ($i=0; $i<=59; $i+=$step) {
3782         $minutes[$i] = sprintf("%02d",$i);
3783     }
3785     return choose_from_menu($hours,   $hour,   $currentdate['hours'],   '','','',$return)
3786         .choose_from_menu($minutes, $minute, $currentdate['minutes'], '','','',$return);
3789 /**
3790  * Prints time limit value selector
3791  *
3792  * @uses $CFG
3793  * @param int $timelimit ?
3794  * @param string $unit ?
3795  * @todo Finish documenting this function
3796  */
3797 function print_timer_selector($timelimit = 0, $unit = '', $name = 'timelimit') {
3799     global $CFG;
3801     if ($unit) {
3802         $unit = ' '.$unit;
3803     }
3805     // Max timelimit is sessiontimeout - 10 minutes.
3806     $maxvalue = ($CFG->sessiontimeout / 60) - 10;
3808     for ($i=1; $i<=$maxvalue; $i++) {
3809         $minutes[$i] = $i.$unit;
3810     }
3811     choose_from_menu($minutes, $name, $timelimit, get_string('none'));
3814 /**
3815  * Prints a grade menu (as part of an existing form) with help
3816  * Showing all possible numerical grades and scales
3817  *
3818  * @uses $CFG
3819  * @param int $courseid ?
3820  * @param string $name ?
3821  * @param string $current ?
3822  * @param boolean $includenograde ?
3823  * @todo Finish documenting this function
3824  */
3825 function print_grade_menu($courseid, $name, $current, $includenograde=true) {
3827     global $CFG;
3829     $strscale = get_string('scale');
3830     $strscales = get_string('scales');
3832     $scales = get_scales_menu($courseid);
3833     foreach ($scales as $i => $scalename) {
3834         $grades[-$i] = $strscale .': '. $scalename;
3835     }
3836     if ($includenograde) {
3837         $grades[0] = get_string('nograde');
3838     }
3839     for ($i=100; $i>=1; $i--) {
3840         $grades[$i] = $i;
3841     }
3842     choose_from_menu($grades, $name, $current, '');
3844     $linkobject = '<span class="helplink"><img height="17" width="17" alt="'.$strscales.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
3845     link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true', 'ratingscales',
3846                           $linkobject, 400, 500, $strscales);
3849 /**
3850  * Prints a scale menu (as part of an existing form) including help button
3851  * Just like {@link print_grade_menu()} but without the numeric grades
3852  *
3853  * @param int $courseid ?
3854  * @param string $name ?
3855  * @param string $current ?
3856  * @todo Finish documenting this function
3857  */
3858 function print_scale_menu($courseid, $name, $current) {
3860     global $CFG;
3862     $strscales = get_string('scales');
3863     choose_from_menu(get_scales_menu($courseid), $name, $current, '');
3865     $linkobject = '<span class="helplink"><img height="17" width="17" alt="'.$strscales.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
3866     link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true', 'ratingscales',
3867                           $linkobject, 400, 500, $strscales);
3870 /**
3871  * Prints a help button about a scale
3872  *
3873  * @uses $CFG
3874  * @param id $courseid ?
3875  * @param object $scale ?
3876  * @todo Finish documenting this function
3877  */
3878 function print_scale_menu_helpbutton($courseid, $scale) {
3880     global $CFG;
3882     $strscales = get_string('scales');
3884     $linkobject = '<span class="helplink"><img height="17" width="17" alt="'.$scale->name.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
3885     link_to_popup_window ('/course/scales.php?id='. $courseid .'&amp;list=true&amp;scaleid='. $scale->id, 'ratingscale',
3886                           $linkobject, 400, 500, $scale->name);
3889 /**
3890  * Print an error page displaying an error message.
3891  * Old method, don't call directly in new code - use print_error instead.
3892  *
3893  *
3894  * @uses $SESSION
3895  * @uses $CFG
3896  * @param string $message The message to display to the user about the error.
3897  * @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.
3898  */
3899 function error ($message, $link='') {
3900     global $CFG, $SESSION;
3902     @header('HTTP/1.0 404 Not Found');
3904     print_header(get_string('error'));
3905     echo '<br />';
3907     $message = clean_text($message);   // In case nasties are in here
3909     print_simple_box($message, '', '', '', '', 'errorbox');
3911     if (!$link) {
3912         if ( !empty($SESSION->fromurl) ) {
3913             $link = $SESSION->fromurl;
3914             unset($SESSION->fromurl);
3915         } else {
3916             $link = $CFG->wwwroot .'/';
3917         }
3918     }
3919     print_continue($link);
3920     print_footer();
3921     for ($i=0;$i<512;$i++) {  // Padding to help IE work with 404
3922         echo ' ';
3923     }
3924     die;
3927 /**
3928  * Print an error page displaying an error message.  New method - use this for new code.
3929  *
3930  * @uses $SESSION
3931  * @uses $CFG
3932  * @param string $string The name of the string from error.php to print
3933  * @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.
3934  */
3935 function print_error ($string, $link='') {
3937     $string = get_string($string, 'error');
3938     error($string, $link);
3942 /**
3943  * Print a help button.
3944  *
3945  * @uses $CFG
3946  * @param string $page  The keyword that defines a help page
3947  * @param string $title The title of links, rollover tips, alt tags etc
3948  *           'Help with' (or the language equivalent) will be prefixed and '...' will be stripped.
3949  * @param string $module Which module is the page defined in
3950  * @param mixed $image Use a help image for the link?  (true/false/"both")
3951  * @param string $text If defined then this text is used in the page, and
3952  *           the $page variable is ignored.
3953  * @param boolean $return If true then the output is returned as a string, if false it is printed to the current page.
3954  * @param string $imagetext The full text for the helpbutton icon. If empty use default help.gif
3955  * @return string
3956  * @todo Finish documenting this function
3957  */
3958 function helpbutton ($page, $title='', $module='moodle', $image=true, $linktext=false, $text='', $return=false,
3959                      $imagetext='') {
3960     global $CFG;
3962     if ($module == '') {
3963         $module = 'moodle';
3964     }
3966     $linkobject = '<span class="helplink">';
3968     //Accessibility: prefix the alt text/title with 'Help:'
3969     $tooltip = get_string('helpprefix', '', trim($title));
3970     
3971     if ($image) {
3972         if ($imagetext == '') {
3973             $imagetext = '<img alt="'.$tooltip.'" src="'.
3974                           $CFG->pixpath .'/help.gif" />';
3975         }
3976         if ($linktext) {
3977             $linkobject .= $title.'&nbsp;';
3978         }
3980         $linkobject .= $imagetext;
3982     } else {
3983         $linkobject .= $tooltip;
3984     }
3986     $linkobject .= '</span>';
3988     if ($text) {
3989         $url = '/help.php?module='. $module .'&amp;text='. htmlentities(urlencode($text));
3990     } else {
3991         $url = '/help.php?module='. $module .'&amp;file='. $page .'.html';
3992     }
3994     $link = link_to_popup_window ($url, 'popup', $linkobject, 400, 500, $tooltip, 'none', true);
3996     if ($return) {
3997         return $link;
3998     } else {
3999         echo $link;
4000     }
4003 /**
4004  * Print a help button.
4005  *
4006  * Prints a special help button that is a link to the "live" emoticon popup
4007  * @uses $CFG
4008  * @uses $SESSION
4009  * @param string $form ?
4010  * @param string $field ?
4011  * @todo Finish documenting this function
4012  */
4013 function emoticonhelpbutton($form, $field) {
4015     global $CFG, $SESSION;
4017     $SESSION->inserttextform = $form;
4018     $SESSION->inserttextfield = $field;
4019     $imagetext = '<img src="' . $CFG->pixpath . '/s/smiley.gif" border="0" align="middle" width="15" height="15" alt=""
4020     class="emoticon" style="margin-left: 7px" />';
4022     helpbutton('emoticons', get_string('helpemoticons'), 'moodle', true, true, '', false, $imagetext);
4025 /**
4026  * Print a message and exit.
4027  *
4028  * @uses $CFG
4029  * @param string $message ?
4030  * @param string $link ?
4031  * @todo Finish documenting this function
4032  */
4033 function notice ($message, $link='') {
4034     global $CFG;
4036     $message = clean_text($message);
4037     $link    = clean_text($link);
4039     if (!$link) {
4040         if (!empty($_SERVER['HTTP_REFERER'])) {
4041             $link = $_SERVER['HTTP_REFERER'];
4042         } else {
4043             $link = $CFG->wwwroot .'/';
4044         }
4045     }
4047     echo '<br />';
4048     print_simple_box($message, 'center', '50%', '', '20', 'noticebox');
4049     print_continue($link);
4050     print_footer(get_site());
4051     die;
4054 /**
4055  * Print a message along with "Yes" and "No" links for the user to continue.
4056  *
4057  * @param string $message The text to display
4058  * @param string $linkyes The link to take the user to if they choose "Yes"
4059  * @param string $linkno The link to take the user to if they choose "No"
4060  */
4061 function notice_yesno ($message, $linkyes, $linkno) {
4063     global $CFG;
4065     $message = clean_text($message);
4066     $linkyes = clean_text($linkyes);
4067     $linkno = clean_text($linkno);
4069     print_simple_box_start('center', '60%', '', 5, 'noticebox', 'notice');
4070     echo '<p align="center">'. $message .'</p>';
4071     echo '<table align="center" cellpadding="20"><tr><td>';
4072     print_single_button($linkyes, NULL, get_string('yes'), 'post', $CFG->framename);
4073     echo '</td><td>';
4074     print_single_button($linkno, NULL, get_string('no'), 'post', $CFG->framename);
4075     echo '</td></tr></table>';
4076     print_simple_box_end();
4079 /**
4080  * Redirects the user to another page, after printing a notice
4081  *
4082  * @param string $url The url to take the user to
4083  * @param string $message The text message to display to the user about the redirect, if any
4084  * @param string $delay How long before refreshing to the new page at $url?
4085  * @todo '&' needs to be encoded into '&amp;' for XHTML compliance,
4086  *      however, this is not true for javascript. Therefore we
4087  *      first decode all entities in $url (since we cannot rely on)
4088  *      the correct input) and then encode for where it's needed
4089  *      echo "<script type='text/javascript'>alert('Redirect $url');</script>";
4090  */
4091 function redirect($url, $message='', $delay='0') {
4093     global $CFG;
4095     //$url     = clean_text($url);
4096     if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()])) {
4097        $url = sid_process_url($url);
4098     }
4100     $message = clean_text($message);
4102     $url = html_entity_decode($url); // for php < 4.3.0 this is defined in moodlelib.php
4103     $url = str_replace(array("\n", "\r"), '', $url); // some more cleaning
4104     $encodedurl = htmlentities($url);
4105     $tmpstr = clean_text('<a href="'.$encodedurl.'" />'); //clean encoded URL
4106     $encodedurl = substr($tmpstr, 9, strlen($tmpstr)-13);
4107     $url = addslashes(html_entity_decode($encodedurl));
4109     if (empty($message)) {
4110         echo '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />';
4111         echo '<script type="text/javascript">'. "\n" .'<!--'. "\n". "location.replace('$url');". "\n". '//-->'. "\n". '</script>';   // To cope with Mozilla bug
4112     } else {
4114         if (empty($delay)) {
4115             $delay = 3;  // There's no point having a message with no delay
4116         }
4117         print_header('', '', '', '', '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />');
4118         echo '<center>';
4119         echo '<p>'. $message .'</p>';
4120         echo '<p>( <a href="'. $encodedurl .'">'. get_string('continue') .'</a> )</p>';
4121         echo '</center>';
4123 ?>
4124 <script type="text/javascript">
4125 <!--
4127   function redirect() {
4128       document.location.replace('<?php echo $url ?>');
4129   }
4130   setTimeout("redirect()", <?php echo ($delay * 1000) ?>);
4131 -->
4132 </script>
4133 <?php
4135     }
4136     die;
4139 /**
4140  * Print a bold message in an optional color.
4141  *
4142  * @param string $message The message to print out
4143  * @param string $style Optional style to display message text in
4144  * @param string $align Alignment option
4145  */
4146 function notify ($message, $style='notifyproblem', $align='center') {
4148     if ($style == 'green') {
4149         $style = 'notifysuccess';  // backward compatible with old color system
4150     }
4152     $message = clean_text($message);
4154     echo '<div class="'.$style.'" align="'. $align .'">'. $message .'</div>'."<br />\n";
4158 /**
4159  * Given an email address, this function will return an obfuscated version of it
4160  *
4161  * @param string $email The email address to obfuscate
4162  * @return string
4163  */
4164  function obfuscate_email($email) {
4166     $i = 0;
4167     $length = strlen($email);
4168     $obfuscated = '';
4169     while ($i < $length) {
4170         if (rand(0,2)) {
4171             $obfuscated.='%'.dechex(ord($email{$i}));
4172         } else {
4173             $obfuscated.=$email{$i};
4174         }
4175         $i++;
4176     }
4177     return $obfuscated;
4180 /**
4181  * This function takes some text and replaces about half of the characters
4182  * with HTML entity equivalents.   Return string is obviously longer.
4183  *
4184  * @param string $plaintext The text to be obfuscated
4185  * @return string
4186  */
4187 function obfuscate_text($plaintext) {
4189     $i=0;
4190     $length = strlen($plaintext);
4191     $obfuscated='';
4192     $prev_obfuscated = false;
4193     while ($i < $length) {
4194         $c = ord($plaintext{$i});
4195         $numerical = ($c >= ord('0')) && ($c <= ord('9'));
4196         if ($prev_obfuscated and $numerical ) {
4197             $obfuscated.='&#'.ord($plaintext{$i}).';';
4198         } else if (rand(0,2)) {
4199             $obfuscated.='&#'.ord($plaintext{$i}).';';
4200             $prev_obfuscated = true;
4201         } else {
4202             $obfuscated.=$plaintext{$i};
4203             $prev_obfuscated = false;
4204         }
4205       $i++;
4206     }
4207     return $obfuscated;
4210 /**
4211  * This function uses the {@link obfuscate_email()} and {@link obfuscate_text()}
4212  * to generate a fully obfuscated email link, ready to use.
4213  *
4214  * @param string $email The email address to display
4215  * @param string $label The text to dispalyed as hyperlink to $email
4216  * @param boolean $dimmed If true then use css class 'dimmed' for hyperlink
4217  * @return string
4218  */
4219 function obfuscate_mailto($email, $label='', $dimmed=false) {
4221     if (empty($label)) {
4222         $label = $email;
4223     }
4224     if ($dimmed) {
4225         $title = get_string('emaildisable');
4226         $dimmed = ' class="dimmed"';
4227     } else {
4228         $title = '';
4229         $dimmed = '';
4230     }
4231     return sprintf("<a href=\"%s:%s\" $dimmed title=\"$title\">%s</a>",
4232                     obfuscate_text('mailto'), obfuscate_email($email),
4233                     obfuscate_text($label));
4236 /**
4237  * Prints a single paging bar to provide access to other pages  (usually in a search)
4238  *
4239  * @param int $totalcount Thetotal number of entries available to be paged through
4240  * @param int $page The page you are currently viewing
4241  * @param int $perpage The number of entries that should be shown per page
4242  * @param string $baseurl The url which will be used to create page numbered links. Each page will consist of the base url appended by the page
4243 var an equal sign, then the page number.
4244  * @param string $pagevar This is the variable name that you use for the page number in your code (ie. 'tablepage', 'blogpage', etc)
4245  */
4246 function print_paging_bar($totalcount, $page, $perpage, $baseurl, $pagevar='page',$nocurr=false) {
4248     $maxdisplay = 18;
4250     if ($totalcount > $perpage) {
4251         echo '<div class="paging">';
4252         echo get_string('page') .':';
4253         if ($page > 0) {
4254             $pagenum = $page - 1;
4255             echo '&nbsp;(<a  href="'. $baseurl . $pagevar .'='. $pagenum .'">'. get_string('previous') .'</a>)&nbsp;';
4256         }
4257         $lastpage = ceil($totalcount / $perpage);
4258         if ($page > 15) {
4259             $startpage = $page - 10;
4260             echo '&nbsp;<a href="'. $baseurl . $pagevar .'=0">1</a>&nbsp;...';
4261         } else {
4262             $startpage = 0;
4263         }
4264         $currpage = $startpage;
4265         $displaycount = 0;
4266         while ($displaycount < $maxdisplay and $currpage < $lastpage) {
4267             $displaypage = $currpage+1;
4268             if ($page == $currpage && empty($nocurr)) {
4269                 echo '&nbsp;&nbsp;'. $displaypage;
4270             } else {
4271                 echo '&nbsp;&nbsp;<a href="'. $baseurl . $pagevar .'='. $currpage .'">'. $displaypage .'</a>';
4272             }
4273             $displaycount++;
4274             $currpage++;
4275         }
4276         if ($currpage < $lastpage) {
4277             $lastpageactual = $lastpage - 1;
4278             echo '&nbsp;...<a href="'. $baseurl . $pagevar .'='. $lastpageactual .'">'. $lastpage .'</a>&nbsp;';
4279         }
4280         $pagenum = $page + 1;
4281         if ($pagenum != $displaypage) {
4282             echo '&nbsp;&nbsp;(<a href="'. $baseurl . $pagevar .'='. $pagenum .'">'. get_string('next') .'</a>)';
4283         }
4284         echo '</div>';
4285     }
4288 /**
4289  * This function is used to rebuild the <nolink> tag because some formats (PLAIN and WIKI)
4290  * will transform it to html entities
4291  *
4292  * @param string $text Text to search for nolink tag in
4293  * @return string
4294  */
4295 function rebuildnolinktag($text) {
4297     $text = preg_replace('/&lt;(\/*nolink)&gt;/i','<$1>',$text);
4299     return $text;
4302 /**
4303  * Prints a nice side block with an optional header.  The content can either
4304  * be a block of HTML or a list of text with optional icons.
4305  *
4306  * @param  string $heading ?
4307  * @param  string $content ?
4308  * @param  array $list ?
4309  * @param  array $icons ?
4310  * @param  string $footer ?
4311  * @param  array $attributes ?
4312  * @todo Finish documenting this function. Show example of various attributes, etc.
4313  */
4314 function print_side_block($heading='', $content='', $list=NULL, $icons=NULL, $footer='', $attributes = array()) {
4316     //Accessibility: skip block link, with $block_id to differentiate links.
4317     static $block_id = 0;
4318     $block_id++;
4319     $skip_text = get_string('skipblock','access').' '.$block_id;
4320     $skip_link = '<a href="#sb-'.$block_id.'" class="skip-block" title="'.$skip_text.'"><span class="accesshide">'.$skip_text.'</span></a>'; 
4321     $skip_dest = '<span id="sb-'.$block_id.'" class="skip-block-to"></span>';
4322     if (! empty($heading)) {
4323         $heading .= $skip_link;
4324     } else {
4325         echo $skip_link;
4326     }
4328     print_side_block_start($heading, $attributes);
4330     if ($content) {
4331         echo $content;
4332         if ($footer) {
4333             echo '<div class="footer">'. $footer .'</div>';
4334         }
4335     } else {
4336         if ($list) {
4337             $row = 0;
4338             //Accessibility: replaced unnecessary table with list, see themes/standard/styles_layout.css
4339             echo "\n<ul class='list'>\n";
4340             foreach ($list as $key => $string) {
4341                 echo '<li class="r'. $row .'">';
4342                 if ($icons) {
4343                     echo '<span class="icon c0">'. $icons[$key] .'</span>';
4344                 }
4345                 echo '<span class="c1">'. $string .'</span>';
4346                 echo "</li>\n";
4347                 $row = $row ? 0:1;
4348             }
4349             echo "</ul>\n";
4350         }
4351         if ($footer) {
4352             echo '<div class="footer">'. $footer .'</div>';
4353         }
4355     }
4357     print_side_block_end($attributes);
4358     echo $skip_dest;
4361 /**
4362  * Starts a nice side block with an optional header.
4363  *
4364  * @param string $heading ?
4365  * @param array $attributes ?
4366  * @todo Finish documenting this function
4367  */
4368 function print_side_block_start($heading='', $attributes = array()) {
4370     global $CFG;
4372     // If there are no special attributes, give a default CSS class
4373     if (empty($attributes) || !is_array($attributes)) {
4374         $attributes = array('class' => 'sideblock');
4376     } else if(!isset($attributes['class'])) {
4377         $attributes['class'] = 'sideblock';
4379     } else if(!strpos($attributes['class'], 'sideblock')) {
4380         $attributes['class'] .= ' sideblock';
4381     }
4383     // OK, the class is surely there and in addition to anything
4384     // else, it's tagged as a sideblock
4386     /*
4388     // IE misery: if I do it this way, blocks which start hidden cannot be "unhidden"
4390     // If there is a cookie to hide this thing, start it hidden
4391     if (!empty($attributes['id']) && isset($_COOKIE['hide:'.$attributes['id']])) {
4392         $attributes['class'] = 'hidden '.$attributes['class'];
4393     }
4394     */
4396     $attrtext = '';
4397     foreach ($attributes as $attr => $val) {
4398         $attrtext .= ' '.$attr.'="'.$val.'"';
4399     }
4401     echo '<div '.$attrtext.'>';
4402     if ($heading) {
4403         //Accessibility: replaced <div> with H2; no, H2 more appropriate in moodleblock.class.php: _title_html.
4404         echo '<div class="header">'.$heading.'</div>';
4405     }
4406     echo '<div class="content">';
4411 /**
4412  * Print table ending tags for a side block box.
4413  */
4414 function print_side_block_end($attributes = array()) {