adding capabilities
[moodle.git] / lib / weblib.php
1 <?php // $Id$
3 ///////////////////////////////////////////////////////////////////////////
4 //                                                                       //
5 // NOTICE OF COPYRIGHT                                                   //
6 //                                                                       //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment         //
8 //          http://moodle.com                                            //
9 //                                                                       //
10 // Copyright (C) 2001-2003  Martin Dougiamas  http://dougiamas.com       //
11 //                                                                       //
12 // This program is free software; you can redistribute it and/or modify  //
13 // it under the terms of the GNU General Public License as published by  //
14 // the Free Software Foundation; either version 2 of the License, or     //
15 // (at your option) any later version.                                   //
16 //                                                                       //
17 // This program is distributed in the hope that it will be useful,       //
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of        //
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         //
20 // GNU General Public License for more details:                          //
21 //                                                                       //
22 //          http://www.gnu.org/copyleft/gpl.html                         //
23 //                                                                       //
24 ///////////////////////////////////////////////////////////////////////////
26 /**
27  * Library of functions for web output
28  *
29  * Library of all general-purpose Moodle PHP functions and constants
30  * that produce HTML output
31  *
32  * Other main libraries:
33  * - datalib.php - functions that access the database.
34  * - moodlelib.php - general-purpose Moodle functions.
35  * @author Martin Dougiamas
36  * @version  $Id$
37  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
38  * @package moodlecore
39  */
41 /// We are going to uses filterlib functions here
42 require_once("$CFG->libdir/filterlib.php");
44 /// Constants
46 /// Define text formatting types ... eventually we can add Wiki, BBcode etc
48 /**
49  * Does all sorts of transformations and filtering
50  */
51 define('FORMAT_MOODLE',   '0');   // Does all sorts of transformations and filtering
53 /**
54  * Plain HTML (with some tags stripped)
55  */
56 define('FORMAT_HTML',     '1');   // Plain HTML (with some tags stripped)
58 /**
59  * Plain text (even tags are printed in full)
60  */
61 define('FORMAT_PLAIN',    '2');   // Plain text (even tags are printed in full)
63 /**
64  * Wiki-formatted text
65  * Deprecated: left here just to note that '3' is not used (at the moment)
66  * and to catch any latent wiki-like text (which generates an error)
67  */
68 define('FORMAT_WIKI',     '3');   // Wiki-formatted text
70 /**
71  * Markdown-formatted text http://daringfireball.net/projects/markdown/
72  */
73 define('FORMAT_MARKDOWN', '4');   // Markdown-formatted text http://daringfireball.net/projects/markdown/
75 /**
76  * TRUSTTEXT marker - if present in text, text cleaning should be bypassed
77  */
78 define('TRUSTTEXT', '#####TRUSTTEXT#####');
81 /**
82  * Allowed tags - string of html tags that can be tested against for safe html tags
83  * @global string $ALLOWED_TAGS
84  */
85 $ALLOWED_TAGS =
86 '<p><br><b><i><u><font><table><tbody><span><div><tr><td><th><ol><ul><dl><li><dt><dd><h1><h2><h3><h4><h5><h6><hr><img><a><strong><emphasis><em><sup><sub><address><cite><blockquote><pre><strike><param><acronym><nolink><lang><tex><algebra><math><mi><mn><mo><mtext><mspace><ms><mrow><mfrac><msqrt><mroot><mstyle><merror><mpadded><mphantom><mfenced><msub><msup><msubsup><munder><mover><munderover><mmultiscripts><mtable><mtr><mtd><maligngroup><malignmark><maction><cn><ci><apply><reln><fn><interval><inverse><sep><condition><declare><lambda><compose><ident><quotient><exp><factorial><divide><max><min><minus><plus><power><rem><times><root><gcd><and><or><xor><not><implies><forall><exists><abs><conjugate><eq><neq><gt><lt><geq><leq><ln><log><int><diff><partialdiff><lowlimit><uplimit><bvar><degree><set><list><union><intersect><in><notin><subset><prsubset><notsubset><notprsubset><setdiff><sum><product><limit><tendsto><mean><sdev><variance><median><mode><moment><vector><matrix><matrixrow><determinant><transpose><selector><annotation><semantics><annotation-xml><tt><code>';
88 /**
89  * Allowed protocols - array of protocols that are safe to use in links and so on
90  * @global string $ALLOWED_PROTOCOLS
91  */
92 $ALLOWED_PROTOCOLS = array('http', 'https', 'ftp', 'news', 'mailto', 'rtsp', 'teamspeak', 'gopher', 'mms',
93                            'color', 'callto', 'cursor', 'text-align', 'font-size', 'font-weight', 'font-style',
94                            'border', 'margin', 'padding', 'background');   // CSS as well to get through kses
97 /// Functions
99 /**
100  * Add quotes to HTML characters
101  *
102  * Returns $var with HTML characters (like "<", ">", etc.) properly quoted.
103  * This function is very similar to {@link p()}
104  *
105  * @param string $var the string potentially containing HTML characters
106  * @param boolean $strip to decide if we want to strip slashes or no. Default to false.
107  *                true should be used to print data from forms and false for data from DB.
108  * @return string
109  */
110 function s($var, $strip=false) {
112     if ($var == '0') {  // for integer 0, boolean false, string '0'
113         return '0';
114     }
116     if ($strip) {
117         return preg_replace("/&amp;(#\d+);/i", "&$1;", htmlspecialchars(stripslashes_safe($var)));
118     } else {
119         return preg_replace("/&amp;(#\d+);/i", "&$1;", htmlspecialchars($var));
120     }
123 /**
124  * Add quotes to HTML characters
125  *
126  * Prints $var with HTML characters (like "<", ">", etc.) properly quoted.
127  * This function is very similar to {@link s()}
128  *
129  * @param string $var the string potentially containing HTML characters
130  * @param boolean $strip to decide if we want to strip slashes or no. Default to false.
131  *                true should be used to print data from forms and false for data from DB.
132  * @return string
133  */
134 function p($var, $strip=false) {
135     echo s($var, $strip);
139 /**
140  * Ensure that a variable is set
141  *
142  * Return $var if it is defined, otherwise return $default,
143  * This function is very similar to {@link optional_variable()}
144  *
145  * @param    mixed $var the variable which may be unset
146  * @param    mixed $default the value to return if $var is unset
147  * @return   mixed
148  */
149 function nvl(&$var, $default='') {
150     global $CFG;
152     if (!empty($CFG->disableglobalshack)) {
153       error( "The nvl() function is deprecated ($var, $default)." );
154     }
155     return isset($var) ? $var : $default;
158 /**
159  * Remove query string from url
160  *
161  * Takes in a URL and returns it without the querystring portion
162  *
163  * @param string $url the url which may have a query string attached
164  * @return string
165  */
166  function strip_querystring($url) {
168     if ($commapos = strpos($url, '?')) {
169         return substr($url, 0, $commapos);
170     } else {
171         return $url;
172     }
175 /**
176  * Returns the URL of the HTTP_REFERER, less the querystring portion
177  * @return string
178  */
179 function get_referer() {
181     return strip_querystring(nvl($_SERVER['HTTP_REFERER']));
185 /**
186  * Returns the name of the current script, WITH the querystring portion.
187  * this function is necessary because PHP_SELF and REQUEST_URI and SCRIPT_NAME
188  * return different things depending on a lot of things like your OS, Web
189  * server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.)
190  * <b>NOTE:</b> This function returns false if the global variables needed are not set.
191  *
192  * @return string
193  */
194  function me() {
196     if (!empty($_SERVER['REQUEST_URI'])) {
197         return $_SERVER['REQUEST_URI'];
199     } else if (!empty($_SERVER['PHP_SELF'])) {
200         if (!empty($_SERVER['QUERY_STRING'])) {
201             return $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
202         }
203         return $_SERVER['PHP_SELF'];
205     } else if (!empty($_SERVER['SCRIPT_NAME'])) {
206         if (!empty($_SERVER['QUERY_STRING'])) {
207             return $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
208         }
209         return $_SERVER['SCRIPT_NAME'];
211     } else if (!empty($_SERVER['URL'])) {     // May help IIS (not well tested)
212         if (!empty($_SERVER['QUERY_STRING'])) {
213             return $_SERVER['URL'] .'?'. $_SERVER['QUERY_STRING'];
214         }
215         return $_SERVER['URL'];
217     } else {
218         notify('Warning: Could not find any of these web server variables: $REQUEST_URI, $PHP_SELF, $SCRIPT_NAME or $URL');
219         return false;
220     }
223 /**
224  * Like {@link me()} but returns a full URL
225  * @see me()
226  * @return string
227  */
228 function qualified_me() {
230     global $CFG;
232     if (!empty($CFG->wwwroot)) {
233         $url = parse_url($CFG->wwwroot);
234     }
236     if (!empty($url['host'])) {
237         $hostname = $url['host'];
238     } else if (!empty($_SERVER['SERVER_NAME'])) {
239         $hostname = $_SERVER['SERVER_NAME'];
240     } else if (!empty($_ENV['SERVER_NAME'])) {
241         $hostname = $_ENV['SERVER_NAME'];
242     } else if (!empty($_SERVER['HTTP_HOST'])) {
243         $hostname = $_SERVER['HTTP_HOST'];
244     } else if (!empty($_ENV['HTTP_HOST'])) {
245         $hostname = $_ENV['HTTP_HOST'];
246     } else {
247         notify('Warning: could not find the name of this server!');
248         return false;
249     }
251     if (!empty($url['port'])) {
252         $hostname .= ':'.$url['port'];
253     } else if (!empty($_SERVER['SERVER_PORT'])) {
254         if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
255             $hostname .= ':'.$_SERVER['SERVER_PORT'];
256         }
257     }
259     if (isset($_SERVER['HTTPS'])) {
260         $protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
261     } else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS']
262         $protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://';
263     } else {
264         $protocol = 'http://';
265     }
267     $url_prefix = $protocol.$hostname;
268     return $url_prefix . me();
271 /**
272  * Determine if a web referer is valid
273  *
274  * Returns true if the referer is the same as the goodreferer. If
275  * the referer to test is not specified, use {@link qualified_me()}.
276  * If the admin has not set secure forms ($CFG->secureforms) then
277  * this function returns true regardless of a match.
278  *
279  * @uses $CFG
280  * @param string $goodreferer the url to compare to referer
281  * @return boolean
282  */
283 function match_referer($goodreferer = '') {
284     global $CFG;
286     if (empty($CFG->secureforms)) {    // Don't bother checking referer
287         return true;
288     }
290     if ($goodreferer == 'nomatch') {   // Don't bother checking referer
291         return true;
292     }
294     if (empty($goodreferer)) {
295         $goodreferer = qualified_me();
296         // try to remove everything after ? because POST url may contain GET parameters (SID rewrite, etc.)
297         $pos = strpos($goodreferer, '?');
298         if ($pos !== FALSE) {
299             $goodreferer = substr($goodreferer, 0, $pos);
300         }
301     }
303     $referer = get_referer();
305     return (($referer == $goodreferer) or ($referer == $CFG->wwwroot .'/') or ($referer == $CFG->wwwroot .'/index.php'));
308 /**
309  * Determine if there is data waiting to be processed from a form
310  *
311  * Used on most forms in Moodle to check for data
312  * Returns the data as an object, if it's found.
313  * This object can be used in foreach loops without
314  * casting because it's cast to (array) automatically
315  *
316  * Checks that submitted POST data exists, and also
317  * checks the referer against the given url (it uses
318  * the current page if none was specified.
319  *
320  * @uses $CFG
321  * @param string $url the url to compare to referer for secure forms
322  * @return boolean
323  */
324 function data_submitted($url='') {
327     global $CFG;
329     if (empty($_POST)) {
330         return false;
332     } else {
333         if (match_referer($url)) {
334             return (object)$_POST;
335         } else {
336             if ($CFG->debug > 10) {
337                 notice('The form did not come from this page! (referer = '. get_referer() .')');
338             }
339             return false;
340         }
341     }
344 /**
345  * Moodle replacement for php stripslashes() function,
346  * works also for objects and arrays.
347  *
348  * The standard php stripslashes() removes ALL backslashes
349  * even from strings - so  C:\temp becomes C:temp - this isn't good.
350  * This function should work as a fairly safe replacement
351  * to be called on quoted AND unquoted strings (to be sure)
352  *
353  * @param mixed something to remove unsafe slashes from
354  * @return mixed
355  */
356 function stripslashes_safe($mixed) {
357     // there is no need to remove slashes from int, float and bool types
358     if (empty($mixed)) {
359         //nothing to do...
360     } else if (is_string($mixed)) {
361         if (ini_get_bool('magic_quotes_sybase')) { //only unescape single quotes
362             $mixed = str_replace("''", "'", $mixed);
363         } else { //the rest, simple and double quotes and backslashes
364             $mixed = str_replace("\\'", "'", $mixed);
365             $mixed = str_replace('\\"', '"', $mixed);
366             $mixed = str_replace('\\\\', '\\', $mixed);
367         }
368     } else if (is_array($mixed)) {
369         foreach ($mixed as $key => $value) {
370             $mixed[$key] = stripslashes_safe($value);
371         }
372     } else if (is_object($mixed)) {
373         $vars = get_object_vars($mixed);
374         foreach ($vars as $key => $value) {
375             $mixed->$key = stripslashes_safe($value);
376         }
377     }
379     return $mixed;
382 /**
383  * Recursive implementation of stripslashes()
384  *
385  * This function will allow you to strip the slashes from a variable.
386  * If the variable is an array or object, slashes will be stripped
387  * from the items (or properties) it contains, even if they are arrays
388  * or objects themselves.
389  *
390  * @param mixed the variable to remove slashes from
391  * @return mixed
392  */
393 function stripslashes_recursive($var) {
394     if(is_object($var)) {
395         $properties = get_object_vars($var);
396         foreach($properties as $property => $value) {
397             $var->$property = stripslashes_recursive($value);
398         }
399     }
400     else if(is_array($var)) {
401         foreach($var as $property => $value) {
402             $var[$property] = stripslashes_recursive($value);
403         }
404     }
405     else if(is_string($var)) {
406         $var = stripslashes($var);
407     }
408     return $var;
411 /**
412  * Given some normal text this function will break up any
413  * long words to a given size by inserting the given character
414  *
415  * It's multibyte savvy and doesn't change anything inside html tags.
416  *
417  * @param string $string the string to be modified
418  * @param int $maxsize maximum length of the string to be returned
419  * @param string $cutchar the string used to represent word breaks
420  * @return string
421  */
422 function break_up_long_words($string, $maxsize=20, $cutchar=' ') {
424 /// Loading the textlib singleton instance. We are going to need it.
425     $textlib = textlib_get_instance();
427 /// First of all, save all the tags inside the text to skip them
428     $tags = array();
429     filter_save_tags($string,$tags);
431 /// Process the string adding the cut when necessary
432     $output = '';
433     $length = $textlib->strlen($string, current_charset());
434     $wordlength = 0;
436     for ($i=0; $i<$length; $i++) {
437         $char = $textlib->substr($string, $i, 1, current_charset());
438         if ($char == ' ' or $char == "\t" or $char == "\n" or $char == "\r" or $char == "<" or $char == ">") {
439             $wordlength = 0;
440         } else {
441             $wordlength++;
442             if ($wordlength > $maxsize) {
443                 $output .= $cutchar;
444                 $wordlength = 0;
445             }
446         }
447         $output .= $char;
448     }
450 /// Finally load the tags back again
451     if (!empty($tags)) {
452         $output = str_replace(array_keys($tags), $tags, $output);
453     }
455     return $output;
458 /**
459  * This does a search and replace, ignoring case
460  * This function is only used for versions of PHP older than version 5
461  * which do not have a native version of this function.
462  * Taken from the PHP manual, by bradhuizenga @ softhome.net
463  *
464  * @param string $find the string to search for
465  * @param string $replace the string to replace $find with
466  * @param string $string the string to search through
467  * return string
468  */
469 if (!function_exists('str_ireplace')) {    /// Only exists in PHP 5
470     function str_ireplace($find, $replace, $string) {
472         if (!is_array($find)) {
473             $find = array($find);
474         }
476         if(!is_array($replace)) {
477             if (!is_array($find)) {
478                 $replace = array($replace);
479             } else {
480                 // this will duplicate the string into an array the size of $find
481                 $c = count($find);
482                 $rString = $replace;
483                 unset($replace);
484                 for ($i = 0; $i < $c; $i++) {
485                     $replace[$i] = $rString;
486                 }
487             }
488         }
490         foreach ($find as $fKey => $fItem) {
491             $between = explode(strtolower($fItem),strtolower($string));
492             $pos = 0;
493             foreach($between as $bKey => $bItem) {
494                 $between[$bKey] = substr($string,$pos,strlen($bItem));
495                 $pos += strlen($bItem) + strlen($fItem);
496             }
497             $string = implode($replace[$fKey],$between);
498         }
499         return ($string);
500     }
503 /**
504  * Locate the position of a string in another string
505  *
506  * This function is only used for versions of PHP older than version 5
507  * which do not have a native version of this function.
508  * Taken from the PHP manual, by dmarsh @ spscc.ctc.edu
509  *
510  * @param string $haystack The string to be searched
511  * @param string $needle The string to search for
512  * @param int $offset The position in $haystack where the search should begin.
513  */
514 if (!function_exists('stripos')) {    /// Only exists in PHP 5
515     function stripos($haystack, $needle, $offset=0) {
517         return strpos(strtoupper($haystack), strtoupper($needle), $offset);
518     }
521 /**
522  * Load a template from file
523  *
524  * Returns a (big) string containing the contents of a template file with all
525  * the variables interpolated.  all the variables must be in the $var[] array or
526  * object (whatever you decide to use).
527  *
528  * <b>WARNING: do not use this on big files!!</b>
529  *
530  * @param string $filename Location on the server's filesystem where template can be found.
531  * @param mixed $var Passed in by reference. An array or object which will be loaded with data from the template file.
532  */
533 function read_template($filename, &$var) {
535     $temp = str_replace("\\", "\\\\", implode(file($filename), ''));
536     $temp = str_replace('"', '\"', $temp);
537     eval("\$template = \"$temp\";");
538     return $template;
541 /**
542  * Set a variable's value depending on whether or not it already has a value.
543  *
544  * If variable is set, set it to the set_value otherwise set it to the
545  * unset_value.  used to handle checkboxes when you are expecting them from
546  * a form
547  *
548  * @param mixed $var Passed in by reference. The variable to check.
549  * @param mixed $set_value The value to set $var to if $var already has a value.
550  * @param mixed $unset_value The value to set $var to if $var does not already have a value.
551  */
552 function checked(&$var, $set_value = 1, $unset_value = 0) {
554     if (empty($var)) {
555         $var = $unset_value;
556     } else {
557         $var = $set_value;
558     }
561 /**
562  * Prints the word "checked" if a variable is true, otherwise prints nothing,
563  * used for printing the word "checked" in a checkbox form element.
564  *
565  * @param boolean $var Variable to be checked for true value
566  * @param string $true_value Value to be printed if $var is true
567  * @param string $false_value Value to be printed if $var is false
568  */
569 function frmchecked(&$var, $true_value = 'checked', $false_value = '') {
571     if ($var) {
572         echo $true_value;
573     } else {
574         echo $false_value;
575     }
578 /**
579  * This function will create a HTML link that will work on both
580  * Javascript and non-javascript browsers.
581  * Relies on the Javascript function openpopup in javascript.php
582  *
583  * $url must be relative to home page  eg /mod/survey/stuff.php
584  * @param string $url Web link relative to home page
585  * @param string $name Name to be assigned to the popup window
586  * @param string $linkname Text to be displayed as web link
587  * @param int $height Height to assign to popup window
588  * @param int $width Height to assign to popup window
589  * @param string $title Text to be displayed as popup page title
590  * @param string $options List of additional options for popup window
591  * @todo Add code examples and list of some options that might be used.
592  * @param boolean $return Should the link to the popup window be returned as a string (true) or printed immediately (false)?
593  * @return string
594  * @uses $CFG
595  */
596 function link_to_popup_window ($url, $name='popup', $linkname='click here',
597                                $height=400, $width=500, $title='Popup window',
598                                $options='none', $return=false) {
600     global $CFG;
602     if ($options == 'none') {
603         $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
604     }
605     $fullscreen = 0;
607     if (!(strpos($url,$CFG->wwwroot) === false)) { // some log url entries contain _SERVER[HTTP_REFERRER] in which case wwwroot is already there.
608         $url = substr($url, strlen($CFG->wwwroot));
609     }
611     $link = '<a target="'. $name .'" title="'. $title .'" href="'. $CFG->wwwroot . $url .'" '.
612            "onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\">$linkname</a>";
613     if ($return) {
614         return $link;
615     } else {
616         echo $link;
617     }
620 /**
621  * This function will print a button submit form element
622  * that will work on both Javascript and non-javascript browsers.
623  * Relies on the Javascript function openpopup in javascript.php
624  *
625  * $url must be relative to home page  eg /mod/survey/stuff.php
626  * @param string $url Web link relative to home page
627  * @param string $name Name to be assigned to the popup window
628  * @param string $linkname Text to be displayed as web link
629  * @param int $height Height to assign to popup window
630  * @param int $width Height to assign to popup window
631  * @param string $title Text to be displayed as popup page title
632  * @param string $options List of additional options for popup window
633  * @param string $return If true, return as a string, otherwise print
634  * @return string
635  * @uses $CFG
636  */
637 function button_to_popup_window ($url, $name='popup', $linkname='click here',
638                                  $height=400, $width=500, $title='Popup window', $options='none', $return=false,
639                                  $id='', $class='') {
641     global $CFG;
643     if ($options == 'none') {
644         $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
645     }
647     if ($id) {
648         $id = ' id="'.$id.'" ';
649     }
650     if ($class) {
651         $class = ' class="'.$class.'" ';
652     }
653     $fullscreen = 0;
655     $button = '<input type="button" name="'.$name.'" title="'. $title .'" value="'. $linkname .' ..." '.$id.$class.
656               "onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\" />\n";
657     if ($return) {
658         return $button;
659     } else {
660         echo $button;
661     }
665 /**
666  * Prints a simple button to close a window
667  */
668 function close_window_button($name='closewindow', $return=false) {
669     $output = '';
671     $output .= '<center>' . "\n";
672     $output .= '<script type="text/javascript">' . "\n";
673     $output .= '<!--' . "\n";
674     $output .= "document.write('<form>');\n";
675     $output .= "document.write('<input type=\"button\" onclick=\"self.close();\" value=\"".get_string("closewindow")."\" />');\n";
676     $output .= "document.write('<\/form>');\n";
677     $output .= '-->' . "\n";
678     $output .= '</script>' . "\n";
679     $output .= '<noscript>' . "\n";
680     $output .= get_string($name);
681     $output .= '</noscript>' . "\n";
682     $output .= '</center>' . "\n";
684     if ($return) {
685         return $output;
686     } else {
687         echo $output;
688     }
691 /*
692  * Try and close the current window immediately using Javascript
693  */
694 function close_window($delay=0) {
695     echo '<script language="JavaScript" type="text/javascript">'."\n";
696     echo '<!--'."\n";
697     if ($delay) {
698         sleep($delay);
699     }
700     echo 'self.close();'."\n";
701     echo '-->'."\n";
702     echo '</script>'."\n";
703     exit;
707 /**
708  * Given an array of value, creates a popup menu to be part of a form
709  * $options["value"]["label"]
710  *
711  * @param    type description
712  * @todo Finish documenting this function
713  */
714 function choose_from_menu ($options, $name, $selected='', $nothing='choose', $script='',
715                            $nothingvalue='0', $return=false, $disabled=false, $tabindex=0) {
717     if ($nothing == 'choose') {
718         $nothing = get_string('choose') .'...';
719     }
721     $attributes = ($script) ? 'onchange="'. $script .'"' : '';
722     if ($disabled) {
723         $attributes .= ' disabled="disabled"';
724     }
726     if ($tabindex) {
727         $attributes .= ' tabindex="'.$tabindex.'"';
728     }
730     $id = str_replace('[]', '', $name); // name may end in [], which would make an invalid id. e.g. numeric question type editing form.
731     $output = '<select id="menu'.$id.'" name="'. $name .'" '. $attributes .'>' . "\n";
732     if ($nothing) {
733         $output .= '   <option value="'. $nothingvalue .'"'. "\n";
734         if ($nothingvalue === $selected) {
735             $output .= ' selected="selected"';
736         }
737         $output .= '>'. $nothing .'</option>' . "\n";
738     }
739     if (!empty($options)) {
740         foreach ($options as $value => $label) {
741             $output .= '   <option value="'. $value .'"';
742             if ((string)$value == (string)$selected) {
743                 $output .= ' selected="selected"';
744             }
745             if ($label === '') {
746                 $output .= '>'. $value .'</option>' . "\n";
747             } else {
748                 $output .= '>'. $label .'</option>' . "\n";
749             }
750         }
751     }
752     $output .= '</select>' . "\n";
754     if ($return) {
755         return $output;
756     } else {
757         echo $output;
758     }
761 /**
762  * Choose value 0 or 1 from a menu with options 'No' and 'Yes'.
763  * Other options like choose_from_menu.
764  */
765 function choose_from_menu_yesno($name, $selected, $script = '',
766         $return = false, $disabled = false, $tabindex = 0) {
767     return choose_from_menu(array(get_string('no'), get_string('yes')), $name,
768             $selected, '', $script, '0', $return, $disabled, $tabindex);
771 /**
772  * Just like choose_from_menu, but takes a nested array (2 levels) and makes a dropdown menu
773  * including option headings with the first level.
774  */
775 function choose_from_menu_nested($options,$name,$selected='',$nothing='choose',$script = '',
776                                  $nothingvalue=0,$return=false,$disabled=false,$tabindex=0) {
778    if ($nothing == 'choose') {
779         $nothing = get_string('choose') .'...';
780     }
782     $attributes = ($script) ? 'onchange="'. $script .'"' : '';
783     if ($disabled) {
784         $attributes .= ' disabled="disabled"';
785     }
787     if ($tabindex) {
788         $attributes .= ' tabindex="'.$tabindex.'"';
789     }
791     $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
792     if ($nothing) {
793         $output .= '   <option value="'. $nothingvalue .'"'. "\n";
794         if ($nothingvalue === $selected) {
795             $output .= ' selected="selected"';
796         }
797         $output .= '>'. $nothing .'</option>' . "\n";
798     }
799     if (!empty($options)) {
800         foreach ($options as $section => $values) {
801             $output .= '   <optgroup label="'.$section.'">'."\n";
802             foreach ($values as $value => $label) {
803                 $output .= '   <option value="'. $value .'"';
804                 if ((string)$value == (string)$selected) {
805                     $output .= ' selected="selected"';
806                 }
807                 if ($label === '') {
808                     $output .= '>'. $value .'</option>' . "\n";
809                 } else {
810                     $output .= '>'. $label .'</option>' . "\n";
811                 }
812             }
813             $output .= '   </optgroup>'."\n";
814         }
815     }
816     $output .= '</select>' . "\n";
818     if ($return) {
819         return $output;
820     } else {
821         echo $output;
822     }
826 /**
827  * Given an array of values, creates a group of radio buttons to be part of a form
828  *
829  * @param array  $options  An array of value-label pairs for the radio group (values as keys)
830  * @param string $name     Name of the radiogroup (unique in the form)
831  * @param string $checked  The value that is already checked
832  */
833 function choose_from_radio ($options, $name, $checked='', $return=false) {
835     static $idcounter = 0;
837     if (!$name) {
838         $name = 'unnamed';
839     }
841     $output = '<span class="radiogroup '.$name."\">\n";
843     if (!empty($options)) {
844         $currentradio = 0;
845         foreach ($options as $value => $label) {
846             $htmlid = 'auto-rb'.sprintf('%04d', ++$idcounter);
847             $output .= ' <span class="radioelement '.$name.' rb'.$currentradio."\">";
848             $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="radio" value="'.$value.'"';
849             if ($value == $checked) {
850                 $output .= ' checked="checked"';
851             }
852             if ($label === '') {
853                 $output .= ' /> <label for="'.$htmlid.'">'.  $value .'</label></span>' .  "\n";
854             } else {
855                 $output .= ' /> <label for="'.$htmlid.'">'.  $label .'</label></span>' .  "\n";
856             }
857             $currentradio = ($currentradio + 1) % 2;
858         }
859     }
861     $output .= '</span>' .  "\n";
863     if ($return) {
864         return $output;
865     } else {
866         echo $output;
867     }
870 /** Display an standard html checkbox with an optional label
871  *
872  * @param string  $name    The name of the checkbox
873  * @param string  $value   The valus that the checkbox will pass when checked
874  * @param boolean $checked The flag to tell the checkbox initial state
875  * @param string  $label   The label to be showed near the checkbox
876  * @param string  $alt     The info to be inserted in the alt tag
877  */
878 function print_checkbox ($name, $value, $checked = true, $label = '', $alt = '', $script='',$return=false) {
880     static $idcounter = 0;
882     if (!$name) {
883         $name = 'unnamed';
884     }
886     if (!$alt) {
887         $alt = 'checkbox';
888     }
890     if ($checked) {
891         $strchecked = ' checked="checked"';
892     } else {
893         $strchecked = '';
894     }
896     $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
897     $output  = '<span class="checkbox '.$name."\">";
898     $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="checkbox" value="'.$value.'" alt="'.$alt.'"'.$strchecked.' '.((!empty($script)) ? ' onClick="'.$script.'" ' : '').' />';
899     if(!empty($label)) {
900         $output .= ' <label for="'.$htmlid.'">'.$label.'</label>';
901     }
902     $output .= '</span>'."\n";
904     if (empty($return)) {
905         echo $output;
906     } else {
907         return $output;
908     }
912 /** Display an standard html text field with an optional label
913  *
914  * @param string  $name    The name of the text field
915  * @param string  $value   The value of the text field
916  * @param string  $label   The label to be showed near the text field
917  * @param string  $alt     The info to be inserted in the alt tag
918  */
919 function print_textfield ($name, $value, $alt = '',$size=50,$maxlength=0, $return=false) {
921     static $idcounter = 0;
923     if (empty($name)) {
924         $name = 'unnamed';
925     }
927     if (empty($alt)) {
928         $alt = 'textfield';
929     }
931     if (!empty($maxlength)) {
932         $maxlength = ' maxlength="'.$maxlength.'" ';
933     }
935     $htmlid = 'auto-tf'.sprintf('%04d', ++$idcounter);
936     $output  = '<span class="textfield '.$name."\">";
937     $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="text" value="'.$value.'" size="'.$size.'" '.$maxlength.' alt="'.$alt.'" />';
939     $output .= '</span>'."\n";
941     if (empty($return)) {
942         echo $output;
943     } else {
944         return $output;
945     }
950 /**
951  * Implements a complete little popup form
952  *
953  * @uses $CFG
954  * @param string $common  The URL up to the point of the variable that changes
955  * @param array $options  Alist of value-label pairs for the popup list
956  * @param string $formname Name must be unique on the page
957  * @param string $selected The option that is already selected
958  * @param string $nothing The label for the "no choice" option
959  * @param string $help The name of a help page if help is required
960  * @param string $helptext The name of the label for the help button
961  * @param boolean $return Indicates whether the function should return the text
962  *         as a string or echo it directly to the page being rendered
963  * @param string $targetwindow The name of the target page to open the linked page in.
964  * @return string If $return is true then the entire form is returned as a string.
965  * @todo Finish documenting this function<br>
966  */
967 function popup_form($common, $options, $formname, $selected='', $nothing='choose', $help='', $helptext='', $return=false, $targetwindow='self') {
969     global $CFG;
970     static $go, $choose;   /// Locally cached, in case there's lots on a page
972     if (empty($options)) {
973         return '';
974     }
976     if (!isset($go)) {
977         $go = get_string('go');
978     }
980     if ($nothing == 'choose') {
981         if (!isset($choose)) {
982             $choose = get_string('choose');
983         }
984         $nothing = $choose.'...';
985     }
987     $startoutput = '<form action="'.$CFG->wwwroot.'/course/jumpto.php"'.
988                         ' method="get"'.
989                         ' target="'.$CFG->framename.'"'.
990                         ' name="'.$formname.'"'.
991                         ' class="popupform">';
993     $output = '<select name="jump" onchange="'.$targetwindow.'.location=document.'.$formname.
994                        '.jump.options[document.'.$formname.'.jump.selectedIndex].value;">'."\n";
996     if ($nothing != '') {
997         $output .= "   <option value=\"javascript:void(0)\">$nothing</option>\n";
998     }
1000     $inoptgroup = false;
1001     foreach ($options as $value => $label) {
1003         if (substr($label,0,2) == '--') { /// we are starting a new optgroup
1005             /// Check to see if we already have a valid open optgroup
1006             /// XHTML demands that there be at least 1 option within an optgroup
1007             if ($inoptgroup and (count($optgr) > 1) ) {
1008                 $output .= implode('', $optgr);
1009                 $output .= '   </optgroup>';
1010             }
1012             unset($optgr);
1013             $optgr = array();
1015             $optgr[]  = '   <optgroup label="'. substr($label,2) .'">';   // Plain labels
1017             $inoptgroup = true; /// everything following will be in an optgroup
1018             continue;
1020         } else {
1021            if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()]))
1022             {
1023                 $url=sid_process_url( $common . $value );
1024             } else
1025             {
1026                 $url=$common . $value;
1027             }
1028             $optstr = '   <option value="' . $url . '"';
1030             if ($value == $selected) {
1031                 $optstr .= ' selected="selected"';
1032             }
1034             if ($label) {
1035                 $optstr .= '>'. $label .'</option>' . "\n";
1036             } else {
1037                 $optstr .= '>'. $value .'</option>' . "\n";
1038             }
1040             if ($inoptgroup) {
1041                 $optgr[] = $optstr;
1042             } else {
1043                 $output .= $optstr;
1044             }
1045         }
1047     }
1049     /// catch the final group if not closed
1050     if ($inoptgroup and count($optgr) > 1) {
1051         $output .= implode('', $optgr);
1052         $output .= '    </optgroup>';
1053     }
1055     $output .= '</select>';
1056     $output .= '<noscript id="noscript'.$formname.'" style="display: inline;">';
1057     $output .= '<input type="submit" value="'.$go.'" /></noscript>';
1058     $output .= '<script type="text/javascript">'.
1059                "\n<!--\n".
1060                'document.getElementById("noscript'.$formname.'").style.display = "none";'.
1061                "\n-->\n".'</script>';
1062     $output .= '</form>' . "\n";
1064     if ($help) {
1065         $button = helpbutton($help, $helptext, 'moodle', true, false, '', true);
1066     } else {
1067         $button = '';
1068     }
1070     if ($return) {
1071         return $startoutput.$button.$output;
1072     } else {
1073         echo $startoutput.$button.$output;
1074     }
1078 /**
1079  * Prints some red text
1080  *
1081  * @param string $error The text to be displayed in red
1082  */
1083 function formerr($error) {
1085     if (!empty($error)) {
1086         echo '<font color="#ff0000">'. $error .'</font>';
1087     }
1090 /**
1091  * Validates an email to make sure it makes sense.
1092  *
1093  * @param string $address The email address to validate.
1094  * @return boolean
1095  */
1096 function validate_email($address) {
1098     return (ereg('^[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+'.
1099                  '(\.[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+)*'.
1100                   '@'.
1101                   '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
1102                   '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$',
1103                   $address));
1106 /**
1107  * Extracts file argument either from file parameter or PATH_INFO
1108  *
1109  * @param string $scriptname name of the calling script
1110  * @return string file path (only safe characters)
1111  */
1112 function get_file_argument($scriptname) {
1113     global $_SERVER;
1115     $relativepath = FALSE;
1117     // first try normal parameter (compatible method == no relative links!)
1118     $relativepath = optional_param('file', FALSE, PARAM_PATH);
1119     if ($relativepath === '/testslasharguments') {
1120         echo 'test -1      : Incorrect use - try "file.php/testslasharguments" instead'; //indicate fopen/fread works for health center
1121         die;
1122     }
1124     // then try extract file from PATH_INFO (slasharguments method)
1125     if (!$relativepath and !empty($_SERVER['PATH_INFO'])) {
1126         $path_info = $_SERVER['PATH_INFO'];
1127         // check that PATH_INFO works == must not contain the script name
1128         if (!strpos($path_info, $scriptname)) {
1129             $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1130             if ($relativepath === '/testslasharguments') {
1131                 echo 'test 1      : Slasharguments test passed. Server confguration is compatible with file.php/1/pic.jpg slashargument setting.'; //indicate ok for health center
1132                 die;
1133             }
1134         }
1135     }
1137     // now if both fail try the old way
1138     // (for compatibility with misconfigured or older buggy php implementations)
1139     if (!$relativepath) {
1140         $arr = explode($scriptname, me());
1141         if (!empty($arr[1])) {
1142             $path_info = strip_querystring($arr[1]);
1143             $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
1144             if ($relativepath === '/testslasharguments') {
1145                 echo 'test 2      : Slasharguments test passed (compatibility hack). Server confguration may be compatible with file.php/1/pic.jpg slashargument setting'; //indicate ok for health center
1146                 die;
1147             }
1148         }
1149     }
1151     return $relativepath;
1154 /**
1155  * Check for bad characters ?
1156  *
1157  * @param string $string ?
1158  * @param int $allowdots ?
1159  * @todo Finish documenting this function - more detail needed in description as well as details on arguments
1160  */
1161 function detect_munged_arguments($string, $allowdots=1) {
1162     if (substr_count($string, '..') > $allowdots) {   // Sometimes we allow dots in references
1163         return true;
1164     }
1165     if (ereg('[\|\`]', $string)) {  // check for other bad characters
1166         return true;
1167     }
1168     if (empty($string) or $string == '/') {
1169         return true;
1170     }
1172     return false;
1175 /**
1176  * Searches the current environment variables for some slash arguments
1177  *
1178  * @param string $file ?
1179  * @todo Finish documenting this function
1180  */
1181 function get_slash_arguments($file='file.php') {
1183     if (!$string = me()) {
1184         return false;
1185     }
1187     $pathinfo = explode($file, $string);
1189     if (!empty($pathinfo[1])) {
1190         return addslashes($pathinfo[1]);
1191     } else {
1192         return false;
1193     }
1196 /**
1197  * Extracts arguments from "/foo/bar/something"
1198  * eg http://mysite.com/script.php/foo/bar/something
1199  *
1200  * @param string $string ?
1201  * @param int $i ?
1202  * @return array|string
1203  * @todo Finish documenting this function
1204  */
1205 function parse_slash_arguments($string, $i=0) {
1207     if (detect_munged_arguments($string)) {
1208         return false;
1209     }
1210     $args = explode('/', $string);
1212     if ($i) {     // return just the required argument
1213         return $args[$i];
1215     } else {      // return the whole array
1216         array_shift($args);  // get rid of the empty first one
1217         return $args;
1218     }
1221 /**
1222  * Just returns an array of text formats suitable for a popup menu
1223  *
1224  * @uses FORMAT_MOODLE
1225  * @uses FORMAT_HTML
1226  * @uses FORMAT_PLAIN
1227  * @uses FORMAT_MARKDOWN
1228  * @return array
1229  */
1230 function format_text_menu() {
1232     return array (FORMAT_MOODLE => get_string('formattext'),
1233                   FORMAT_HTML   => get_string('formathtml'),
1234                   FORMAT_PLAIN  => get_string('formatplain'),
1235                   FORMAT_MARKDOWN  => get_string('formatmarkdown'));
1238 /**
1239  * Given text in a variety of format codings, this function returns
1240  * the text as safe HTML.
1241  *
1242  * @uses $CFG
1243  * @uses FORMAT_MOODLE
1244  * @uses FORMAT_HTML
1245  * @uses FORMAT_PLAIN
1246  * @uses FORMAT_WIKI
1247  * @uses FORMAT_MARKDOWN
1248  * @param string $text The text to be formatted. This is raw text originally from user input.
1249  * @param int $format Identifier of the text format to be used
1250  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1251  * @param  array $options ?
1252  * @param int $courseid ?
1253  * @return string
1254  * @todo Finish documenting this function
1255  */
1256 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) {
1258     global $CFG, $course;
1260     if (!isset($options->trusttext)) {
1261         $options->trusttext = false;
1262     }
1264     if (!isset($options->noclean)) {
1265         $options->noclean=false;
1266     }
1267     if (!isset($options->smiley)) {
1268         $options->smiley=true;
1269     }
1270     if (!isset($options->filter)) {
1271         $options->filter=true;
1272     }
1273     if (!isset($options->para)) {
1274         $options->para=true;
1275     }
1276     if (!isset($options->newlines)) {
1277         $options->newlines=true;
1278     }
1280     if (empty($courseid)) {
1281         if (!empty($course->id)) {         // An ugly hack for better compatibility
1282             $courseid = $course->id;
1283         }
1284     }
1286     if (!empty($CFG->cachetext)) {
1287         $time = time() - $CFG->cachetext;
1288         $md5key = md5($text.'-'.$courseid.$options->noclean.$options->smiley.$options->filter.$options->para.$options->newlines.$format.current_language().$courseid.$options->trusttext);
1289         if ($oldcacheitem = get_record_sql('SELECT * FROM '.$CFG->prefix.'cache_text WHERE md5key = \''.$md5key.'\'', true)) {
1290             if ($oldcacheitem->timemodified >= $time) {
1291                 return $oldcacheitem->formattedtext;
1292             }
1293         }
1294     }
1296     // trusttext overrides the noclean option!
1297     if ($options->trusttext) {
1298         if (trusttext_present($text)) {
1299             $text = trusttext_strip($text);
1300             if (!empty($CFG->enabletrusttext)) {
1301                 $options->noclean = true;
1302             } else {
1303                 $options->noclean = false;
1304             }
1305         } else {
1306             $options->noclean = false;
1307         }
1308     }
1310     $CFG->currenttextiscacheable = true;   // Default status - can be changed by any filter
1312     switch ($format) {
1313         case FORMAT_HTML:
1314             if ($options->smiley) {
1315                 replace_smilies($text);
1316             }
1317             if (!$options->noclean) {
1318                 $text = clean_text($text, $format);
1319             }
1320             if ($options->filter) {
1321                 $text = filter_text($text, $courseid);
1322             }
1323             break;
1325         case FORMAT_PLAIN:
1326             $text = s($text);
1327             $text = rebuildnolinktag($text);
1328             $text = str_replace('  ', '&nbsp; ', $text);
1329             $text = nl2br($text);
1330             break;
1332         case FORMAT_WIKI:
1333             // this format is deprecated
1334             $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle.  You should not be seeing
1335                      this message as all texts should have been converted to Markdown format instead.
1336                      Please post a bug report to http://moodle.org/bugs with information about where you
1337                      saw this message.</p>'.s($text);
1338             break;
1340         case FORMAT_MARKDOWN:
1341             $text = markdown_to_html($text);
1342             if ($options->smiley) {
1343                 replace_smilies($text);
1344             }
1345             if (!$options->noclean) {
1346                 $text = clean_text($text, $format);
1347             }
1349             if ($options->filter) {
1350                 $text = filter_text($text, $courseid);
1351             }
1352             break;
1354         default:  // FORMAT_MOODLE or anything else
1355             $text = text_to_html($text, $options->smiley, $options->para, $options->newlines);
1356             if (!$options->noclean) {
1357                 $text = clean_text($text, $format);
1358             }
1360             if ($options->filter) {
1361                 $text = filter_text($text, $courseid);
1362             }
1363             break;
1364     }
1366     if (!empty($CFG->cachetext) and $CFG->currenttextiscacheable) {
1367         $newcacheitem->md5key = $md5key;
1368         $newcacheitem->formattedtext = addslashes($text);
1369         $newcacheitem->timemodified = time();
1370         if ($oldcacheitem) {                               // See bug 4677 for discussion
1371             $newcacheitem->id = $oldcacheitem->id;
1372             @update_record('cache_text', $newcacheitem);   // Update existing record in the cache table
1373                                                            // It's unlikely that the cron cache cleaner could have 
1374                                                            // deleted this entry in the meantime, as it allows
1375                                                            // some extra time to cover these cases.
1376         } else {
1377             @insert_record('cache_text', $newcacheitem);   // Insert a new record in the cache table
1378                                                            // Again, it's possible that another user has caused this
1379                                                            // record to be created already in the time that it took 
1380                                                            // to traverse this function.  That's OK too, as the 
1381                                                            // call above handles duplicate entries, and eventually
1382                                                            // the cron cleaner will delete them.
1383         }
1384     }
1386     return $text;
1389 /** Converts the text format from the value to the 'internal'
1390  *  name or vice versa. $key can either be the value or the name
1391  *  and you get the other back.
1392  *
1393  *  @param mixed int 0-4 or string one of 'moodle','html','plain','markdown'
1394  *  @return mixed as above but the other way around!
1395  */
1396 function text_format_name( $key ) {
1397   $lookup = array();
1398   $lookup[FORMAT_MOODLE] = 'moodle';
1399   $lookup[FORMAT_HTML] = 'html';
1400   $lookup[FORMAT_PLAIN] = 'plain';
1401   $lookup[FORMAT_MARKDOWN] = 'markdown';
1402   $value = "error";
1403   if (!is_numeric($key)) {
1404     $key = strtolower( $key );
1405     $value = array_search( $key, $lookup );
1406   }
1407   else {
1408     if (isset( $lookup[$key] )) {
1409       $value =  $lookup[ $key ];
1410     }
1411   }
1412   return $value;
1415 /** Given a simple string, this function returns the string
1416  *  processed by enabled filters if $CFG->filterall is enabled
1417  *
1418  *  @param string  $string     The string to be filtered.
1419  *  @param boolean $striplinks To strip any link in the result text.
1420  *  @param int     $courseid   Current course as filters can, potentially, use it
1421  *  @return string
1422  */
1423 function format_string ($string, $striplinks = false, $courseid=NULL ) {
1425     global $CFG, $course;
1427     //We'll use a in-memory cache here to speed up repeated strings
1428     static $strcache;
1430     //Calculate md5
1431     $md5 = md5($string.'<+>'.$striplinks);
1433     //Fetch from cache if possible
1434     if(isset($strcache[$md5])) {
1435         return $strcache[$md5];
1436     }
1438     if (empty($courseid)) {
1439         if (!empty($course->id)) {         // An ugly hack for better compatibility
1440             $courseid = $course->id;       // (copied from format_text)
1441         }
1442     }
1444     if (!empty($CFG->filterall)) {
1445         $string = filter_text($string, $courseid);
1446     }
1448     if ($striplinks) {  //strip links in string
1449         $string = preg_replace('/(<a[^>]+?>)(.+?)(<\/a>)/is','$2',$string);
1450     }
1452     //Store to cache
1453     $strcache[$md5] = $string;
1455     return $string;
1458 /**
1459  * Given text in a variety of format codings, this function returns
1460  * the text as plain text suitable for plain email.
1461  *
1462  * @uses FORMAT_MOODLE
1463  * @uses FORMAT_HTML
1464  * @uses FORMAT_PLAIN
1465  * @uses FORMAT_WIKI
1466  * @uses FORMAT_MARKDOWN
1467  * @param string $text The text to be formatted. This is raw text originally from user input.
1468  * @param int $format Identifier of the text format to be used
1469  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1470  * @return string
1471  */
1472 function format_text_email($text, $format) {
1474     switch ($format) {
1476         case FORMAT_PLAIN:
1477             return $text;
1478             break;
1480         case FORMAT_WIKI:
1481             $text = wiki_to_html($text);
1482         /// This expression turns links into something nice in a text format. (Russell Jungwirth)
1483         /// From: http://php.net/manual/en/function.eregi-replace.php and simplified
1484             $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1485             return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1486             break;
1488         case FORMAT_HTML:
1489             return html_to_text($text);
1490             break;
1492         case FORMAT_MOODLE:
1493         case FORMAT_MARKDOWN:
1494         default:
1495             $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
1496             return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
1497             break;
1498     }
1501 /**
1502  * Given some text in HTML format, this function will pass it
1503  * through any filters that have been defined in $CFG->textfilterx
1504  * The variable defines a filepath to a file containing the
1505  * filter function.  The file must contain a variable called
1506  * $textfilter_function which contains the name of the function
1507  * with $courseid and $text parameters
1508  *
1509  * @param string $text The text to be passed through format filters
1510  * @param int $courseid ?
1511  * @return string
1512  * @todo Finish documenting this function
1513  */
1514 function filter_text($text, $courseid=NULL) {
1515     global $CFG;
1517     require_once($CFG->libdir.'/filterlib.php');
1518     if (!empty($CFG->textfilters)) {
1519         $textfilters = explode(',', $CFG->textfilters);
1520         foreach ($textfilters as $textfilter) {
1521             if (is_readable($CFG->dirroot .'/'. $textfilter .'/filter.php')) {
1522                 include_once($CFG->dirroot .'/'. $textfilter .'/filter.php');
1523                 $functionname = basename($textfilter).'_filter';
1524                 if (function_exists($functionname)) {
1525                     $text = $functionname($courseid, $text);
1526                 }
1527             }
1528         }
1529     }
1531     /// <nolink> tags removed for XHTML compatibility
1532     $text = str_replace('<nolink>', '', $text);
1533     $text = str_replace('</nolink>', '', $text);
1535     return $text;
1538 /**
1539  * Is the text marked as trusted?
1540  *
1541  * @param string $text text to be searched for TRUSTTEXT marker
1542  * @return boolean
1543  */
1544 function trusttext_present($text) {
1545     if (strpos($text, TRUSTTEXT) !== FALSE) {
1546         return true;
1547     } else {
1548         return false;
1549     }
1552 /**
1553  * This funtion MUST be called before the cleaning or any other
1554  * function that modifies the data! We do not know the origin of trusttext
1555  * in database, if it gets there in tweaked form we must not convert it
1556  * to supported form!!!
1557  *
1558  * Please be carefull not to use stripslashes on data from database
1559  * or twice stripslashes when processing data recieved from user.
1560  *
1561  * @param string $text text that may contain TRUSTTEXT marker 
1562  * @return text without any TRUSTTEXT marker
1563  */
1564 function trusttext_strip($text) {
1565     global $CFG;
1567     while (true) { //removing nested TRUSTTEXT
1568         $orig = $text; 
1569         $text = str_replace(TRUSTTEXT, '', $text);
1570         if (strcmp($orig, $text) === 0) {
1571             return $text;
1572         }
1573     }
1576 /**
1577  * Mark text as trusted, such text may contain any HTML tags because the
1578  * normal text cleaning will be bypassed.
1579  * Please make sure that the text comes from trusted user before storing
1580  * it into database!
1581  */
1582 function trusttext_mark($text) {
1583     global $CFG;
1584     if (!empty($CFG->enabletrusttext) and (strpos($text, TRUSTTEXT) === FALSE)) {
1585         return TRUSTTEXT.$text;
1586     } else {
1587         return $text;
1588     }
1590 function trusttext_after_edit(&$text, $context) {
1591     if (has_capability('moodle/site:trustcontent', $context)) {
1592         $text = trusttext_strip($text);
1593         $text = trusttext_mark($text);
1594     } else {
1595         $text = trusttext_strip($text);
1596     }
1599 function trusttext_prepare_edit(&$text, &$format, $usehtmleditor, $context) {
1600     global $CFG;
1602     $options = new object();
1603     $options->smiley = false;
1604     $options->filter = false;
1605     if (!empty($CFG->enabletrusttext)
1606          and has_capability('moodle/site:trustcontent', $context)
1607          and trusttext_present($text)) {
1608         $options->noclean = true;
1609     } else {
1610         $options->noclean = false;
1611     }
1612     $text = trusttext_strip($text);
1613     if ($usehtmleditor) {
1614         $text = format_text($text, $format, $options);
1615         $format = FORMAT_HTML;
1616     } else if (!$options->noclean){
1617         $text = clean_text($text, $format);
1618     }
1621 /**
1622  * Given raw text (eg typed in by a user), this function cleans it up
1623  * and removes any nasty tags that could mess up Moodle pages.
1624  *
1625  * @uses FORMAT_MOODLE
1626  * @uses FORMAT_PLAIN
1627  * @uses ALLOWED_TAGS
1628  * @param string $text The text to be cleaned
1629  * @param int $format Identifier of the text format to be used
1630  *            (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1631  * @return string The cleaned up text
1632  */
1633 function clean_text($text, $format=FORMAT_MOODLE) {
1635     global $ALLOWED_TAGS;
1637     switch ($format) {
1638         case FORMAT_PLAIN:
1639             return $text;
1641         default:
1643         /// Fix non standard entity notations
1644             $text = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $text);
1645             $text = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $text);
1647         /// Remove tags that are not allowed
1648             $text = strip_tags($text, $ALLOWED_TAGS);
1650         /// Clean up embedded scripts and , using kses
1651             $text = cleanAttributes($text);
1653         /// Remove script events
1654             $text = eregi_replace("([^a-z])language([[:space:]]*)=", "\\1Xlanguage=", $text);
1655             $text = eregi_replace("([^a-z])on([a-z]+)([[:space:]]*)=", "\\1Xon\\2=", $text);
1657             return $text;
1658     }
1661 /**
1662  * This function takes a string and examines it for HTML tags.
1663  * If tags are detected it passes the string to a helper function {@link cleanAttributes2()}
1664  *  which checks for attributes and filters them for malicious content
1665  *         17/08/2004              ::          Eamon DOT Costello AT dcu DOT ie
1666  *
1667  * @param string $str The string to be examined for html tags
1668  * @return string
1669  */
1670 function cleanAttributes($str){
1671     $result = preg_replace_callback(
1672             '%(<[^>]*(>|$)|>)%m', #search for html tags
1673             "cleanAttributes2",
1674             $str
1675             );
1676     return  $result;
1679 /**
1680  * This function takes a string with an html tag and strips out any unallowed
1681  * protocols e.g. javascript:
1682  * It calls ancillary functions in kses which are prefixed by kses
1683 *        17/08/2004              ::          Eamon DOT Costello AT dcu DOT ie
1684  *
1685  * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st
1686  *              element the html to be cleared
1687  * @return string
1688  */
1689 function cleanAttributes2($htmlArray){
1691     global $CFG, $ALLOWED_PROTOCOLS;
1692     require_once($CFG->libdir .'/kses.php');
1694     $htmlTag = $htmlArray[1];
1695     if (substr($htmlTag, 0, 1) != '<') {
1696         return '&gt;';  //a single character ">" detected
1697     }
1698     if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) {
1699         return ''; // It's seriously malformed
1700     }
1701     $slash = trim($matches[1]); //trailing xhtml slash
1702     $elem = $matches[2];    //the element name
1703     $attrlist = $matches[3]; // the list of attributes as a string
1705     $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
1707     $attStr = '';
1708     foreach ($attrArray as $arreach) {
1709         $arreach['name'] = strtolower($arreach['name']);
1710         if ($arreach['name'] == 'style') {
1711             $value = $arreach['value'];
1712             while (true) {
1713                 $prevvalue = $value;
1714                 $value = kses_no_null($value);
1715                 $value = preg_replace("/\/\*.*\*\//Us", '', $value);
1716                 $value = kses_decode_entities($value);
1717                 $value = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $value);
1718                 $value = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $value);
1719                 if ($value === $prevvalue) {
1720                     $arreach['value'] = $value;
1721                     break;
1722                 }
1723             }
1724             $arreach['value'] = preg_replace("/j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t/i", "Xjavascript", $arreach['value']);
1725             $arreach['value'] = preg_replace("/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n/i", "Xexpression", $arreach['value']);
1726         }
1727         $attStr .=  ' '.$arreach['name'].'="'.$arreach['value'].'" ';
1728     }
1730     // Remove last space from attribute list
1731     $attStr = rtrim($attStr);
1733     $xhtml_slash = '';
1734     if (preg_match('%/\s*$%', $attrlist)) {
1735         $xhtml_slash = ' /';
1736     }
1737     return '<'. $slash . $elem . $attStr . $xhtml_slash .'>';
1740 /**
1741  * Replaces all known smileys in the text with image equivalents
1742  *
1743  * @uses $CFG
1744  * @param string $text Passed by reference. The string to search for smily strings.
1745  * @return string
1746  */
1747 function replace_smilies(&$text) {
1748 ///
1749     global $CFG;
1751 /// this builds the mapping array only once
1752     static $runonce = false;
1753     static $e = array();
1754     static $img = array();
1755     static $emoticons = array(
1756         ':-)'  => 'smiley',
1757         ':)'   => 'smiley',
1758         ':-D'  => 'biggrin',
1759         ';-)'  => 'wink',
1760         ':-/'  => 'mixed',
1761         'V-.'  => 'thoughtful',
1762         ':-P'  => 'tongueout',
1763         'B-)'  => 'cool',
1764         '^-)'  => 'approve',
1765         '8-)'  => 'wideeyes',
1766         ':o)'  => 'clown',
1767         ':-('  => 'sad',
1768         ':('   => 'sad',
1769         '8-.'  => 'shy',
1770         ':-I'  => 'blush',
1771         ':-X'  => 'kiss',
1772         '8-o'  => 'surprise',
1773         'P-|'  => 'blackeye',
1774         '8-['  => 'angry',
1775         'xx-P' => 'dead',
1776         '|-.'  => 'sleepy',
1777         '}-]'  => 'evil',
1778         );
1780     if ($runonce == false) {  /// After the first time this is not run again
1781         foreach ($emoticons as $emoticon => $image){
1782             $alttext = get_string($image, 'pix');
1784             $e[] = $emoticon;
1785             $img[] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $CFG->pixpath .'/s/'. $image .'.gif" />';
1786         }
1787         $runonce = true;
1788     }
1790     // Exclude from transformations all the code inside <script> tags
1791     // Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
1792     // Based on code from glossary fiter by Williams Castillo.
1793     //       - Eloy
1795     // Detect all the <script> zones to take out
1796     $excludes = array();
1797     preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
1799     // Take out all the <script> zones from text
1800     foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
1801         $excludes['<+'.$key.'+>'] = $value;
1802     }
1803     if ($excludes) {
1804         $text = str_replace($excludes,array_keys($excludes),$text);
1805     }
1807 /// this is the meat of the code - this is run every time
1808     $text = str_replace($e, $img, $text);
1810     // Recover all the <script> zones to text
1811     if ($excludes) {
1812         $text = str_replace(array_keys($excludes),$excludes,$text);
1813     }
1816 /**
1817  * Given plain text, makes it into HTML as nicely as possible.
1818  * May contain HTML tags already
1819  *
1820  * @uses $CFG
1821  * @param string $text The string to convert.
1822  * @param boolean $smiley Convert any smiley characters to smiley images?
1823  * @param boolean $para If true then the returned string will be wrapped in paragraph tags
1824  * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
1825  * @return string
1826  */
1828 function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
1829 ///
1831     global $CFG;
1833 /// Remove any whitespace that may be between HTML tags
1834     $text = eregi_replace(">([[:space:]]+)<", "><", $text);
1836 /// Remove any returns that precede or follow HTML tags
1837     $text = eregi_replace("([\n\r])<", " <", $text);
1838     $text = eregi_replace(">([\n\r])", "> ", $text);
1840     convert_urls_into_links($text);
1842 /// Make returns into HTML newlines.
1843     if ($newlines) {
1844         $text = nl2br($text);
1845     }
1847 /// Turn smileys into images.
1848     if ($smiley) {
1849         replace_smilies($text);
1850     }
1852 /// Wrap the whole thing in a paragraph tag if required
1853     if ($para) {
1854         return '<p>'.$text.'</p>';
1855     } else {
1856         return $text;
1857     }
1860 /**
1861  * Given Markdown formatted text, make it into XHTML using external function
1862  *
1863  * @uses $CFG
1864  * @param string $text The markdown formatted text to be converted.
1865  * @return string Converted text
1866  */
1867 function markdown_to_html($text) {
1868     global $CFG;
1870     require_once($CFG->libdir .'/markdown.php');
1872     return Markdown($text);
1875 /**
1876  * Given HTML text, make it into plain text using external function
1877  *
1878  * @uses $CFG
1879  * @param string $html The text to be converted.
1880  * @return string
1881  */
1882 function html_to_text($html) {
1884     global $CFG;
1886     require_once($CFG->libdir .'/html2text.php');
1888     return html2text($html);
1891 /**
1892  * Given some text this function converts any URLs it finds into HTML links
1893  *
1894  * @param string $text Passed in by reference. The string to be searched for urls.
1895  */
1896 function convert_urls_into_links(&$text) {
1897 /// Make lone URLs into links.   eg http://moodle.com/
1898     $text = eregi_replace("([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])",
1899                           "\\1<a href=\"\\2://\\3\\4\" target=\"_blank\">\\2://\\3\\4</a>", $text);
1901 /// eg www.moodle.com
1902     $text = eregi_replace("([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])",
1903                           "\\1<a href=\"http://www.\\2\\3\" target=\"_blank\">www.\\2\\3</a>", $text);
1906 /**
1907  * This function will highlight search words in a given string
1908  * It cares about HTML and will not ruin links.  It's best to use
1909  * this function after performing any conversions to HTML.
1910  * Function found here: http://forums.devshed.com/t67822/scdaa2d1c3d4bacb4671d075ad41f0854.html
1911  *
1912  * @param string $needle The string to search for
1913  * @param string $haystack The string to search for $needle in
1914  * @param int $case ?
1915  * @return string
1916  * @todo Finish documenting this function
1917  */
1918 function highlight($needle, $haystack, $case=0,
1919                     $left_string='<span class="highlight">', $right_string='</span>') {
1920     if (empty($needle)) {
1921         return $haystack;
1922     }
1924     //$list_of_words = eregi_replace("[^-a-zA-Z0-9&.']", " ", $needle);  // bug 3101
1925     $list_of_words = $needle;
1926     $list_array = explode(' ', $list_of_words);
1927     for ($i=0; $i<sizeof($list_array); $i++) {
1928         if (strlen($list_array[$i]) == 1) {
1929             $list_array[$i] = '';
1930         }
1931     }
1932     $list_of_words = implode(' ', $list_array);
1933     $list_of_words_cp = $list_of_words;
1934     $final = array();
1935     preg_match_all('/<(.+?)>/is',$haystack,$list_of_words);
1937     foreach (array_unique($list_of_words[0]) as $key=>$value) {
1938         $final['<|'.$key.'|>'] = $value;
1939     }
1941     $haystack = str_replace($final,array_keys($final),$haystack);
1942     $list_of_words_cp = eregi_replace(' +', '|', $list_of_words_cp);
1944     if ($list_of_words_cp{0}=='|') {
1945         $list_of_words_cp{0} = '';
1946     }
1947     if ($list_of_words_cp{strlen($list_of_words_cp)-1}=='|') {
1948         $list_of_words_cp{strlen($list_of_words_cp)-1}='';
1949     }
1951     $list_of_words_cp = trim($list_of_words_cp);
1953     if ($list_of_words_cp) {
1955       $list_of_words_cp = "(". $list_of_words_cp .")";
1957       if (!$case){
1958         $haystack = eregi_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1959       } else {
1960         $haystack = ereg_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1961       }
1962     }
1963     $haystack = str_replace(array_keys($final),$final,$haystack);
1965     return $haystack;
1968 /**
1969  * This function will highlight instances of $needle in $haystack
1970  * It's faster that the above function and doesn't care about
1971  * HTML or anything.
1972  *
1973  * @param string $needle The string to search for
1974  * @param string $haystack The string to search for $needle in
1975  * @return string
1976  */
1977 function highlightfast($needle, $haystack) {
1979     $parts = explode(strtolower($needle), strtolower($haystack));
1981     $pos = 0;
1983     foreach ($parts as $key => $part) {
1984         $parts[$key] = substr($haystack, $pos, strlen($part));
1985         $pos += strlen($part);
1987         $parts[$key] .= '<span class="highlight">'.substr($haystack, $pos, strlen($needle)).'</span>';
1988         $pos += strlen($needle);
1989     }
1991     return (join('', $parts));
1995 /// STANDARD WEB PAGE PARTS ///////////////////////////////////////////////////
1997 /**
1998  * Print a standard header
1999  *
2000  * @uses $USER
2001  * @uses $CFG
2002  * @uses $SESSION
2003  * @param string $title Appears at the top of the window
2004  * @param string $heading Appears at the top of the page
2005  * @param string $navigation Premade navigation string (for use as breadcrumbs links)
2006  * @param string $focus Indicates form element to get cursor focus on load eg  inputform.password
2007  * @param string $meta Meta tags to be added to the header
2008  * @param boolean $cache Should this page be cacheable?
2009  * @param string $button HTML code for a button (usually for module editing)
2010  * @param string $menu HTML code for a popup menu
2011  * @param boolean $usexml use XML for this page
2012  * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
2013  * @param bool   $return If true, return the visible elements of the header instead of echoing them.
2014  */
2015 function print_header ($title='', $heading='', $navigation='', $focus='',
2016                        $meta='', $cache=true, $button='&nbsp;', $menu='',
2017                        $usexml=false, $bodytags='', $return=false) {
2019     global $USER, $CFG, $THEME, $SESSION, $ME, $SITE, $HTTPSPAGEREQUIRED;
2021 /// This makes sure that the header is never repeated twice on a page
2022     if (defined('HEADER_PRINTED')) {
2023         if ($CFG->debug > 7) {
2024             notify('print_header() was called more than once - this should not happen.  Please check the code for this page closely. Note: error() and redirect() are now safe to call after print_header().');
2025         }
2026         return;
2027     }
2028     define('HEADER_PRINTED', 'true');
2031     global $course, $COURSE;
2032     if (!empty($COURSE->lang)) {
2033         $CFG->courselang = $COURSE->lang;
2034         moodle_setlocale();
2035     } else if (!empty($course->lang)) { // ugly backwards compatibility hack
2036         $CFG->courselang = $course->lang;
2037         moodle_setlocale();
2038     }
2039     if (!empty($COURSE->theme)) {
2040         if (!empty($CFG->allowcoursethemes)) {
2041             $CFG->coursetheme = $COURSE->theme;
2042             theme_setup();
2043         }
2044     } else if (!empty($course->theme)) { // ugly backwards compatibility hack
2045         if (!empty($CFG->allowcoursethemes)) {
2046             $CFG->coursetheme = $course->theme;
2047             theme_setup();
2048         }
2049     }
2051 /// We have to change some URLs in styles if we are in a $HTTPSPAGEREQUIRED page
2052     if (!empty($HTTPSPAGEREQUIRED)) {
2053         $CFG->themewww = str_replace('http:', 'https:', $CFG->themewww);
2054         $CFG->pixpath = str_replace('http:', 'https:', $CFG->pixpath);
2055         $CFG->modpixpath = str_replace('http:', 'https:', $CFG->modpixpath);
2056         foreach ($CFG->stylesheets as $key => $stylesheet) {
2057             $CFG->stylesheets[$key] = str_replace('http:', 'https:', $stylesheet);
2058         }
2059     }
2061 /// Add the required stylesheets
2062     $stylesheetshtml = '';
2063     foreach ($CFG->stylesheets as $stylesheet) {
2064         $stylesheetshtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
2065     }
2066     $meta = $stylesheetshtml.$meta;
2069     if ($navigation == 'home') {
2070         $home = true;
2071         $navigation = '';
2072     } else {
2073         $home = false;
2074     }
2076 /// This is another ugly hack to make navigation elements available to print_footer later
2077     $THEME->title      = $title;
2078     $THEME->heading    = $heading;
2079     $THEME->navigation = $navigation;
2080     $THEME->button     = $button;
2081     $THEME->menu       = $menu;
2082     $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
2084     if ($button == '') {
2085         $button = '&nbsp;';
2086     }
2088     if (!$menu and $navigation) {
2089         if (empty($CFG->loginhttps)) {
2090             $wwwroot = $CFG->wwwroot;
2091         } else {
2092             $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
2093         }
2094         if (isset($course->id)) {
2095             $menu = user_login_string($course);
2096         } else {
2097             $menu = user_login_string($SITE);
2098         }
2099     }
2101     if (isset($SESSION->justloggedin)) {
2102         unset($SESSION->justloggedin);
2103         if (!empty($CFG->displayloginfailures)) {
2104             if (!empty($USER->username) and !isguest()) {
2105                 if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
2106                     $menu .= '&nbsp;<font size="1">';
2107                     if (empty($count->accounts)) {
2108                         $menu .= get_string('failedloginattempts', '', $count);
2109                     } else {
2110                         $menu .= get_string('failedloginattemptsall', '', $count);
2111                     }
2112                     if (isadmin()) {
2113                         $menu .= ' (<a href="'.$CFG->wwwroot.'/course/report/log/index.php'.
2114                                              '?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
2115                     }
2116                     $menu .= '</font>';
2117                 }
2118             }
2119         }
2120     }
2123     $encoding = current_charset();
2125     $meta = '<meta http-equiv="content-type" content="text/html; charset='. $encoding .'" />'. "\n". $meta ."\n";
2126     if (!$usexml) {
2127         @header('Content-type: text/html; charset='.$encoding);
2128     }
2130     if ( get_string('thisdirection') == 'rtl' ) {
2131         $direction = ' dir="rtl"';
2132     } else {
2133         $direction = ' dir="ltr"';
2134     }
2135     //Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag.
2136     $language = str_replace('_utf8','',$CFG->lang);
2137     $direction .= ' lang="'.$language.'" xml:lang="'.$language.'"';
2139     if ($cache) {  // Allow caching on "back" (but not on normal clicks)
2140         @header('Cache-Control: private, pre-check=0, post-check=0, max-age=0');
2141         @header('Pragma: no-cache');
2142         @header('Expires: ');
2143     } else {       // Do everything we can to always prevent clients and proxies caching
2144         @header('Cache-Control: no-store, no-cache, must-revalidate');
2145         @header('Cache-Control: post-check=0, pre-check=0', false);
2146         @header('Pragma: no-cache');
2147         @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
2148         @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
2150         $meta .= "\n<meta http-equiv=\"pragma\" content=\"no-cache\" />";
2151         $meta .= "\n<meta http-equiv=\"expires\" content=\"0\" />";
2152     }
2153     @header('Accept-Ranges: none');
2155     if ($usexml) {       // Added by Gustav Delius / Mad Alex for MathML output
2156                          // Modified by Julian Sedding
2157         $currentlanguage = current_language();
2158         $mathplayer = preg_match("/MathPlayer/i", $_SERVER['HTTP_USER_AGENT']);
2159         if(!$mathplayer) {
2160             header('Content-Type: application/xhtml+xml');
2161         }
2162         echo '<?xml version="1.0" ?>'."\n";
2163         if (!empty($CFG->xml_stylesheets)) {
2164             $stylesheets = explode(';', $CFG->xml_stylesheets);
2165             foreach ($stylesheets as $stylesheet) {
2166                 echo '<?xml-stylesheet type="text/xsl" href="'. $CFG->wwwroot .'/'. $stylesheet .'" ?>' . "\n";
2167             }
2168         }
2169         echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1';
2170         if (!empty($CFG->xml_doctype_extra)) {
2171             echo ' plus '. $CFG->xml_doctype_extra;
2172         }
2173         echo '//' . strtoupper($currentlanguage) . '" "'. $CFG->xml_dtd .'">'."\n";
2174         $direction = " xmlns=\"http://www.w3.org/1999/xhtml\"
2175                        xmlns:math=\"http://www.w3.org/1998/Math/MathML\"
2176                        xmlns:xlink=\"http://www.w3.org/1999/xlink\"
2177                        $direction";
2178         if($mathplayer) {
2179             $meta .= '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n";
2180             $meta .= '<!--comment required to prevent this becoming an empty tag-->'."\n";
2181             $meta .= '</object>'."\n";
2182             $meta .= '<?import namespace="math" implementation="#mathplayer" ?>' . "\n";
2183         }
2184     }
2186     // Clean up the title
2188     $title = str_replace('"', '&quot;', $title);
2189     $title = strip_tags($title);
2191     // Create class and id for this page
2193     page_id_and_class($pageid, $pageclass);
2195     if (isset($course->id)) {
2196         $pageclass .= ' course-'.$course->id;
2197     } else {
2198         $pageclass .= ' course-'.SITEID;
2199     }
2201     if (!empty($USER->editing)) {
2202         $pageclass .= ' editing';
2203     }
2205     $bodytags .= ' class="'.$pageclass.'" id="'.$pageid.'"';
2207     ob_start();
2208     include ($CFG->themedir.current_theme().'/header.html');
2209     $output = ob_get_contents();
2210     ob_end_clean();
2212     if (!empty($CFG->messaging)) {
2213         $output .= message_popup_window();
2214     }
2216     if ($return) {
2217         return $output;
2218     } else {
2219         echo $output;
2220     }
2223 /**
2224  * This version of print_header is simpler because the course name does not have to be
2225  * provided explicitly in the strings. It can be used on the site page as in courses
2226  * Eventually all print_header could be replaced by print_header_simple
2227  *
2228  * @param string $title Appears at the top of the window
2229  * @param string $heading Appears at the top of the page
2230  * @param string $navigation Premade navigation string (for use as breadcrumbs links)
2231  * @param string $focus Indicates form element to get cursor focus on load eg  inputform.password
2232  * @param string $meta Meta tags to be added to the header
2233  * @param boolean $cache Should this page be cacheable?
2234  * @param string $button HTML code for a button (usually for module editing)
2235  * @param string $menu HTML code for a popup menu
2236  * @param boolean $usexml use XML for this page
2237  * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
2238  * @param bool   $return If true, return the visible elements of the header instead of echoing them.
2239  */
2240 function print_header_simple($title='', $heading='', $navigation='', $focus='', $meta='',
2241                        $cache=true, $button='&nbsp;', $menu='', $usexml=false, $bodytags='', $return=false) {
2243     global $course,$CFG;                // The same hack is used in print_header
2245     $shortname ='';
2246     if ($course->category) {
2247         $shortname = '<a href="'.$CFG->wwwroot.'/course/view.php?id='. $course->id .'">'. $course->shortname .'</a> ->';
2248     }
2250     $output = print_header($course->shortname .': '. $title, $course->fullname .' '. $heading, $shortname .' '. $navigation, $focus, $meta,
2251                            $cache, $button, $menu, $usexml, $bodytags, true);
2253     if ($return) {
2254         return $output;
2255     } else {
2256         echo $output;
2257     }
2261 /**
2262  * Can provide a course object to make the footer contain a link to
2263  * to the course home page, otherwise the link will go to the site home
2264  *
2265  * @uses $CFG
2266  * @uses $USER
2267  * @param course $course {@link $COURSE} object containing course information
2268  * @param ? $usercourse ?
2269  * @todo Finish documenting this function
2270  */
2271 function print_footer($course=NULL, $usercourse=NULL, $return=false) {
2272     global $USER, $CFG, $THEME;
2274 /// Course links
2275     if ($course) {
2276         if (is_string($course) && $course == 'none') {          // Don't print any links etc
2277             $homelink = '';
2278             $loggedinas = '';
2279             $home  = false;
2280         } else if (is_string($course) && $course == 'home') {   // special case for site home page - please do not remove
2281             $course = get_site();
2282             $homelink  = '<div class="sitelink">'.
2283                '<a title="moodle '. $CFG->release .' ('. $CFG->version .')" href="http://moodle.org/" target="_blank">'.
2284                '<br /><img width="100" height="30" src="pix/moodlelogo.gif" border="0" alt="moodlelogo" /></a></div>';
2285             $home  = true;
2286         } else {
2287             $homelink = '<div class="homelink"><a target="'.$CFG->framename.'" href="'.$CFG->wwwroot.
2288                         '/course/view.php?id='.$course->id.'">'.$course->shortname.'</a></div>';
2289             $home  = false;
2290         }
2291     } else {
2292         $course = get_site();  // Set course as site course by default
2293         $homelink = '<div class="homelink"><a target="'.$CFG->framename.'" href="'.$CFG->wwwroot.'/">'.get_string('home').'</a></div>';
2294         $home  = false;
2295     }
2297 /// Set up some other navigation links (passed from print_header by ugly hack)
2298     $menu        = isset($THEME->menu) ? str_replace('navmenu', 'navmenufooter', $THEME->menu) : '';
2299     $title       = isset($THEME->title) ? $THEME->title : '';
2300     $button      = isset($THEME->button) ? $THEME->button : '';
2301     $heading     = isset($THEME->heading) ? $THEME->heading : '';
2302     $navigation  = isset($THEME->navigation) ? $THEME->navigation : '';
2303     $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
2306 /// Set the user link if necessary
2307     if (!$usercourse and is_object($course)) {
2308         $usercourse = $course;
2309     }
2311     if (!isset($loggedinas)) {
2312         $loggedinas = user_login_string($usercourse, $USER);
2313     }
2315     if ($loggedinas == $menu) {
2316         $menu = '';
2317     }
2319 /// Provide some performance info if required
2320     $performanceinfo = '';
2321     if (defined('MDL_PERF') || $CFG->perfdebug > 7) {
2322         $perf = get_performance_info();
2323         if (defined('MDL_PERFTOLOG')) {
2324             error_log("PERF: " . $perf['txt']);
2325         }
2326         if (defined('MDL_PERFTOFOOT') || $CFG->debug > 7 || $CFG->perfdebug > 7) {
2327             $performanceinfo = $perf['html'];
2328         }
2329     }
2332 /// Include the actual footer file
2334     ob_start();
2335     include ($CFG->themedir.current_theme().'/footer.html');
2336     $output = ob_get_contents();
2337     ob_end_clean();
2339     if ($return) {
2340         return $output;
2341     } else {
2342         echo $output;
2343     }
2346 /**
2347  * Returns the name of the current theme
2348  *
2349  * @uses $CFG
2350  * @param $USER
2351  * @param $SESSION
2352  * @return string
2353  */
2354 function current_theme() {
2355     global $CFG, $USER, $SESSION, $course;
2357     if (!empty($CFG->pagetheme)) {  // Page theme is for special page-only themes set by code
2358         return $CFG->pagetheme;
2360     } else if (!empty($CFG->coursetheme) and !empty($CFG->allowcoursethemes)) {  // Course themes override others
2361         return $CFG->coursetheme;
2363     } else if (!empty($SESSION->theme)) {    // Session theme can override other settings
2364         return $SESSION->theme;
2366     } else if (!empty($USER->theme) and !empty($CFG->allowuserthemes)) {    // User theme can override site theme
2367         return $USER->theme;
2369     } else {
2370         return $CFG->theme;
2371     }
2375 /**
2376  * This function is called by stylesheets to set up the header
2377  * approriately as well as the current path
2378  *
2379  * @uses $CFG
2380  * @param int $lastmodified ?
2381  * @param int $lifetime ?
2382  * @param string $thename ?
2383  */
2384 function style_sheet_setup($lastmodified=0, $lifetime=300, $themename='', $forceconfig='', $lang='') {
2386     global $CFG, $THEME;
2388     // Fix for IE6 caching - we don't want the filemtime('styles.php'), instead use now.
2389     $lastmodified = time();
2391     header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmodified) . ' GMT');
2392     header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
2393     header('Cache-Control: max-age='. $lifetime);
2394     header('Pragma: ');
2395     header('Content-type: text/css');  // Correct MIME type
2397     $DEFAULT_SHEET_LIST = array('styles_layout', 'styles_fonts', 'styles_color');
2399     if (empty($themename)) {
2400         $themename = current_theme();  // So we have something.  Normally not needed.
2401     } else {
2402         $themename = clean_param($themename, PARAM_SAFEDIR);
2403     }
2405     if (!empty($forceconfig)) {        // Page wants to use the config from this theme instead
2406         unset($THEME);
2407         include($CFG->themedir.$forceconfig.'/'.'config.php');
2408     }
2410 /// If this is the standard theme calling us, then find out what sheets we need
2412     if ($themename == 'standard') {
2413         if (!isset($THEME->standardsheets) or $THEME->standardsheets === true) { // Use all the sheets we have
2414             $THEME->sheets = $DEFAULT_SHEET_LIST;
2415         } else if (empty($THEME->standardsheets)) {                              // We can stop right now!
2416             echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2417             exit;
2418         } else {                                                                 // Use the provided subset only
2419             $THEME->sheets = $THEME->standardsheets;
2420         }
2422 /// If we are a parent theme, then check for parent definitions
2424     } else if (!empty($THEME->parent) && $themename == $THEME->parent) {
2425         if (!isset($THEME->parentsheets) or $THEME->parentsheets === true) {     // Use all the sheets we have
2426             $THEME->sheets = $DEFAULT_SHEET_LIST;
2427         } else if (empty($THEME->parentsheets)) {                                // We can stop right now!
2428             echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2429             exit;
2430         } else {                                                                 // Use the provided subset only
2431             $THEME->sheets = $THEME->parentsheets;
2432         }
2433     }
2435 /// Work out the last modified date for this theme
2437     foreach ($THEME->sheets as $sheet) {
2438         if (file_exists($CFG->themedir.$themename.'/'.$sheet.'.css')) {
2439             $sheetmodified = filemtime($CFG->themedir.$themename.'/'.$sheet.'.css');
2440             if ($sheetmodified > $lastmodified) {
2441                 $lastmodified = $sheetmodified;
2442             }
2443         }
2444     }
2447 /// Get a list of all the files we want to include
2448     $files = array();
2450     foreach ($THEME->sheets as $sheet) {
2451         $files[] = array($CFG->themedir, $themename.'/'.$sheet.'.css');
2452     }
2454     if ($themename == 'standard') {          // Add any standard styles included in any modules
2455         if (!empty($THEME->modsheets)) {     // Search for styles.php within activity modules
2456             if ($mods = get_list_of_plugins('mod')) {
2457                 foreach ($mods as $mod) {
2458                     if (file_exists($CFG->dirroot.'/mod/'.$mod.'/styles.php')) {
2459                         $files[] = array($CFG->dirroot, '/mod/'.$mod.'/styles.php');
2460                     }
2461                 }
2462             }
2463         }
2465         if (!empty($THEME->blocksheets)) {     // Search for styles.php within block modules
2466             if ($mods = get_list_of_plugins('blocks')) {
2467                 foreach ($mods as $mod) {
2468                     if (file_exists($CFG->dirroot.'/blocks/'.$mod.'/styles.php')) {
2469                         $files[] = array($CFG->dirroot, '/blocks/'.$mod.'/styles.php');
2470                     }
2471                 }
2472             }
2473         }
2475         if (!empty($THEME->langsheets)) {     // Search for styles.php within the current language
2476             if (file_exists($CFG->dirroot.'/lang/'.$lang.'/styles.php')) {
2477                 $files[] = array($CFG->dirroot, '/lang/'.$lang.'/styles.php');
2478             }
2479         }
2480     }
2483     if ($files) {
2484     /// Produce a list of all the files first
2485         echo '/**************************************'."\n";
2486         echo ' * THEME NAME: '.$themename."\n *\n";
2487         echo ' * Files included in this sheet:'."\n *\n";
2488         foreach ($files as $file) {
2489             echo ' *   '.$file[1]."\n";
2490         }
2491         echo ' **************************************/'."\n\n";
2494     /// Actually output all the files in order.
2495         foreach ($files as $file) {
2496             echo '/***** '.$file[1].' start *****/'."\n\n";
2497             @include_once($file[0].$file[1]);
2498             echo '/***** '.$file[1].' end *****/'."\n\n";
2499         }
2500     }
2502     return $CFG->themewww.$themename;   // Only to help old themes (1.4 and earlier)
2506 function theme_setup($theme = '', $params=NULL) {
2507 /// Sets up global variables related to themes
2509     global $CFG, $THEME, $SESSION, $USER;
2511     if (empty($theme)) {
2512         $theme = current_theme();
2513     }
2515 /// Load up the theme config
2516     $THEME = NULL;   // Just to be sure
2517     include($CFG->themedir. $theme .'/config.php');  // Main config for current theme
2519 /// Put together the parameters
2520     if (!$params) {
2521         $params = array();
2522     }
2523     if ($theme != $CFG->theme) {
2524         $params[] = 'forceconfig='.$theme;
2525     }
2527 /// Force language too if required
2528     if (!empty($THEME->langsheets)) {
2529         $params[] = 'lang='.current_language();
2530     }
2532 /// Convert params to string
2533     if ($params) {
2534         $paramstring = '?'.implode('&', $params);
2535     } else {
2536         $paramstring = '';
2537     }
2539 /// Set up image paths
2540     if (empty($THEME->custompix)) {    // Could be set in the above file
2541         $CFG->pixpath = $CFG->wwwroot .'/pix';
2542         $CFG->modpixpath = $CFG->wwwroot .'/mod';
2543     } else {
2544         $CFG->pixpath = $CFG->themewww . $theme .'/pix';
2545         $CFG->modpixpath = $CFG->themewww . $theme .'/pix/mod';
2546     }
2548 /// Header and footer paths
2549     $CFG->header = $CFG->themedir . $theme .'/header.html';
2550     $CFG->footer = $CFG->themedir . $theme .'/footer.html';
2552 /// Define stylesheet loading order
2553     $CFG->stylesheets = array();
2554     if ($theme != 'standard') {    /// The standard sheet is always loaded first
2555         $CFG->stylesheets[] = $CFG->themewww.'standard/styles.php'.$paramstring;
2556     }
2557     if (!empty($THEME->parent)) {  /// Parent stylesheets are loaded next
2558         $CFG->stylesheets[] = $CFG->themewww.$THEME->parent.'/styles.php'.$paramstring;
2559     }
2560     $CFG->stylesheets[] = $CFG->themewww.$theme.'/styles.php'.$paramstring;
2565 /**
2566  * Returns text to be displayed to the user which reflects their login status
2567  *
2568  * @uses $CFG
2569  * @uses $USER
2570  * @param course $course {@link $COURSE} object containing course information
2571  * @param user $user {@link $USER} object containing user information
2572  * @return string
2573  */
2574 function user_login_string($course=NULL, $user=NULL) {
2575     global $USER, $CFG, $SITE;
2577     if (empty($user) and !empty($USER->id)) {
2578         $user = $USER;
2579     }
2581     if (empty($course)) {
2582         $course = $SITE;
2583     }
2585     if (isset($user->realuser)) {
2586         if ($realuser = get_record('user', 'id', $user->realuser)) {
2587             $fullname = fullname($realuser, true);
2588             $realuserinfo = " [<a target=\"{$CFG->framename}\"
2589             href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&amp;return=1\">$fullname</a>] ";
2590         }
2591     } else {
2592         $realuserinfo = '';
2593     }
2595     if (empty($CFG->loginhttps)) {
2596         $wwwroot = $CFG->wwwroot;
2597     } else {
2598         $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
2599     }
2601     if (isset($user->id) and $user->id) {
2602         $fullname = fullname($user, true);
2603         $username = "<a target=\"{$CFG->framename}\" href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>";
2604         if (isguest($user->id)) {
2605             $loggedinas = $realuserinfo.get_string('loggedinasguest').
2606                       " (<a target=\"{$CFG->framename}\" href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
2607         } else {
2608             $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '.
2609                       " (<a target=\"{$CFG->framename}\" href=\"$CFG->wwwroot/login/logout.php\">".get_string('logout').'</a>)';
2610         }
2611     } else {
2612         $loggedinas = get_string('loggedinnot', 'moodle').
2613                       " (<a target=\"{$CFG->framename}\" href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
2614     }
2615     return '<div class="logininfo">'.$loggedinas.'</div>';
2618 /**
2619  * Prints breadcrumbs links
2620  *
2621  * @uses $CFG
2622  * @param string $navigation The breadcrumbs string to be printed
2623  */
2624 function print_navigation ($navigation, $return=false) {
2625     global $CFG, $USER;
2627     $output = '';
2629     if ($navigation) {
2630         //Accessibility: breadcrumb links now in a list, &raquo; replaced with image.
2631         $nav_text = get_string('youarehere','access');
2632         $output .= '<h2 class="accesshide">'.$nav_text.",</h2><ul>\n";
2633         if (! $site = get_site()) {
2634             $site->shortname = get_string('home');
2635         }
2636         $navigation = '<li title="'.$nav_text.'"><img src="'.$CFG->pixpath.'/a/r_breadcrumb.gif" class="resize" alt="" /> '
2637             .str_replace('->', '</li><li title="'.$nav_text.'"><img src="'.$CFG->pixpath.'/a/r_breadcrumb.gif" class="resize" alt="" /> ', $navigation)."</li>\n";
2638         $output .= '<li class="first"><a target="'. $CFG->framename .'" href="'. $CFG->wwwroot.((!isadmin() && !empty($USER->id) && !empty($CFG->mymoodleredirect) && !isguest())
2639                                                                        ? '/my' : '') .'/">'. $site->shortname ."</a></li>\n". $navigation;
2640         $output .= "</ul>\n";  
2641     }
2643     if ($return) {
2644         return $output;
2645     } else {
2646         echo $output;
2647     }
2650 /**
2651  * Prints a string in a specified size  (retained for backward compatibility)
2652  *
2653  * @param string $text The text to be displayed
2654  * @param int $size The size to set the font for text display.
2655  */
2656 function print_headline($text, $size=2, $return=false) {
2657     $output = print_heading($text, 'left', $size, true);
2658     if ($return) {
2659         return $output;
2660     } else {
2661         echo $output;
2662     }
2665 /**
2666  * Prints text in a format for use in headings.
2667  *
2668  * @param string $text The text to be displayed
2669  * @param string $align The alignment of the printed paragraph of text
2670  * @param int $size The size to set the font for text display.
2671  */
2672 function print_heading($text, $align='', $size=2, $class='main', $return=false) {
2673     if ($align) {
2674         $align = ' align="'.$align.'"';
2675     }
2676     if ($class) {
2677         $class = ' class="'.$class.'"';
2678     }
2679     $output = "<h$size $align $class>".stripslashes_safe($text)."</h$size>";
2681     if ($return) {
2682         return $output;
2683     } else {
2684         echo $output;
2685     }
2688 /**
2689  * Centered heading with attached help button (same title text)
2690  * and optional icon attached
2691  *
2692  * @param string $text The text to be displayed
2693  * @param string $helppage The help page to link to
2694  * @param string $module The module whose help should be linked to
2695  * @param string $icon Image to display if needed
2696  */
2697 function print_heading_with_help($text, $helppage, $module='moodle', $icon='', $return=false) {
2698     $output = '';
2699     $output .= '<h2 class="main help">'.$icon.stripslashes_safe($text);
2700     $output .= helpbutton($helppage, $text, $module, true, false, '', true);
2701     $output .= '</h2>';
2703     if ($return) {
2704         return $output;
2705     } else {
2706         echo $output;
2707     }
2711 function print_heading_block($heading, $class='', $return=false) {
2712     //Accessibility: 'headingblock' is now H1, see theme/standard/styles_*.css: ??
2713     $output = '<h2 class="headingblock header '.$class.'">'.stripslashes($heading).'</h2>';
2715     if ($return) {
2716         return $output;
2717     } else {
2718         echo $output;
2719     }
2723 /**
2724  * Print a link to continue on to another page.
2725  *
2726  * @uses $CFG
2727  * @param string $link The url to create a link to.
2728  */
2729 function print_continue($link, $return=false) {
2731     global $CFG;
2733     // in case we are logging upgrade in admin/index.php stop it
2734     if (function_exists('upgrade_log_finish')) {
2735         upgrade_log_finish();
2736     }
2738     $output = '';
2740     if (!$link) {
2741         $link = $_SERVER['HTTP_REFERER'];
2742     }
2744     $output .= '<div class="continuebutton">';
2745     $output .= print_single_button($link, NULL, get_string('continue'), 'post', $CFG->framename, true);
2746     $output .= '</div>'."\n";
2748     if ($return) {
2749         return $output;
2750     } else {
2751         echo $output;
2752     }
2755 /**
2756  * Print a message in a standard themed box.
2757  * See, {@link print_simple_box_start}.
2758  *
2759  * @param string $align string, alignment of the box, not the text (default center, left, right).
2760  * @param string $width string, width of the box, including units %, for example '100%'. 
2761  * @param string $color string, background colour of the box, for example '#eee'.
2762  * @param int $padding integer, padding in pixels, specified without units.
2763  * @param string $class string, space-separated class names.
2764  * @todo Finish documenting this function
2765  */
2766 function print_simple_box($message, $align='', $width='', $color='', $padding=5, $class='generalbox', $id='', $return=false) {
2767     $output = '';
2768     $output .= print_simple_box_start($align, $width, $color, $padding, $class, $id, true);
2769     $output .= stripslashes_safe($message);
2770     $output .= print_simple_box_end(true);
2772     if ($return) {
2773         return $output;
2774     } else {
2775         echo $output;
2776     }
2779 /**
2780  * Print the top portion of a standard themed box using a TABLE.  Yes, we know.
2781  * See bug 4943 for details on some accessibility work regarding this that didn't make it into 1.6.
2782  *
2783  * @param string $align string, alignment of the box, not the text (default center, left, right).
2784  * @param string $width string, width of the box, including % units, for example '100%'.
2785  * @param string $color string, background colour of the box, for example '#eee'.
2786  * @param int $padding integer, padding in pixels, specified without units.
2787  * @param string $class string, space-separated class names.
2788  */
2789 function print_simple_box_start($align='', $width='', $color='', $padding=5, $class='generalbox', $id='', $return=false) {
2791     $output = '';
2793     if ($color) {
2794         $color = 'bgcolor="'. $color .'"';
2795     }
2796     if ($align) {
2797         $align = 'align="'. $align .'"';
2798     }
2799     if ($width) {
2800         $width = 'width="'. $width .'"';
2801     }
2802     if ($id) {
2803         $id = 'id="'. $id .'"';
2804     }
2805     $output .= "<table $align $width $id class=\"$class\" border=\"0\" cellpadding=\"$padding\" cellspacing=\"0\">".
2806          "<tr><td $color class=\"$class"."content\">";
2808     if ($return) {
2809         return $output;
2810     } else {
2811         echo $output;
2812     }
2815 /**
2816  * Print the end portion of a standard themed box.
2817  */
2818 function print_simple_box_end($return=false) {
2819     $output = '</td></tr></table>';
2820     if ($return) {
2821         return $output;
2822     } else {
2823         echo $output;
2824     }
2828 /**
2829  * Print a self contained form with a single submit button.
2830  *
2831  * @param string $link ?
2832  * @param array $options ?
2833  * @param string $label ?
2834  * @param string $method ?
2835  * @todo Finish documenting this function
2836  */
2837 function print_single_button($link, $options, $label='OK', $method='get', $target='_self', $return=false) {
2838     $output = '';
2839     $output .= '<div class="singlebutton">';
2840     $output .= '<form action="'. $link .'" method="'. $method .'" target="'.$target.'">';
2841     if ($options) {
2842         foreach ($options as $name => $value) {
2843             $output .= '<input type="hidden" name="'. $name .'" value="'. $value .'" />';
2844         }
2845     }
2846     $output .= '<input type="submit" value="'. $label .'" /></form></div>';
2848     if ($return) {
2849         return $output;
2850     } else {
2851         echo $output;
2852     }
2855 /**
2856  * Print a spacer image with the option of including a line break.
2857  *
2858  * @param int $height ?
2859  * @param int $width ?
2860  * @param boolean $br ?
2861  * @todo Finish documenting this function
2862  */
2863 function print_spacer($height=1, $width=1, $br=true, $return=false) {
2864     global $CFG;
2865     $output = '';
2867     $output .= '<img class="spacer" height="'. $height .'" width="'. $width .'" src="'. $CFG->wwwroot .'/pix/spacer.gif" alt="" />';
2868     if ($br) {
2869         $output .= '<br />'."\n";
2870     }
2872     if ($return) {
2873         return $output;
2874     } else {
2875         echo $output;
2876     }
2879 /**
2880  * Given the path to a picture file in a course, or a URL,
2881  * this function includes the picture in the page.
2882  *
2883  * @param string $path ?
2884  * @param int $courseid ?
2885  * @param int $height ?
2886  * @param int $width ?
2887  * @param string $link ?
2888  * @todo Finish documenting this function
2889  */
2890 function print_file_picture($path, $courseid=0, $height='', $width='', $link='', $return=false) {
2891     global $CFG;
2892     $output = '';
2894     if ($height) {
2895         $height = 'height="'. $height .'"';
2896     }
2897     if ($width) {
2898         $width = 'width="'. $width .'"';
2899     }
2900     if ($link) {
2901         $output .= '<a href="'. $link .'">';
2902     }
2903     if (substr(strtolower($path), 0, 7) == 'http://') {
2904         $output .= '<img border="0" '.$height . $width .' src="'. $path .'" />';
2906     } else if ($courseid) {
2907         $output .= '<img border="0" '. $height . $width .' src="';
2908         if ($CFG->slasharguments) {        // Use this method if possible for better caching
2909             $output .= $CFG->wwwroot .'/file.php/'. $courseid .'/'. $path;
2910         } else {
2911             $output .= $CFG->wwwroot .'/file.php?file=/'. $courseid .'/'. $path;
2912         }
2913         $output .= '" />';
2914     } else {
2915         $output .= 'Error: must pass URL or course';
2916     }
2917     if ($link) {
2918         $output .= '</a>';
2919     }
2921     if ($return) {
2922         return $output;
2923     } else {
2924         echo $output;
2925     }
2928 /**
2929  * Print the specified user's avatar.
2930  *
2931  * @param int $userid ?
2932  * @param int $courseid ?
2933  * @param boolean $picture Print the user picture?
2934  * @param int $size Size in pixels.  Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatability
2935  * @param boolean $return If false print picture to current page, otherwise return the output as string
2936  * @param boolean $link Enclose printed image in a link to view specified course?
2937  * return string
2938  * @todo Finish documenting this function
2939  */
2940 function print_user_picture($userid, $courseid, $picture, $size=0, $return=false, $link=true, $target='') {
2941     global $CFG;
2943     if ($link) {
2944         if ($target) {
2945             $target=' target="_blank"';
2946         }
2947         $output = '<a '.$target.' href="'. $CFG->wwwroot .'/user/view.php?id='. $userid .'&amp;course='. $courseid .'">';
2948     } else {
2949         $output = '';
2950     }
2951     if (empty($size)) {
2952         $file = 'f2';
2953         $size = 35;
2954     } else if ($size === true or $size == 1) {
2955         $file = 'f1';
2956         $size = 100;
2957     } else if ($size >= 50) {
2958         $file = 'f1';
2959     } else {
2960         $file = 'f2';
2961     }
2962     $class = "userpicture";
2963     if ($picture) {  // Print custom user picture
2964         if ($CFG->slasharguments) {        // Use this method if possible for better caching
2965             $src =  $CFG->wwwroot .'/user/pix.php/'. $userid .'/'. $file .'.jpg"';
2966         } else {
2967             $src =  $CFG->wwwroot .'/user/pix.php?file=/'. $userid .'/'. $file .'.jpg"';
2968         }
2969     } else {         // Print default user pictures (use theme version if available)
2970         $class .= " defaultuserpic";
2971         $src =  "$CFG->pixpath/u/$file.png\"";
2972     }
2973     $output .= "<img class=\"$class\" align=\"middle\" src=\"$src".
2974                    " border=\"0\" width=\"$size\" height=\"$size\" alt=\"\" />";
2975     if ($link) {
2976         $output .= '</a>';
2977     }
2979     if ($return) {
2980         return $output;
2981     } else {
2982         echo $output;
2983     }
2986 /**
2987  * Prints a summary of a user in a nice little box.
2988  *
2989  * @uses $CFG
2990  * @uses $USER
2991  * @param user $user A {@link $USER} object representing a user
2992  * @param course $course A {@link $COURSE} object representing a course
2993  */
2994 function print_user($user, $course, $messageselect=false, $return=false) {
2996     global $CFG, $USER;
2998     $output = '';
3000     static $string;
3001     static $datestring;
3002     static $countries;
3003     static $isteacher;
3004     static $isadmin;
3006     $context = get_context_instance(CONTEXT_COURSE, $course->id);
3007     if (empty($string)) {     // Cache all the strings for the rest of the page
3009         $string->email       = get_string('email');
3010         $string->location    = get_string('location');
3011         $string->lastaccess  = get_string('lastaccess');
3012         $string->activity    = get_string('activity');
3013         $string->unenrol     = get_string('unenrol');
3014         $string->loginas     = get_string('loginas');
3015         $string->fullprofile = get_string('fullprofile');
3016         $string->role        = get_string('role');
3017         $string->name        = get_string('name');
3018         $string->never       = get_string('never');
3020         $datestring->day     = get_string('day');
3021         $datestring->days    = get_string('days');
3022         $datestring->hour    = get_string('hour');
3023         $datestring->hours   = get_string('hours');
3024         $datestring->min     = get_string('min');
3025         $datestring->mins    = get_string('mins');
3026         $datestring->sec     = get_string('sec');
3027         $datestring->secs    = get_string('secs');
3029         $countries = get_list_of_countries();
3031         $isteacher = isteacher($course->id);
3032         $isadmin   = isadmin();
3033     }
3035 /// Get the hidden field list
3036     if ($isteacher || $isadmin) {
3037         $hiddenfields = array();
3038     } else {
3039         $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
3040     }
3042     $output .= '<table class="userinfobox">';
3043     $output .= '<tr>';
3044     $output .= '<td class="left side">';
3045     $output .= print_user_picture($user->id, $course->id, $user->picture, true, true);
3046     $output .= '</td>';
3047     $output .= '<td class="content">';
3048     $output .= '<div class="username">'.fullname($user, has_capability('moodle/site:viewfullnames', $context)).'</div>';
3049     $output .= '<div class="info">';
3050     if (!empty($user->role) and ($user->role <> $course->teacher)) {
3051         $output .= $string->role .': '. $user->role .'<br />';
3052     }
3053     if ($user->maildisplay == 1 or ($user->maildisplay == 2 and $course->category and !isguest()) or $isteacher) {
3054         $output .= $string->email .': <a href="mailto:'. $user->email .'">'. $user->email .'</a><br />';
3055     }
3056     if (($user->city or $user->country) and (!isset($hiddenfields['city']) or !isset($hiddenfields['country']))) {
3057         $output .= $string->location .': ';
3058         if ($user->city && !isset($hiddenfields['city'])) {
3059             $output .= $user->city;
3060         }
3061         if (!empty($countries[$user->country]) && !isset($hiddenfields['country'])) {
3062             if ($user->city && !isset($hiddenfields['city'])) {
3063                 $output .= ', ';
3064             }
3065             $output .= $countries[$user->country];
3066         }
3067         $output .= '<br />';
3068     }
3070     if (!isset($hiddenfields['lastaccess'])) {
3071         if ($user->lastaccess) {
3072             $output .= $string->lastaccess .': '. userdate($user->lastaccess);
3073             $output .= '&nbsp ('. format_time(time() - $user->lastaccess, $datestring) .')';
3074         } else {
3075             $output .= $string->lastaccess .': '. $string->never;
3076         }
3077     }
3078     $output .= '</div></td><td class="links">';
3079     //link to blogs
3080     if ($CFG->bloglevel > 0) {
3081         $output .= '<a href="'.$CFG->wwwroot.'/blog/index.php?userid='.$user->id.'">'.get_string('blogs','blog').'</a><br />';
3082     }
3084     if ($isteacher) {
3085         $timemidnight = usergetmidnight(time());
3086         $output .= '<a href="'. $CFG->wwwroot .'/course/user.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->activity .'</a><br />';
3087         if (!iscreator($user->id) or ($isadmin and !isadmin($user->id))) {  // Includes admins
3088             if ($course->category and isteacheredit($course->id) and isstudent($course->id, $user->id)) {  // Includes admins
3089                 $output .= '<a href="'. $CFG->wwwroot .'/course/unenrol.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->unenrol .'</a><br />';
3090             }
3091             if ($USER->id != $user->id) {
3092                 $output .= '<a href="'. $CFG->wwwroot .'/course/loginas.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->loginas .'</a><br />';
3093             }
3094         }
3095     }
3096     $output .= '<a href="'. $CFG->wwwroot .'/user/view.php?id='. $user->id .'&amp;course='. $course->id .'">'. $string->fullprofile .'...</a>';
3098     if (!empty($messageselect) && $isteacher) {
3099         $output .= '<br /><input type="checkbox" name="';
3100         if (isteacher($course->id, $user->id)) {
3101             $output .= 'teacher';
3102         } else {
3103             $output .= 'user';
3104         }
3105         $output .= $user->id.'" /> ';
3106     }
3108     $output .= '</td></tr></table>';
3110     if ($return) {
3111         return $output;
3112     } else {
3113         echo $output;
3114     }
3117 /**
3118  * Print a specified group's avatar.
3119  *
3120  * @param group $group A {@link group} object representing a group or array of groups
3121  * @param int $courseid ?
3122  * @param boolean $large ?
3123  * @param boolean $return ?
3124  * @param boolean $link ?
3125  * @return string
3126  * @todo Finish documenting this function
3127  */
3128 function print_group_picture($group, $courseid, $large=false, $return=false, $link=true) {
3129     global $CFG;
3131     if (is_array($group)) {
3132         $output = '';
3133         foreach($group as $g) {
3134             $output .= print_group_picture($g, $courseid, $large, true, $link);
3135         }
3136         if ($return) {
3137             return $output;
3138         } else {
3139             echo $output;
3140             return;
3141         }
3142     }
3144     static $isteacheredit;
3145     $context = get_context_instance(CONTEXT_COURSE, $courseid);
3146     if (!isset($isteacheredit)) {
3147         $isteacheredit = isteacheredit($courseid);
3148     }
3150     if ($group->hidepicture and !has_capability('moodle/course:managegroups', $context)) {
3151         return '';
3152     }
3154     if ($link or has_capability('moodle/site:accessallgroups', $context)) {
3155         $output = '<a href="'. $CFG->wwwroot .'/user/index.php?id='. $courseid .'&amp;group='. $group->id .'">';
3156     } else {
3157         $output = '';
3158     }
3159     if ($large) {
3160         $file = 'f1';
3161         $size = 100;
3162     } else {
3163         $file = 'f2';
3164         $size = 35;
3165     }
3166     if ($group->picture) {  // Print custom group picture
3167         if ($CFG->slasharguments) {        // Use this method if possible for better caching
3168             $output .= '<img class="grouppicture" align="middle" src="'.$CFG->wwwroot.'/user/pixgroup.php/'.$group->id.'/'.$file.'.jpg"'.
3169                        ' border="0" width="'.$size.'" height="'.$size.'" alt="" title="'.s($group->name).'"/>';
3170         } else {
3171             $output .= '<img class="grouppicture" align="middle" src="'.$CFG->wwwroot.'/user/pixgroup.php?file=/'.$group->id.'/'.$file.'.jpg"'.
3172                        ' border="0" width="'.$size.'" height="'.$size.'" alt="" title="'.s($group->name).'"/>';
3173         }
3174     }
3175     if ($link or has_capability('moodle/site:accessallgroups', $context)) {
3176         $output .= '</a>';
3177     }
3179     if ($return) {
3180         return $output;
3181     } else {
3182         echo $output;
3183     }
3186 /**
3187  * Print a png image.
3188  *
3189  * @param string $url ?
3190  * @param int $sizex ?
3191  * @param int $sizey ?
3192  * @param boolean $return ?
3193  * @param string $parameters ?
3194  * @todo Finish documenting this function
3195  */
3196 function print_png($url, $sizex, $sizey, $return, $parameters='alt=""') {
3197     global $CFG;
3198     static $recentIE;
3200     if (!isset($recentIE)) {
3201         $recentIE = check_browser_version('MSIE', '5.0');
3202     }
3204     if ($recentIE) {  // work around the HORRIBLE bug IE has with alpha transparencies
3205         $output .= '<img src="'. $CFG->pixpath .'/spacer.gif" width="'. $sizex .'" height="'. $sizey .'"'.
3206                    ' border="0" class="png" style="width: '. $sizex .'px; height: '. $sizey .'px; '.
3207                    ' filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='.
3208                    "'$url', sizingMethod='scale') ".
3209                    ' '. $parameters .' />';
3210     } else {
3211         $output .= '<img src="'. $url .'" border="0" width="'. $sizex .'" height="'. $sizey .'" '.
3212                    ' '. $parameters .' />';
3213     }
3215     if ($return) {
3216         return $output;
3217     } else {
3218         echo $output;
3219     }
3222 /**
3223  * Print a nicely formatted table.
3224  *
3225  * @param array $table is an object with several properties.
3226  *     <ul<li>$table->head - An array of heading names.
3227  *     <li>$table->align - An array of column alignments
3228  *     <li>$table->size  - An array of column sizes
3229  *     <li>$table->wrap - An array of "nowrap"s or nothing
3230  *     <li>$table->data[] - An array of arrays containing the data.
3231  *     <li>$table->width  - A percentage of the page
3232  *     <li>$table->tablealign  - Align the whole table
3233  *     <li>$table->cellpadding  - Padding on each cell
3234  *     <li>$table->cellspacing  - Spacing between cells
3235  * </ul>
3236  * @param bool $return whether to return an output string or echo now
3237  * @return boolean or $string
3238  * @todo Finish documenting this function
3239  */
3240 function print_table($table, $return=false) {
3241     $output = '';
3243     if (isset($table->align)) {
3244         foreach ($table->align as $key => $aa) {
3245             if ($aa) {
3246                 $align[$key] = ' align="'. $aa .'"';
3247             } else {
3248                 $align[$key] = '';
3249             }
3250         }
3251     }
3252     if (isset($table->size)) {
3253         foreach ($table->size as $key => $ss) {
3254             if ($ss) {
3255                 $size[$key] = ' width="'. $ss .'"';
3256             } else {
3257                 $size[$key] = '';
3258             }
3259         }
3260     }
3261     if (isset($table->wrap)) {
3262         foreach ($table->wrap as $key => $ww) {
3263             if ($ww) {
3264                 $wrap[$key] = ' nowrap="nowrap" ';
3265             } else {
3266                 $wrap[$key] = '';
3267             }
3268         }
3269     }
3271     if (empty($table->width)) {
3272         $table->width = '80%';
3273     }
3275     if (empty($table->tablealign)) {
3276         $table->tablealign = 'center';
3277     }
3279     if (empty($table->cellpadding)) {
3280         $table->cellpadding = '5';
3281     }
3283     if (empty($table->cellspacing)) {
3284         $table->cellspacing = '1';
3285     }
3287     if (empty($table->class)) {
3288         $table->class = 'generaltable';
3289     }
3291     $tableid = empty($table->id) ? '' : 'id="'.$table->id.'"';
3293     $output .= '<table width="'.$table->width.'" border="0" align="'.$table->tablealign.'" ';
3294     $output .= " cellpadding=\"$table->cellpadding\" cellspacing=\"$table->cellspacing\" class=\"$table->class\" $tableid>\n";
3296     $countcols = 0;
3298     if (!empty($table->head)) {
3299         $countcols = count($table->head);
3300         $output .= '<tr>';
3301         foreach ($table->head as $key => $heading) {
3303             if (!isset($size[$key])) {
3304                 $size[$key] = '';
3305             }
3306             if (!isset($align[$key])) {
3307                 $align[$key] = '';
3308             }
3309             $output .= '<th valign="top" '. $align[$key].$size[$key] .' nowrap="nowrap" class="header c'.$key.'">'. $heading .'</th>';
3310         }
3311         $output .= '</tr>'."\n";
3312     }
3314     if (!empty($table->data)) {
3315         $oddeven = 1;
3316         foreach ($table->data as $key => $row) {
3317             $oddeven = $oddeven ? 0 : 1;
3318             $output .= '<tr class="r'.$oddeven.'">'."\n";
3319             if ($row == 'hr' and $countcols) {
3320                 $output .= '<td colspan="'. $countcols .'"><div class="tabledivider"></div></td>';
3321             } else {  /// it's a normal row of data
3322                 foreach ($row as $key => $item) {
3323                     if (!isset($size[$key])) {
3324                         $size[$key] = '';
3325                     }
3326                     if (!isset($align[$key])) {
3327                         $align[$key] = '';
3328                     }
3329                     if (!isset($wrap[$key])) {
3330                         $wrap[$key] = '';
3331                     }
3332                     $output .= '<td '. $align[$key].$size[$key].$wrap[$key] .' class="cell c'.$key.'">'. $item .'</td>';
3333                 }
3334             }
3335             $output .= '</tr>'."\n";
3336         }
3337     }
3338     $output .= '</table>'."\n";
3340     if ($return) {
3341         return $output;
3342     }
3344     echo $output;
3345     return true;
3348 /**
3349  * Creates a nicely formatted table and returns it.
3350  *
3351  * @param array $table is an object with several properties.
3352  *     <ul<li>$table->head - An array of heading names.
3353  *     <li>$table->align - An array of column alignments
3354  *     <li>$table->size  - An array of column sizes
3355  *     <li>$table->wrap - An array of "nowrap"s or nothing
3356  *     <li>$table->data[] - An array of arrays containing the data.
3357  *     <li>$table->class -  A css class name
3358  *     <li>$table->fontsize - Is the size of all the text
3359  *     <li>$table->tablealign  - Align the whole table
3360  *     <li>$table->width  - A percentage of the page
3361  *     <li>$table->cellpadding  - Padding on each cell
3362  *     <li>$table->cellspacing  - Spacing between cells
3363  * </ul>
3364  * @return string
3365  * @todo Finish documenting this function
3366  */
3367 function make_table($table) {
3369     if (isset($table->align)) {
3370         foreach ($table->align as $key => $aa) {
3371             if ($aa) {
3372                 $align[$key] = ' align="'. $aa .'"';
3373             } else {
3374                 $align[$key] = '';
3375             }
3376         }
3377     }
3378     if (isset($table->size)) {
3379         foreach ($table->size as $key => $ss) {
3380             if ($ss) {
3381                 $size[$key] = ' width="'. $ss .'"';
3382             } else {
3383                 $size[$key] = '';
3384             }
3385         }
3386     }
3387     if (isset($table->wrap)) {
3388         foreach ($table->wrap as $key => $ww) {
3389             if ($ww) {
3390                 $wrap[$key] = ' nowrap="nowrap" ';
3391             } else {
3392                 $wrap[$key] = '';
3393             }
3394         }
3395     }
3397     if (empty($table->width)) {
3398         $table->width = '80%';
3399     }
3401     if (empty($table->tablealign)) {
3402         $table->tablealign = 'center';
3403     }
3405     if (empty($table->cellpadding)) {
3406         $table->cellpadding = '5';
3407     }
3409     if (empty($table->cellspacing)) {
3410         $table->cellspacing = '1';
3411     }
3413     if (empty($table->class)) {
3414         $table->class = 'generaltable';
3415     }
3417     if (empty($table->fontsize)) {
3418         $fontsize = '';
3419     } else {
3420         $fontsize = '<font size="'. $table->fontsize .'">';
3421     }
3423     $output =  '<table width="'. $table->width .'" align="'. $table->tablealign .'" ';
3424     $output .= ' cellpadding="'. $table->cellpadding .'" cellspacing="'. $table->cellspacing .'" class="'. $table->class .'">'."\n";
3426     if (!empty($table->head)) {
3427         $output .= '<tr valign="top">';
3428         foreach ($table->head as $key => $heading) {
3429             if (!isset($size[$key])) {
3430                 $size[$key] = '';
3431             }
3432             if (!isset($align[$key])) {
3433                 $align[$key] = '';
3434             }
3435             $output .= '<th valign="top" '. $align[$key].$size[$key] .' nowrap="nowrap" class="'. $table->class .'header">'.$fontsize.$heading.'</th>';
3436         }
3437         $output .= '</tr>'."\n";
3438     }
3440     foreach ($table->data as $row) {
3441         $output .= '<tr valign="top">';
3442         foreach ($row as $key => $item) {
3443             if (!isset($size[$key])) {
3444                 $size[$key] = '';
3445             }
3446             if (!isset($align[$key])) {
3447                 $align[$key] = '';
3448             }
3449             if (!isset($wrap[$key])) {
3450                 $wrap[$key] = '';
3451             }
3452             $output .= '<td '. $align[$key].$size[$key].$wrap[$key] .' class="'. $table->class .'cell">'. $fontsize . $item .'</td>';
3453         }
3454         $output .= '</tr>'."\n";
3455     }
3456     $output .= '</table>'."\n";
3458     return $output;
3461 function print_recent_activity_note($time, $user, $isteacher, $text, $link, $return=false) {
3462     static $strftimerecent;
3464     $output = '';
3466     if (empty($strftimerecent)) {
3467         $strftimerecent = get_string('strftimerecent');
3468     }
3470     $date = userdate($time, $strftimerecent);
3471     $name = fullname($user, $isteacher);
3473     $output .= '<div class="head">';
3474     $output .= '<div class="date">'.$date.'</div> '.
3475          '<div class="name">'.fullname($user, $isteacher).'</div>';
3476     $output .= '</div>';
3477     $output .= '<div class="info"><a href="'.$link.'">'.format_string($text,true).'</a></div>';
3479     if ($return) {
3480         return $output;
3481     } else {
3482         echo $output;
3483     }
3487 /**
3488  * Prints a basic textarea field.
3489  *
3490  * @uses $CFG
3491  * @param boolean $usehtmleditor ?
3492  * @param int $rows ?
3493  * @param int $cols ?
3494  * @param null $width <b>Legacy field no longer used!</b>  Set to zero to get control over mincols
3495  * @param null $height <b>Legacy field no longer used!</b>  Set to zero to get control over minrows
3496  * @param string $name ?
3497  * @param string $value ?
3498  * @param int $courseid ?
3499  * @todo Finish documenting this function
3500  */
3501 function print_textarea($usehtmleditor, $rows, $cols, $width, $height, $name, $value='', $courseid=0, $return=false) {
3502 /// $width and height are legacy fields and no longer used as pixels like they used to be.
3503 /// However, you can set them to zero to override the mincols and minrows values below.
3505     global $CFG, $course;
3506     static $scriptcount; // For loading the htmlarea script only once.
3508     $mincols = 65;
3509     $minrows = 10;
3510     $str = '';
3511     
3512     if ( empty($CFG->editorsrc) ) { // for backward compatibility.
3513         if (empty($courseid)) {
3514             if (!empty($course->id)) {  // search for it in global context
3515                 $courseid = $course->id;
3516             }
3517         }
3519         if (empty($scriptcount)) {
3520             $scriptcount = 0;
3521         }
3523         if ($usehtmleditor) {
3525             if (!empty($courseid) and isteacher($courseid)) {
3526                 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3527                 $CFG->wwwroot .'/lib/editor/htmlarea/htmlarea.php?id='. $courseid .'"></script>'."\n" : '';
3528             } else {
3529                 $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3530                 $CFG->wwwroot .'/lib/editor/htmlarea/htmlarea.php"></script>'."\n" : '';
3531             }
3532             $str .= ($scriptcount < 1) ? '<script type="text/javascript" src="'.
3533             $CFG->wwwroot .'/lib/editor/htmlarea/lang/en.php"></script>'."\n" : '';
3534             $scriptcount++;
3536             if ($height) {    // Usually with legacy calls
3537                 if ($rows < $minrows) {
3538                     $rows = $minrows;
3539                 }
3540             }
3541             if ($width) {    // Usually with legacy calls
3542                 if ($cols < $mincols) {
3543                     $cols = $mincols;
3544                 }
3545             }
3546         }
3547     }
3548     $str .= '<textarea id="edit-'. $name .'" name="'. $name .'" rows="'. $rows .'" cols="'. $cols .'">';
3549     if ($usehtmleditor) {
3550         $str .= htmlspecialchars(stripslashes_safe($value)); // needed for editing of cleaned text!
3551     } else {
3552         $str .= s($value);
3553     }
3554     $str .= '</textarea>'."\n";
3555     
3556     if ($return) {
3557         return $str;
3558     }
3559     echo $str;
3562 /**
3563  * Legacy function, provided for backward compatability.
3564  * This method now simply calls {@link use_html_editor()}
3565  *
3566  * @deprecated Use {@link use_html_editor()} instead.
3567  * @param string $name Form element to replace with HTMl editor by name
3568  * @todo Finish documenting this function
3569  */
3570 function print_richedit_javascript($form, $name, $source='no') {
3571     use_html_editor($name);
3574 /**
3575  * Sets up the HTML editor on textareas in the current page.
3576  * If a field name is provided, then it will only be
3577  * applied to that field - otherwise it will be used
3578  * on every textarea in the page.
3579  *
3580  * In most cases no arguments need to be supplied
3581  *
3582  * @param string $name Form element to replace with HTMl editor by name
3583  */
3584 function use_html_editor($name='', $editorhidebuttons='') {
3585     $editor = 'editor_'.md5($name); //name might contain illegal characters
3586     echo '<script language="javascript" type="text/javascript" defer="defer">'."\n";
3587     echo "$editor = new HTMLArea('edit-$name');\n";
3588     echo "var config = $editor.config;\n";
3590     echo print_editor_config($editorhidebuttons);
3592     if (empty($name)) {
3593         echo "\nHTMLArea.replaceAll($editor.config);\n";
3594     } else {
3595         echo "\n$editor.generate();\n";
3596     }
3597     echo '</script>'."\n";
3600 function print_editor_config($editorhidebuttons='', $return=false) {
3601     global $CFG;
3602     
3603     $str = "config.pageStyle = \"body {";
3604     
3605     if (!(empty($CFG->editorbackgroundcolor))) {
3606         $str .= " background-color: $CFG->editorbackgroundcolor;";
3607     }
3609     if (!(empty($CFG->editorfontfamily))) {
3610         $str .= " font-family: $CFG->editorfontfamily;";
3611     }
3613     if (!(empty($CFG->editorfontsize))) {
3614         $str .= " font-size: $CFG->editorfontsize;";
3615     }
3617     $str .= " }\";\n";
3618     $str .= "config.killWordOnPaste = ";
3619     $str .= (empty($CFG->editorkillword)) ? "false":"true";
3620     $str .= ';'."\n";
3621     $str .= 'config.fontname = {'."\n";
3622     
3623     $fontlist = isset($CFG->editorfontlist) ? explode(';', $CFG->editorfontlist) : array();
3624     $i = 1;                     // Counter is used to get rid of the last comma.
3626     foreach ($fontlist as $fontline) {
3627         if (!empty($fontline)) {
3628             if ($i > 1) {
3629                 $str .= ','."\n";
3630             }
3631             list($fontkey, $fontvalue) = split(':', $fontline);
3632             $str .= '"'. $fontkey ."\":\t'". $fontvalue ."'";
3634             $i++;
3635         }
3636     }
3637     $str .= '};';
3639     if (!empty($editorhidebuttons)) {
3640         $str .= "\nconfig.hideSomeButtons(\" ". $editorhidebuttons ." \");\n";
3641     } else if (!empty($CFG->editorhidebuttons)) {
3642         $str .= "\nconfig.hideSomeButtons(\" ". $CFG->editorhidebuttons ." \");\n";
3643     }
3645     if (!empty($CFG->editorspelling) && !empty($CFG->aspellpath)) {
3646         $str .= print_speller_code($usehtmleditor=true, true);
3647     }
3648             
3649     if ($return) {
3650         return $str;
3651     }
3652     echo $str;
3655 /**
3656  * Returns a turn edit on/off button for course in a self contained form.
3657  * Used to be an icon, but it's now a simple form button
3658  *
3659  * @uses $CFG
3660  * @uses $USER
3661  * @param int $courseid The course  to update by id as found in 'course' table
3662  * @return string
3663  */
3664 function update_course_icon($courseid) {
3666     global $CFG, $USER;
3668     if (has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $courseid))) {
3669         if (!empty($USER->editing)) {
3670             $string = get_string('turneditingoff');
3671             $edit = '0';
3672         } else {
3673             $string = get_string('turneditingon');
3674             $edit = '1';
3675         }
3676         return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/course/view.php\">".
3677             "<input type=\"hidden\" name=\"id\" value=\"$courseid\" />".
3678             "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
3679             "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />".
3680             "<input type=\"submit\" value=\"$string\" /></form>";
3681     }
3685 /**
3686  * Returns a turn edit on/off button for course in a self contained form.
3687  * Used to be an icon, but it's now a simple form button
3688  *
3689  * @uses $CFG
3690  * @uses $USER
3691  * @param int $courseid The course  to update by id as found in 'course' table
3692  * @return string
3693  */
3694 function update_mymoodle_icon() {
3696     global $CFG, $USER;
3698     if (!empty($USER->editing)) {
3699         $string = get_string('updatemymoodleoff');
3700         $edit = '0';
3701     } else {
3702         $string = get_string('updatemymoodleon');
3703         $edit = '1';
3704     }
3705     return "<form target=\"$CFG->framename\" method=\"get\" action=\"$CFG->wwwroot/my/index.php\">".
3706         "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".