Fixed a missing space which broken XHTML 1.0 strict
[moodle.git] / lib / weblib.php
CommitLineData
abf45bed 1<?php // $Id$
f9903ed0 2
9fa49e22 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///////////////////////////////////////////////////////////////////////////
f9903ed0 25
7cf1c7bd 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$
89dcb99d 37 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
7cf1c7bd 38 * @package moodlecore
39 */
772e78be 40
6aaa17c7 41/// We are going to uses filterlib functions here
42require_once("$CFG->libdir/filterlib.php");
43
0095d5cd 44/// Constants
45
c1d57101 46/// Define text formatting types ... eventually we can add Wiki, BBcode etc
7cf1c7bd 47
48/**
49 * Does all sorts of transformations and filtering
50 */
b0ccd3fb 51define('FORMAT_MOODLE', '0'); // Does all sorts of transformations and filtering
7cf1c7bd 52
53/**
54 * Plain HTML (with some tags stripped)
55 */
b0ccd3fb 56define('FORMAT_HTML', '1'); // Plain HTML (with some tags stripped)
7cf1c7bd 57
58/**
59 * Plain text (even tags are printed in full)
60 */
b0ccd3fb 61define('FORMAT_PLAIN', '2'); // Plain text (even tags are printed in full)
7cf1c7bd 62
63/**
64 * Wiki-formatted text
6a6495ff 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)
7cf1c7bd 67 */
b0ccd3fb 68define('FORMAT_WIKI', '3'); // Wiki-formatted text
7cf1c7bd 69
70/**
71 * Markdown-formatted text http://daringfireball.net/projects/markdown/
72 */
b0ccd3fb 73define('FORMAT_MARKDOWN', '4'); // Markdown-formatted text http://daringfireball.net/projects/markdown/
0095d5cd 74
7d8a3cb0 75/**
76 * TRUSTTEXT marker - if present in text, text cleaning should be bypassed
77 */
78define('TRUSTTEXT', '#####TRUSTTEXT#####');
79
7cf1c7bd 80
81/**
82 * Allowed tags - string of html tags that can be tested against for safe html tags
83 * @global string $ALLOWED_TAGS
84 */
39dda0fc 85$ALLOWED_TAGS =
d046ae55 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>';
87
037dcbb6 88/**
89 * Allowed protocols - array of protocols that are safe to use in links and so on
90 * @global string $ALLOWED_PROTOCOLS
91 */
f941df22 92$ALLOWED_PROTOCOLS = array('http', 'https', 'ftp', 'news', 'mailto', 'rtsp', 'teamspeak', 'gopher', 'mms',
c06c8492 93 'color', 'callto', 'cursor', 'text-align', 'font-size', 'font-weight', 'font-style',
016ffbd7 94 'border', 'margin', 'padding', 'background'); // CSS as well to get through kses
037dcbb6 95
96
0095d5cd 97/// Functions
98
7cf1c7bd 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
d4a42ff4 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.
7cf1c7bd 108 * @return string
109 */
d4a42ff4 110function s($var, $strip=false) {
111
63e554d0 112 if ($var == '0') { // for integer 0, boolean false, string '0'
113 return '0';
3662bce5 114 }
d4a42ff4 115
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 }
f9903ed0 121}
122
7cf1c7bd 123/**
124 * Add quotes to HTML characters
125 *
d48b00b4 126 * Prints $var with HTML characters (like "<", ">", etc.) properly quoted.
7cf1c7bd 127 * This function is very similar to {@link s()}
128 *
129 * @param string $var the string potentially containing HTML characters
d4a42ff4 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.
7cf1c7bd 132 * @return string
133 */
d4a42ff4 134function p($var, $strip=false) {
135 echo s($var, $strip);
f9903ed0 136}
137
7cf1c7bd 138
139/**
140 * Ensure that a variable is set
141 *
772e78be 142 * Return $var if it is defined, otherwise return $default,
7cf1c7bd 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 */
b0ccd3fb 149function nvl(&$var, $default='') {
c23ff4d6 150 global $CFG;
8553b700 151
c23ff4d6 152 if (!empty($CFG->disableglobalshack)) {
153 error( "The nvl() function is deprecated ($var, $default)." );
154 }
8553b700 155 return isset($var) ? $var : $default;
156}
f9903ed0 157
7cf1c7bd 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) {
f9903ed0 167
b9b8ab69 168 if ($commapos = strpos($url, '?')) {
169 return substr($url, 0, $commapos);
170 } else {
171 return $url;
172 }
f9903ed0 173}
174
7cf1c7bd 175/**
176 * Returns the URL of the HTTP_REFERER, less the querystring portion
177 * @return string
178 */
f9903ed0 179function get_referer() {
f9903ed0 180
b0ccd3fb 181 return strip_querystring(nvl($_SERVER['HTTP_REFERER']));
f9903ed0 182}
183
c1d57101 184
7cf1c7bd 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.)
d48b00b4 190 * <b>NOTE:</b> This function returns false if the global variables needed are not set.
191 *
7cf1c7bd 192 * @return string
193 */
194 function me() {
f9903ed0 195
b0ccd3fb 196 if (!empty($_SERVER['REQUEST_URI'])) {
197 return $_SERVER['REQUEST_URI'];
c1d57101 198
b0ccd3fb 199 } else if (!empty($_SERVER['PHP_SELF'])) {
200 if (!empty($_SERVER['QUERY_STRING'])) {
201 return $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
fced815c 202 }
b0ccd3fb 203 return $_SERVER['PHP_SELF'];
c1d57101 204
b0ccd3fb 205 } else if (!empty($_SERVER['SCRIPT_NAME'])) {
206 if (!empty($_SERVER['QUERY_STRING'])) {
207 return $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
fced815c 208 }
b0ccd3fb 209 return $_SERVER['SCRIPT_NAME'];
fced815c 210
b0ccd3fb 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'];
16c4d82a 214 }
b0ccd3fb 215 return $_SERVER['URL'];
16c4d82a 216
b9b8ab69 217 } else {
b0ccd3fb 218 notify('Warning: Could not find any of these web server variables: $REQUEST_URI, $PHP_SELF, $SCRIPT_NAME or $URL');
bcdfe14e 219 return false;
7fbd6b1c 220 }
f9903ed0 221}
222
7cf1c7bd 223/**
d48b00b4 224 * Like {@link me()} but returns a full URL
7cf1c7bd 225 * @see me()
7cf1c7bd 226 * @return string
227 */
f9903ed0 228function qualified_me() {
f9903ed0 229
b6e22603 230 global $CFG;
231
68796e6b 232 if (!empty($CFG->wwwroot)) {
233 $url = parse_url($CFG->wwwroot);
234 }
b6e22603 235
236 if (!empty($url['host'])) {
237 $hostname = $url['host'];
238 } else if (!empty($_SERVER['SERVER_NAME'])) {
b0ccd3fb 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'];
39e018b3 246 } else {
b0ccd3fb 247 notify('Warning: could not find the name of this server!');
bcdfe14e 248 return false;
c1d57101 249 }
dc28eede 250
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 }
258
f77c261e 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 }
f9903ed0 266
39e018b3 267 $url_prefix = $protocol.$hostname;
b9b8ab69 268 return $url_prefix . me();
f9903ed0 269}
270
7cf1c7bd 271/**
272 * Determine if there is data waiting to be processed from a form
273 *
274 * Used on most forms in Moodle to check for data
275 * Returns the data as an object, if it's found.
276 * This object can be used in foreach loops without
277 * casting because it's cast to (array) automatically
772e78be 278 *
9c0f063b 279 * Checks that submitted POST data exists and returns it as object.
d48b00b4 280 *
9c0f063b 281 * @param string $url not used anymore
282 * @return mixed false or object
7cf1c7bd 283 */
b0ccd3fb 284function data_submitted($url='') {
d48b00b4 285
607809b3 286 if (empty($_POST)) {
36b4f985 287 return false;
288 } else {
9c0f063b 289 return (object)$_POST;
36b4f985 290 }
291}
292
7cf1c7bd 293/**
e8d1c9ed 294 * Moodle replacement for php stripslashes() function,
295 * works also for objects and arrays.
7cf1c7bd 296 *
772e78be 297 * The standard php stripslashes() removes ALL backslashes
7cf1c7bd 298 * even from strings - so C:\temp becomes C:temp - this isn't good.
299 * This function should work as a fairly safe replacement
300 * to be called on quoted AND unquoted strings (to be sure)
d48b00b4 301 *
e8d1c9ed 302 * @param mixed something to remove unsafe slashes from
303 * @return mixed
7cf1c7bd 304 */
e8d1c9ed 305function stripslashes_safe($mixed) {
306 // there is no need to remove slashes from int, float and bool types
307 if (empty($mixed)) {
308 //nothing to do...
309 } else if (is_string($mixed)) {
de64b6c6 310 if (ini_get_bool('magic_quotes_sybase')) { //only unescape single quotes
311 $mixed = str_replace("''", "'", $mixed);
312 } else { //the rest, simple and double quotes and backslashes
313 $mixed = str_replace("\\'", "'", $mixed);
314 $mixed = str_replace('\\"', '"', $mixed);
315 $mixed = str_replace('\\\\', '\\', $mixed);
316 }
e8d1c9ed 317 } else if (is_array($mixed)) {
318 foreach ($mixed as $key => $value) {
319 $mixed[$key] = stripslashes_safe($value);
320 }
321 } else if (is_object($mixed)) {
322 $vars = get_object_vars($mixed);
323 foreach ($vars as $key => $value) {
324 $mixed->$key = stripslashes_safe($value);
325 }
326 }
7d8f674d 327
e8d1c9ed 328 return $mixed;
7d8f674d 329}
f9903ed0 330
9b4b78fd 331/**
332 * Recursive implementation of stripslashes()
333 *
334 * This function will allow you to strip the slashes from a variable.
335 * If the variable is an array or object, slashes will be stripped
336 * from the items (or properties) it contains, even if they are arrays
337 * or objects themselves.
338 *
339 * @param mixed the variable to remove slashes from
340 * @return mixed
341 */
342function stripslashes_recursive($var) {
343 if(is_object($var)) {
344 $properties = get_object_vars($var);
345 foreach($properties as $property => $value) {
346 $var->$property = stripslashes_recursive($value);
347 }
348 }
349 else if(is_array($var)) {
350 foreach($var as $property => $value) {
351 $var[$property] = stripslashes_recursive($value);
352 }
353 }
354 else if(is_string($var)) {
355 $var = stripslashes($var);
356 }
357 return $var;
358}
359
7cf1c7bd 360/**
d48b00b4 361 * Given some normal text this function will break up any
362 * long words to a given size by inserting the given character
363 *
6aaa17c7 364 * It's multibyte savvy and doesn't change anything inside html tags.
365 *
7cf1c7bd 366 * @param string $string the string to be modified
89dcb99d 367 * @param int $maxsize maximum length of the string to be returned
7cf1c7bd 368 * @param string $cutchar the string used to represent word breaks
369 * @return string
370 */
4a5644e5 371function break_up_long_words($string, $maxsize=20, $cutchar=' ') {
a2b3f884 372
6aaa17c7 373/// Loading the textlib singleton instance. We are going to need it.
374 $textlib = textlib_get_instance();
8f7dc7f1 375
6aaa17c7 376/// First of all, save all the tags inside the text to skip them
377 $tags = array();
378 filter_save_tags($string,$tags);
5b07d990 379
6aaa17c7 380/// Process the string adding the cut when necessary
4a5644e5 381 $output = '';
810944af 382 $length = $textlib->strlen($string);
4a5644e5 383 $wordlength = 0;
384
385 for ($i=0; $i<$length; $i++) {
810944af 386 $char = $textlib->substr($string, $i, 1);
6aaa17c7 387 if ($char == ' ' or $char == "\t" or $char == "\n" or $char == "\r" or $char == "<" or $char == ">") {
4a5644e5 388 $wordlength = 0;
389 } else {
390 $wordlength++;
391 if ($wordlength > $maxsize) {
392 $output .= $cutchar;
393 $wordlength = 0;
394 }
395 }
396 $output .= $char;
397 }
6aaa17c7 398
399/// Finally load the tags back again
400 if (!empty($tags)) {
401 $output = str_replace(array_keys($tags), $tags, $output);
402 }
403
4a5644e5 404 return $output;
405}
406
7cf1c7bd 407/**
408 * This does a search and replace, ignoring case
409 * This function is only used for versions of PHP older than version 5
410 * which do not have a native version of this function.
411 * Taken from the PHP manual, by bradhuizenga @ softhome.net
d48b00b4 412 *
7cf1c7bd 413 * @param string $find the string to search for
414 * @param string $replace the string to replace $find with
415 * @param string $string the string to search through
416 * return string
417 */
ce57cc79 418if (!function_exists('str_ireplace')) { /// Only exists in PHP 5
7ec2fc00 419 function str_ireplace($find, $replace, $string) {
7ec2fc00 420
421 if (!is_array($find)) {
422 $find = array($find);
423 }
424
425 if(!is_array($replace)) {
426 if (!is_array($find)) {
427 $replace = array($replace);
428 } else {
429 // this will duplicate the string into an array the size of $find
430 $c = count($find);
431 $rString = $replace;
432 unset($replace);
433 for ($i = 0; $i < $c; $i++) {
434 $replace[$i] = $rString;
435 }
436 }
437 }
438
439 foreach ($find as $fKey => $fItem) {
440 $between = explode(strtolower($fItem),strtolower($string));
441 $pos = 0;
442 foreach($between as $bKey => $bItem) {
443 $between[$bKey] = substr($string,$pos,strlen($bItem));
444 $pos += strlen($bItem) + strlen($fItem);
445 }
446 $string = implode($replace[$fKey],$between);
72e4eac6 447 }
7ec2fc00 448 return ($string);
3fe3851d 449 }
3fe3851d 450}
451
7cf1c7bd 452/**
453 * Locate the position of a string in another string
454 *
455 * This function is only used for versions of PHP older than version 5
456 * which do not have a native version of this function.
457 * Taken from the PHP manual, by dmarsh @ spscc.ctc.edu
d48b00b4 458 *
7cf1c7bd 459 * @param string $haystack The string to be searched
460 * @param string $needle The string to search for
89dcb99d 461 * @param int $offset The position in $haystack where the search should begin.
7cf1c7bd 462 */
ce57cc79 463if (!function_exists('stripos')) { /// Only exists in PHP 5
464 function stripos($haystack, $needle, $offset=0) {
d48b00b4 465
ce57cc79 466 return strpos(strtoupper($haystack), strtoupper($needle), $offset);
467 }
468}
469
7cf1c7bd 470/**
471 * Load a template from file
472 *
473 * Returns a (big) string containing the contents of a template file with all
474 * the variables interpolated. all the variables must be in the $var[] array or
475 * object (whatever you decide to use).
476 *
477 * <b>WARNING: do not use this on big files!!</b>
d48b00b4 478 *
7cf1c7bd 479 * @param string $filename Location on the server's filesystem where template can be found.
480 * @param mixed $var Passed in by reference. An array or object which will be loaded with data from the template file.
481 */
f9903ed0 482function read_template($filename, &$var) {
f9903ed0 483
b0ccd3fb 484 $temp = str_replace("\\", "\\\\", implode(file($filename), ''));
b9b8ab69 485 $temp = str_replace('"', '\"', $temp);
486 eval("\$template = \"$temp\";");
487 return $template;
f9903ed0 488}
489
7cf1c7bd 490/**
491 * Set a variable's value depending on whether or not it already has a value.
492 *
772e78be 493 * If variable is set, set it to the set_value otherwise set it to the
7cf1c7bd 494 * unset_value. used to handle checkboxes when you are expecting them from
495 * a form
d48b00b4 496 *
7cf1c7bd 497 * @param mixed $var Passed in by reference. The variable to check.
498 * @param mixed $set_value The value to set $var to if $var already has a value.
499 * @param mixed $unset_value The value to set $var to if $var does not already have a value.
500 */
f9903ed0 501function checked(&$var, $set_value = 1, $unset_value = 0) {
f9903ed0 502
b9b8ab69 503 if (empty($var)) {
504 $var = $unset_value;
505 } else {
506 $var = $set_value;
507 }
f9903ed0 508}
509
7cf1c7bd 510/**
511 * Prints the word "checked" if a variable is true, otherwise prints nothing,
d48b00b4 512 * used for printing the word "checked" in a checkbox form element.
513 *
7cf1c7bd 514 * @param boolean $var Variable to be checked for true value
515 * @param string $true_value Value to be printed if $var is true
516 * @param string $false_value Value to be printed if $var is false
517 */
b0ccd3fb 518function frmchecked(&$var, $true_value = 'checked', $false_value = '') {
f9903ed0 519
b9b8ab69 520 if ($var) {
521 echo $true_value;
522 } else {
523 echo $false_value;
524 }
f9903ed0 525}
526
7cf1c7bd 527/**
528 * This function will create a HTML link that will work on both
529 * Javascript and non-javascript browsers.
530 * Relies on the Javascript function openpopup in javascript.php
d48b00b4 531 *
7cf1c7bd 532 * $url must be relative to home page eg /mod/survey/stuff.php
533 * @param string $url Web link relative to home page
534 * @param string $name Name to be assigned to the popup window
535 * @param string $linkname Text to be displayed as web link
89dcb99d 536 * @param int $height Height to assign to popup window
537 * @param int $width Height to assign to popup window
7cf1c7bd 538 * @param string $title Text to be displayed as popup page title
539 * @param string $options List of additional options for popup window
540 * @todo Add code examples and list of some options that might be used.
541 * @param boolean $return Should the link to the popup window be returned as a string (true) or printed immediately (false)?
542 * @return string
543 * @uses $CFG
544 */
b0ccd3fb 545function link_to_popup_window ($url, $name='popup', $linkname='click here',
da4124be 546 $height=400, $width=500, $title='Popup window',
547 $options='none', $return=false) {
f9903ed0 548
ff80e012 549 global $CFG;
550
b0ccd3fb 551 if ($options == 'none') {
552 $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
b48f834c 553 }
86aa7ccf 554 $fullscreen = 0;
f9903ed0 555
c80b7585 556 if (!(strpos($url,$CFG->wwwroot) === false)) { // some log url entries contain _SERVER[HTTP_REFERRER] in which case wwwroot is already there.
d2fa35a8 557 $url = substr($url, strlen($CFG->wwwroot));
c80b7585 558 }
559
b0ccd3fb 560 $link = '<a target="'. $name .'" title="'. $title .'" href="'. $CFG->wwwroot . $url .'" '.
9de39eae 561 "onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\">$linkname</a>";
1f2eec7b 562 if ($return) {
563 return $link;
564 } else {
565 echo $link;
566 }
f9903ed0 567}
568
7cf1c7bd 569/**
570 * This function will print a button submit form element
571 * that will work on both Javascript and non-javascript browsers.
572 * Relies on the Javascript function openpopup in javascript.php
d48b00b4 573 *
7cf1c7bd 574 * $url must be relative to home page eg /mod/survey/stuff.php
575 * @param string $url Web link relative to home page
576 * @param string $name Name to be assigned to the popup window
577 * @param string $linkname Text to be displayed as web link
89dcb99d 578 * @param int $height Height to assign to popup window
579 * @param int $width Height to assign to popup window
7cf1c7bd 580 * @param string $title Text to be displayed as popup page title
581 * @param string $options List of additional options for popup window
dc28eede 582 * @param string $return If true, return as a string, otherwise print
7cf1c7bd 583 * @return string
584 * @uses $CFG
585 */
b0ccd3fb 586function button_to_popup_window ($url, $name='popup', $linkname='click here',
572fe9ab 587 $height=400, $width=500, $title='Popup window', $options='none', $return=false,
0f7d4e5e 588 $id='', $class='') {
52f1b496 589
590 global $CFG;
591
b0ccd3fb 592 if ($options == 'none') {
593 $options = 'menubar=0,location=0,scrollbars,resizable,width='. $width .',height='. $height;
52f1b496 594 }
0f7d4e5e 595
596 if ($id) {
597 $id = ' id="'.$id.'" ';
598 }
599 if ($class) {
600 $class = ' class="'.$class.'" ';
601 }
52f1b496 602 $fullscreen = 0;
603
0f7d4e5e 604 $button = '<input type="button" name="'.$name.'" title="'. $title .'" value="'. $linkname .' ..." '.$id.$class.
dc28eede 605 "onclick=\"return openpopup('$url', '$name', '$options', $fullscreen);\" />\n";
606 if ($return) {
607 return $button;
608 } else {
609 echo $button;
610 }
52f1b496 611}
612
613
7cf1c7bd 614/**
615 * Prints a simple button to close a window
616 */
eaeaf964 617function close_window_button($name='closewindow', $return=false) {
618 $output = '';
619
620 $output .= '<center>' . "\n";
621 $output .= '<script type="text/javascript">' . "\n";
622 $output .= '<!--' . "\n";
623 $output .= "document.write('<form>');\n";
624 $output .= "document.write('<input type=\"button\" onclick=\"self.close();\" value=\"".get_string("closewindow")."\" />');\n";
625 $output .= "document.write('<\/form>');\n";
626 $output .= '-->' . "\n";
627 $output .= '</script>' . "\n";
628 $output .= '<noscript>' . "\n";
629 $output .= get_string($name);
630 $output .= '</noscript>' . "\n";
631 $output .= '</center>' . "\n";
632
633 if ($return) {
634 return $output;
635 } else {
636 echo $output;
637 }
f9903ed0 638}
639
08396bb2 640/*
641 * Try and close the current window immediately using Javascript
642 */
643function close_window($delay=0) {
dfa924cc 644?>
645<script type="text/javascript">
646<!--
647 function close_this_window() {
648 self.close();
649 }
650 setTimeout("close_this_window()", <?php echo $delay * 1000 ?>);
651-->
652</script>
653<noscript><center>
654<?php print_string('pleaseclose') ?>
655</center></noscript>
656<?php
657 die;
08396bb2 658}
659
660
d48b00b4 661/**
662 * Given an array of value, creates a popup menu to be part of a form
663 * $options["value"]["label"]
664 *
665 * @param type description
666 * @todo Finish documenting this function
667 */
c06c8492 668function choose_from_menu ($options, $name, $selected='', $nothing='choose', $script='',
25e5dbf9 669 $nothingvalue='0', $return=false, $disabled=false, $tabindex=0) {
ab9f24ad 670
b0ccd3fb 671 if ($nothing == 'choose') {
672 $nothing = get_string('choose') .'...';
618b22c5 673 }
674
48e535bc 675 $attributes = ($script) ? 'onchange="'. $script .'"' : '';
676 if ($disabled) {
677 $attributes .= ' disabled="disabled"';
f9903ed0 678 }
9c9f7d77 679
25e5dbf9 680 if ($tabindex) {
681 $attributes .= ' tabindex="'.$tabindex.'"';
682 }
683
7003072a 684 $id = str_replace('[]', '', $name); // name may end in [], which would make an invalid id. e.g. numeric question type editing form.
685 $output = '<select id="menu'.$id.'" name="'. $name .'" '. $attributes .'>' . "\n";
bda8d43a 686 if ($nothing) {
b0ccd3fb 687 $output .= ' <option value="'. $nothingvalue .'"'. "\n";
cec0a0fc 688 if ($nothingvalue === $selected) {
b0ccd3fb 689 $output .= ' selected="selected"';
bda8d43a 690 }
b0ccd3fb 691 $output .= '>'. $nothing .'</option>' . "\n";
873960de 692 }
607809b3 693 if (!empty($options)) {
694 foreach ($options as $value => $label) {
b0ccd3fb 695 $output .= ' <option value="'. $value .'"';
bd905b45 696 if ((string)$value == (string)$selected) {
b0ccd3fb 697 $output .= ' selected="selected"';
607809b3 698 }
b0ccd3fb 699 if ($label === '') {
700 $output .= '>'. $value .'</option>' . "\n";
a20c1090 701 } else {
b0ccd3fb 702 $output .= '>'. $label .'</option>' . "\n";
607809b3 703 }
f9903ed0 704 }
705 }
b0ccd3fb 706 $output .= '</select>' . "\n";
08056730 707
708 if ($return) {
709 return $output;
710 } else {
711 echo $output;
712 }
ab9f24ad 713}
f9903ed0 714
73d4db84 715/**
716 * Choose value 0 or 1 from a menu with options 'No' and 'Yes'.
717 * Other options like choose_from_menu.
718 */
719function choose_from_menu_yesno($name, $selected, $script = '',
720 $return = false, $disabled = false, $tabindex = 0) {
721 return choose_from_menu(array(get_string('no'), get_string('yes')), $name,
722 $selected, '', $script, '0', $return, $disabled, $tabindex);
723}
724
14040797 725/**
726 * Just like choose_from_menu, but takes a nested array (2 levels) and makes a dropdown menu
727 * including option headings with the first level.
728 */
729function choose_from_menu_nested($options,$name,$selected='',$nothing='choose',$script = '',
730 $nothingvalue=0,$return=false,$disabled=false,$tabindex=0) {
731
732 if ($nothing == 'choose') {
733 $nothing = get_string('choose') .'...';
734 }
735
736 $attributes = ($script) ? 'onchange="'. $script .'"' : '';
737 if ($disabled) {
738 $attributes .= ' disabled="disabled"';
739 }
740
741 if ($tabindex) {
742 $attributes .= ' tabindex="'.$tabindex.'"';
743 }
744
745 $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
746 if ($nothing) {
747 $output .= ' <option value="'. $nothingvalue .'"'. "\n";
748 if ($nothingvalue === $selected) {
749 $output .= ' selected="selected"';
750 }
751 $output .= '>'. $nothing .'</option>' . "\n";
752 }
753 if (!empty($options)) {
754 foreach ($options as $section => $values) {
755 $output .= ' <optgroup label="'.$section.'">'."\n";
756 foreach ($values as $value => $label) {
757 $output .= ' <option value="'. $value .'"';
f332bd02 758 if ((string)$value == (string)$selected) {
14040797 759 $output .= ' selected="selected"';
760 }
761 if ($label === '') {
762 $output .= '>'. $value .'</option>' . "\n";
763 } else {
764 $output .= '>'. $label .'</option>' . "\n";
765 }
766 }
767 $output .= ' </optgroup>'."\n";
768 }
769 }
770 $output .= '</select>' . "\n";
771
772 if ($return) {
773 return $output;
774 } else {
775 echo $output;
776 }
777}
778
779
531a3cb2 780/**
781 * Given an array of values, creates a group of radio buttons to be part of a form
c06c8492 782 *
531a3cb2 783 * @param array $options An array of value-label pairs for the radio group (values as keys)
784 * @param string $name Name of the radiogroup (unique in the form)
785 * @param string $checked The value that is already checked
786 */
eaeaf964 787function choose_from_radio ($options, $name, $checked='', $return=false) {
531a3cb2 788
20cbef63 789 static $idcounter = 0;
790
531a3cb2 791 if (!$name) {
792 $name = 'unnamed';
793 }
794
795 $output = '<span class="radiogroup '.$name."\">\n";
796
797 if (!empty($options)) {
798 $currentradio = 0;
799 foreach ($options as $value => $label) {
20cbef63 800 $htmlid = 'auto-rb'.sprintf('%04d', ++$idcounter);
801 $output .= ' <span class="radioelement '.$name.' rb'.$currentradio."\">";
802 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="radio" value="'.$value.'"';
531a3cb2 803 if ($value == $checked) {
804 $output .= ' checked="checked"';
805 }
806 if ($label === '') {
20cbef63 807 $output .= ' /> <label for="'.$htmlid.'">'. $value .'</label></span>' . "\n";
531a3cb2 808 } else {
20cbef63 809 $output .= ' /> <label for="'.$htmlid.'">'. $label .'</label></span>' . "\n";
531a3cb2 810 }
811 $currentradio = ($currentradio + 1) % 2;
812 }
813 }
814
815 $output .= '</span>' . "\n";
816
eaeaf964 817 if ($return) {
818 return $output;
819 } else {
820 echo $output;
821 }
531a3cb2 822}
823
2481037f 824/** Display an standard html checkbox with an optional label
825 *
826 * @param string $name The name of the checkbox
827 * @param string $value The valus that the checkbox will pass when checked
828 * @param boolean $checked The flag to tell the checkbox initial state
829 * @param string $label The label to be showed near the checkbox
830 * @param string $alt The info to be inserted in the alt tag
831 */
d0d272e7 832function print_checkbox ($name, $value, $checked = true, $label = '', $alt = '', $script='',$return=false) {
2481037f 833
20cbef63 834 static $idcounter = 0;
835
2481037f 836 if (!$name) {
837 $name = 'unnamed';
838 }
839
840 if (!$alt) {
841 $alt = 'checkbox';
842 }
843
844 if ($checked) {
845 $strchecked = ' checked="checked"';
f89f924e 846 } else {
847 $strchecked = '';
2481037f 848 }
849
20cbef63 850 $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
2481037f 851 $output = '<span class="checkbox '.$name."\">";
d0d272e7 852 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="checkbox" value="'.$value.'" alt="'.$alt.'"'.$strchecked.' '.((!empty($script)) ? ' onClick="'.$script.'" ' : '').' />';
20cbef63 853 if(!empty($label)) {
854 $output .= ' <label for="'.$htmlid.'">'.$label.'</label>';
855 }
2481037f 856 $output .= '</span>'."\n";
857
d0d272e7 858 if (empty($return)) {
859 echo $output;
860 } else {
861 return $output;
862 }
2481037f 863
864}
865
d0d272e7 866/** Display an standard html text field with an optional label
867 *
868 * @param string $name The name of the text field
869 * @param string $value The value of the text field
870 * @param string $label The label to be showed near the text field
871 * @param string $alt The info to be inserted in the alt tag
872 */
eaeaf964 873function print_textfield ($name, $value, $alt = '',$size=50,$maxlength=0, $return=false) {
d0d272e7 874
875 static $idcounter = 0;
876
877 if (empty($name)) {
878 $name = 'unnamed';
879 }
880
881 if (empty($alt)) {
882 $alt = 'textfield';
883 }
884
885 if (!empty($maxlength)) {
886 $maxlength = ' maxlength="'.$maxlength.'" ';
887 }
888
ce432524 889 $htmlid = 'auto-tf'.sprintf('%04d', ++$idcounter);
d0d272e7 890 $output = '<span class="textfield '.$name."\">";
891 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="text" value="'.$value.'" size="'.$size.'" '.$maxlength.' alt="'.$alt.'" />';
c06c8492 892
d0d272e7 893 $output .= '</span>'."\n";
894
895 if (empty($return)) {
896 echo $output;
897 } else {
898 return $output;
899 }
900
901}
902
903
d48b00b4 904/**
905 * Implements a complete little popup form
906 *
89dcb99d 907 * @uses $CFG
908 * @param string $common The URL up to the point of the variable that changes
909 * @param array $options Alist of value-label pairs for the popup list
910 * @param string $formname Name must be unique on the page
911 * @param string $selected The option that is already selected
912 * @param string $nothing The label for the "no choice" option
913 * @param string $help The name of a help page if help is required
914 * @param string $helptext The name of the label for the help button
915 * @param boolean $return Indicates whether the function should return the text
916 * as a string or echo it directly to the page being rendered
772e78be 917 * @param string $targetwindow The name of the target page to open the linked page in.
89dcb99d 918 * @return string If $return is true then the entire form is returned as a string.
919 * @todo Finish documenting this function<br>
89dcb99d 920 */
921function popup_form($common, $options, $formname, $selected='', $nothing='choose', $help='', $helptext='', $return=false, $targetwindow='self') {
922
772e78be 923 global $CFG;
b0542a1e 924 static $go, $choose; /// Locally cached, in case there's lots on a page
87180677 925
6f9f3b69 926 if (empty($options)) {
927 return '';
928 }
0d0baabf 929
b0542a1e 930 if (!isset($go)) {
931 $go = get_string('go');
037dcbb6 932 }
933
b0ccd3fb 934 if ($nothing == 'choose') {
037dcbb6 935 if (!isset($choose)) {
936 $choose = get_string('choose');
937 }
938 $nothing = $choose.'...';
618b22c5 939 }
940
037dcbb6 941 $startoutput = '<form action="'.$CFG->wwwroot.'/course/jumpto.php"'.
942 ' method="get"'.
943 ' target="'.$CFG->framename.'"'.
944 ' name="'.$formname.'"'.
c4d951e1 945 ' class="popupform">';
037dcbb6 946
947 $output = '<select name="jump" onchange="'.$targetwindow.'.location=document.'.$formname.
16570a27 948 '.jump.options[document.'.$formname.'.jump.selectedIndex].value;">'."\n";
f9903ed0 949
b0ccd3fb 950 if ($nothing != '') {
dfec7b01 951 $output .= " <option value=\"javascript:void(0)\">$nothing</option>\n";
f9903ed0 952 }
953
72b4e283 954 $inoptgroup = false;
f9903ed0 955 foreach ($options as $value => $label) {
772e78be 956
2a003a90 957 if (substr($label,0,2) == '--') { /// we are starting a new optgroup
772e78be 958
2a003a90 959 /// Check to see if we already have a valid open optgroup
960 /// XHTML demands that there be at least 1 option within an optgroup
961 if ($inoptgroup and (count($optgr) > 1) ) {
962 $output .= implode('', $optgr);
72b4e283 963 $output .= ' </optgroup>';
72b4e283 964 }
2a003a90 965
966 unset($optgr);
967 $optgr = array();
968
969 $optgr[] = ' <optgroup label="'. substr($label,2) .'">'; // Plain labels
772e78be 970
2a003a90 971 $inoptgroup = true; /// everything following will be in an optgroup
3326450b 972 continue;
772e78be 973
d897cae4 974 } else {
fd78420b 975 if (!empty($CFG->usesid) && !isset($_COOKIE[session_name()]))
976 {
977 $url=sid_process_url( $common . $value );
978 } else
979 {
980 $url=$common . $value;
981 }
982 $optstr = ' <option value="' . $url . '"';
772e78be 983
d897cae4 984 if ($value == $selected) {
2a003a90 985 $optstr .= ' selected="selected"';
986 }
772e78be 987
2a003a90 988 if ($label) {
989 $optstr .= '>'. $label .'</option>' . "\n";
990 } else {
991 $optstr .= '>'. $value .'</option>' . "\n";
992 }
772e78be 993
2a003a90 994 if ($inoptgroup) {
995 $optgr[] = $optstr;
996 } else {
997 $output .= $optstr;
d897cae4 998 }
f9903ed0 999 }
772e78be 1000
f9903ed0 1001 }
2a003a90 1002
1003 /// catch the final group if not closed
1004 if ($inoptgroup and count($optgr) > 1) {
1005 $output .= implode('', $optgr);
72b4e283 1006 $output .= ' </optgroup>';
1007 }
2a003a90 1008
b0ccd3fb 1009 $output .= '</select>';
3435f39b 1010 $output .= '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
929089e7 1011 $output .= '<div id="noscript'.$formname.'" style="display: inline;">';
1012 $output .= '<input type="submit" value="'.$go.'" /></div>';
037dcbb6 1013 $output .= '<script type="text/javascript">'.
1014 "\n<!--\n".
1015 'document.getElementById("noscript'.$formname.'").style.display = "none";'.
1016 "\n-->\n".'</script>';
b0ccd3fb 1017 $output .= '</form>' . "\n";
d897cae4 1018
1f2eec7b 1019 if ($help) {
1020 $button = helpbutton($help, $helptext, 'moodle', true, false, '', true);
1021 } else {
1022 $button = '';
1023 }
1024
d897cae4 1025 if ($return) {
1f2eec7b 1026 return $startoutput.$button.$output;
d897cae4 1027 } else {
1f2eec7b 1028 echo $startoutput.$button.$output;
d897cae4 1029 }
f9903ed0 1030}
1031
1032
d48b00b4 1033/**
1034 * Prints some red text
1035 *
1036 * @param string $error The text to be displayed in red
1037 */
f9903ed0 1038function formerr($error) {
d48b00b4 1039
f9903ed0 1040 if (!empty($error)) {
b0ccd3fb 1041 echo '<font color="#ff0000">'. $error .'</font>';
f9903ed0 1042 }
1043}
1044
d48b00b4 1045/**
1046 * Validates an email to make sure it makes sense.
1047 *
1048 * @param string $address The email address to validate.
1049 * @return boolean
1050 */
89dcb99d 1051function validate_email($address) {
d48b00b4 1052
78fbaeae 1053 return (ereg('^[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+'.
1054 '(\.[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+)*'.
f9903ed0 1055 '@'.
1056 '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
1057 '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$',
1058 $address));
1059}
1060
690f358b 1061/**
1062 * Extracts file argument either from file parameter or PATH_INFO
1063 *
1064 * @param string $scriptname name of the calling script
1065 * @return string file path (only safe characters)
1066 */
1067function get_file_argument($scriptname) {
1068 global $_SERVER;
1069
1070 $relativepath = FALSE;
1071
1072 // first try normal parameter (compatible method == no relative links!)
1073 $relativepath = optional_param('file', FALSE, PARAM_PATH);
48283ff6 1074 if ($relativepath === '/testslasharguments') {
9bbb40d6 1075 echo 'test -1 : Incorrect use - try "file.php/testslasharguments" instead'; //indicate fopen/fread works for health center
48283ff6 1076 die;
1077 }
690f358b 1078
1079 // then try extract file from PATH_INFO (slasharguments method)
1080 if (!$relativepath and !empty($_SERVER['PATH_INFO'])) {
1081 $path_info = $_SERVER['PATH_INFO'];
1082 // check that PATH_INFO works == must not contain the script name
1083 if (!strpos($path_info, $scriptname)) {
1084 $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
48283ff6 1085 if ($relativepath === '/testslasharguments') {
9bbb40d6 1086 echo 'test 1 : Slasharguments test passed. Server confguration is compatible with file.php/1/pic.jpg slashargument setting.'; //indicate ok for health center
690f358b 1087 die;
1088 }
1089 }
1090 }
1091
1092 // now if both fail try the old way
1093 // (for compatibility with misconfigured or older buggy php implementations)
1094 if (!$relativepath) {
1095 $arr = explode($scriptname, me());
1096 if (!empty($arr[1])) {
1097 $path_info = strip_querystring($arr[1]);
1098 $relativepath = clean_param(rawurldecode($path_info), PARAM_PATH);
48283ff6 1099 if ($relativepath === '/testslasharguments') {
9bbb40d6 1100 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
690f358b 1101 die;
1102 }
1103 }
1104 }
1105
1106 return $relativepath;
1107}
1108
d48b00b4 1109/**
1110 * Check for bad characters ?
1111 *
1112 * @param string $string ?
89dcb99d 1113 * @param int $allowdots ?
1114 * @todo Finish documenting this function - more detail needed in description as well as details on arguments
d48b00b4 1115 */
80035a89 1116function detect_munged_arguments($string, $allowdots=1) {
1117 if (substr_count($string, '..') > $allowdots) { // Sometimes we allow dots in references
6c8e8b5e 1118 return true;
1119 }
393c9b4f 1120 if (ereg('[\|\`]', $string)) { // check for other bad characters
6c8e8b5e 1121 return true;
1122 }
f038d9a3 1123 if (empty($string) or $string == '/') {
1124 return true;
1125 }
1126
6c8e8b5e 1127 return false;
1128}
1129
d48b00b4 1130/**
1131 * Searches the current environment variables for some slash arguments
1132 *
1133 * @param string $file ?
1134 * @todo Finish documenting this function
1135 */
b0ccd3fb 1136function get_slash_arguments($file='file.php') {
f9903ed0 1137
eaa50dbc 1138 if (!$string = me()) {
f9903ed0 1139 return false;
1140 }
eaa50dbc 1141
6ed3da1d 1142 $pathinfo = explode($file, $string);
ab9f24ad 1143
bcdfe14e 1144 if (!empty($pathinfo[1])) {
9b74055f 1145 return addslashes($pathinfo[1]);
6ed3da1d 1146 } else {
1147 return false;
1148 }
1149}
1150
d48b00b4 1151/**
1152 * Extracts arguments from "/foo/bar/something"
1153 * eg http://mysite.com/script.php/foo/bar/something
1154 *
89dcb99d 1155 * @param string $string ?
1156 * @param int $i ?
1157 * @return array|string
d48b00b4 1158 * @todo Finish documenting this function
1159 */
6ed3da1d 1160function parse_slash_arguments($string, $i=0) {
f9903ed0 1161
6c8e8b5e 1162 if (detect_munged_arguments($string)) {
780db230 1163 return false;
1164 }
b0ccd3fb 1165 $args = explode('/', $string);
f9903ed0 1166
1167 if ($i) { // return just the required argument
1168 return $args[$i];
1169
1170 } else { // return the whole array
1171 array_shift($args); // get rid of the empty first one
1172 return $args;
1173 }
1174}
1175
d48b00b4 1176/**
89dcb99d 1177 * Just returns an array of text formats suitable for a popup menu
d48b00b4 1178 *
89dcb99d 1179 * @uses FORMAT_MOODLE
1180 * @uses FORMAT_HTML
1181 * @uses FORMAT_PLAIN
89dcb99d 1182 * @uses FORMAT_MARKDOWN
1183 * @return array
d48b00b4 1184 */
0095d5cd 1185function format_text_menu() {
d48b00b4 1186
b0ccd3fb 1187 return array (FORMAT_MOODLE => get_string('formattext'),
1188 FORMAT_HTML => get_string('formathtml'),
1189 FORMAT_PLAIN => get_string('formatplain'),
b0ccd3fb 1190 FORMAT_MARKDOWN => get_string('formatmarkdown'));
0095d5cd 1191}
1192
d48b00b4 1193/**
1194 * Given text in a variety of format codings, this function returns
772e78be 1195 * the text as safe HTML.
d48b00b4 1196 *
1197 * @uses $CFG
89dcb99d 1198 * @uses FORMAT_MOODLE
1199 * @uses FORMAT_HTML
1200 * @uses FORMAT_PLAIN
1201 * @uses FORMAT_WIKI
1202 * @uses FORMAT_MARKDOWN
1203 * @param string $text The text to be formatted. This is raw text originally from user input.
772e78be 1204 * @param int $format Identifier of the text format to be used
89dcb99d 1205 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1206 * @param array $options ?
1207 * @param int $courseid ?
1208 * @return string
d48b00b4 1209 * @todo Finish documenting this function
1210 */
7d8a3cb0 1211function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) {
0095d5cd 1212
ab9f24ad 1213 global $CFG, $course;
c4ae4fa1 1214
d53ca6ad 1215 if ($text === '') {
1216 return ''; // no need to do any filters and cleaning
1217 }
1218
7d8a3cb0 1219 if (!isset($options->trusttext)) {
1220 $options->trusttext = false;
1221 }
1222
e7a47153 1223 if (!isset($options->noclean)) {
1224 $options->noclean=false;
1225 }
a17c57b5 1226 if (!isset($options->nocache)) {
1227 $options->nocache=false;
1228 }
e7a47153 1229 if (!isset($options->smiley)) {
1230 $options->smiley=true;
1231 }
1232 if (!isset($options->filter)) {
1233 $options->filter=true;
1234 }
1235 if (!isset($options->para)) {
1236 $options->para=true;
1237 }
1238 if (!isset($options->newlines)) {
1239 $options->newlines=true;
f0aa2fed 1240 }
1241
c4ae4fa1 1242 if (empty($courseid)) {
8eaa4c61 1243 if (!empty($course->id)) { // An ugly hack for better compatibility
c4ae4fa1 1244 $courseid = $course->id;
1245 }
1246 }
a751a4e5 1247
a17c57b5 1248 if (!empty($CFG->cachetext) and empty($options->nocache)) {
e7a47153 1249 $time = time() - $CFG->cachetext;
90bbe689 1250 $md5key = md5($text.'-'.(int)$courseid.'-'.current_language().'-'.(int)$format.(int)$options->trusttext.(int)$options->noclean.(int)$options->smiley.(int)$options->filter.(int)$options->para.(int)$options->newlines);
a9743837 1251 if ($oldcacheitem = get_record_sql('SELECT * FROM '.$CFG->prefix.'cache_text WHERE md5key = \''.$md5key.'\'', true)) {
1252 if ($oldcacheitem->timemodified >= $time) {
1253 return $oldcacheitem->formattedtext;
1254 }
e7a47153 1255 }
1256 }
1257
7d8a3cb0 1258 // trusttext overrides the noclean option!
1259 if ($options->trusttext) {
1260 if (trusttext_present($text)) {
1261 $text = trusttext_strip($text);
1262 if (!empty($CFG->enabletrusttext)) {
1263 $options->noclean = true;
1264 } else {
1265 $options->noclean = false;
1266 }
1267 } else {
1268 $options->noclean = false;
1269 }
d53ca6ad 1270 } else if (!debugging('', DEBUG_DEVELOPER)) {
1271 // strip any forgotten trusttext in non-developer mode
1272 // do not forget to disable text cache when debugging trusttext!!
1273 $text = trusttext_strip($text);
7d8a3cb0 1274 }
1275
8eaa4c61 1276 $CFG->currenttextiscacheable = true; // Default status - can be changed by any filter
c06c8492 1277
0095d5cd 1278 switch ($format) {
73f8658c 1279 case FORMAT_HTML:
7d8a3cb0 1280 if ($options->smiley) {
e7a47153 1281 replace_smilies($text);
1282 }
7d8a3cb0 1283 if (!$options->noclean) {
9d40806d 1284 $text = clean_text($text, $format);
1285 }
7d8a3cb0 1286 if ($options->filter) {
e7a47153 1287 $text = filter_text($text, $courseid);
1288 }
73f8658c 1289 break;
1290
6901fa79 1291 case FORMAT_PLAIN:
c06c8492 1292 $text = s($text);
ab892a4f 1293 $text = rebuildnolinktag($text);
b0ccd3fb 1294 $text = str_replace(' ', '&nbsp; ', $text);
6901fa79 1295 $text = nl2br($text);
6901fa79 1296 break;
1297
d342c763 1298 case FORMAT_WIKI:
6a6495ff 1299 // this format is deprecated
572fe9ab 1300 $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle. You should not be seeing
1301 this message as all texts should have been converted to Markdown format instead.
ce50cc70 1302 Please post a bug report to http://moodle.org/bugs with information about where you
e7a47153 1303 saw this message.</p>'.s($text);
d342c763 1304 break;
1305
e7cdcd18 1306 case FORMAT_MARKDOWN:
1307 $text = markdown_to_html($text);
7d8a3cb0 1308 if ($options->smiley) {
e7a47153 1309 replace_smilies($text);
1310 }
7d8a3cb0 1311 if (!$options->noclean) {
9d40806d 1312 $text = clean_text($text, $format);
1313 }
c06c8492 1314
7d8a3cb0 1315 if ($options->filter) {
e7a47153 1316 $text = filter_text($text, $courseid);
1317 }
e7cdcd18 1318 break;
1319
73f8658c 1320 default: // FORMAT_MOODLE or anything else
b7a3d3b2 1321 $text = text_to_html($text, $options->smiley, $options->para, $options->newlines);
7d8a3cb0 1322 if (!$options->noclean) {
9d40806d 1323 $text = clean_text($text, $format);
1324 }
c06c8492 1325
7d8a3cb0 1326 if ($options->filter) {
e7a47153 1327 $text = filter_text($text, $courseid);
1328 }
0095d5cd 1329 break;
0095d5cd 1330 }
f0aa2fed 1331
f47f4659 1332 if (empty($options->nocache) and !empty($CFG->cachetext) and $CFG->currenttextiscacheable) {
1333 $newcacheitem = new object();
a9743837 1334 $newcacheitem->md5key = $md5key;
1335 $newcacheitem->formattedtext = addslashes($text);
1336 $newcacheitem->timemodified = time();
1337 if ($oldcacheitem) { // See bug 4677 for discussion
1338 $newcacheitem->id = $oldcacheitem->id;
1339 @update_record('cache_text', $newcacheitem); // Update existing record in the cache table
1340 // It's unlikely that the cron cache cleaner could have
1341 // deleted this entry in the meantime, as it allows
1342 // some extra time to cover these cases.
1343 } else {
1344 @insert_record('cache_text', $newcacheitem); // Insert a new record in the cache table
1345 // Again, it's possible that another user has caused this
1346 // record to be created already in the time that it took
1347 // to traverse this function. That's OK too, as the
1348 // call above handles duplicate entries, and eventually
1349 // the cron cleaner will delete them.
1350 }
f0aa2fed 1351 }
1352
1353 return $text;
0095d5cd 1354}
1355
4a28b5ca 1356/** Converts the text format from the value to the 'internal'
1357 * name or vice versa. $key can either be the value or the name
1358 * and you get the other back.
c06c8492 1359 *
4a28b5ca 1360 * @param mixed int 0-4 or string one of 'moodle','html','plain','markdown'
1361 * @return mixed as above but the other way around!
1362 */
1363function text_format_name( $key ) {
1364 $lookup = array();
1365 $lookup[FORMAT_MOODLE] = 'moodle';
1366 $lookup[FORMAT_HTML] = 'html';
1367 $lookup[FORMAT_PLAIN] = 'plain';
1368 $lookup[FORMAT_MARKDOWN] = 'markdown';
1369 $value = "error";
1370 if (!is_numeric($key)) {
1371 $key = strtolower( $key );
1372 $value = array_search( $key, $lookup );
1373 }
1374 else {
1375 if (isset( $lookup[$key] )) {
1376 $value = $lookup[ $key ];
1377 }
1378 }
1379 return $value;
1380}
1381
7b2c5e72 1382/** Given a simple string, this function returns the string
1383 * processed by enabled filters if $CFG->filterall is enabled
1384 *
3e6691ee 1385 * @param string $string The string to be filtered.
1386 * @param boolean $striplinks To strip any link in the result text.
1387 * @param int $courseid Current course as filters can, potentially, use it
7b2c5e72 1388 * @return string
1389 */
3e6691ee 1390function format_string ($string, $striplinks = false, $courseid=NULL ) {
7b2c5e72 1391
65b0c132 1392 global $CFG, $COURSE;
7b2c5e72 1393
2a3affe9 1394 //We'll use a in-memory cache here to speed up repeated strings
1395 static $strcache;
1396
1397 //Calculate md5
1c58f440 1398 $md5 = md5($string.'<+>'.$striplinks);
2a3affe9 1399
1400 //Fetch from cache if possible
1401 if(isset($strcache[$md5])) {
1402 return $strcache[$md5];
1403 }
1404
7b2c5e72 1405 if (empty($courseid)) {
65b0c132 1406 if (!empty($COURSE->id)) { // An ugly hack for better compatibility
1407 $courseid = $COURSE->id; // (copied from format_text)
7b2c5e72 1408 }
1409 }
1410
45daee10 1411 if (!empty($CFG->filterall)) {
7b2c5e72 1412 $string = filter_text($string, $courseid);
1413 }
1414
3e6691ee 1415 if ($striplinks) { //strip links in string
1416 $string = preg_replace('/(<a[^>]+?>)(.+?)(<\/a>)/is','$2',$string);
1417 }
1418
2a3affe9 1419 //Store to cache
1420 $strcache[$md5] = $string;
1421
7b2c5e72 1422 return $string;
1423}
1424
d48b00b4 1425/**
1426 * Given text in a variety of format codings, this function returns
1427 * the text as plain text suitable for plain email.
d48b00b4 1428 *
89dcb99d 1429 * @uses FORMAT_MOODLE
1430 * @uses FORMAT_HTML
1431 * @uses FORMAT_PLAIN
1432 * @uses FORMAT_WIKI
1433 * @uses FORMAT_MARKDOWN
1434 * @param string $text The text to be formatted. This is raw text originally from user input.
772e78be 1435 * @param int $format Identifier of the text format to be used
89dcb99d 1436 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1437 * @return string
d48b00b4 1438 */
d342c763 1439function format_text_email($text, $format) {
d342c763 1440
1441 switch ($format) {
1442
1443 case FORMAT_PLAIN:
1444 return $text;
1445 break;
1446
1447 case FORMAT_WIKI:
1448 $text = wiki_to_html($text);
5b472756 1449 /// This expression turns links into something nice in a text format. (Russell Jungwirth)
1450 /// From: http://php.net/manual/en/function.eregi-replace.php and simplified
76add072 1451 $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
7c55a29b 1452 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
d342c763 1453 break;
1454
6ff45b59 1455 case FORMAT_HTML:
1456 return html_to_text($text);
1457 break;
1458
e7cdcd18 1459 case FORMAT_MOODLE:
1460 case FORMAT_MARKDOWN:
67ccec43 1461 default:
76add072 1462 $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
7c55a29b 1463 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
d342c763 1464 break;
1465 }
1466}
0095d5cd 1467
d48b00b4 1468/**
1469 * Given some text in HTML format, this function will pass it
1470 * through any filters that have been defined in $CFG->textfilterx
1471 * The variable defines a filepath to a file containing the
1472 * filter function. The file must contain a variable called
1473 * $textfilter_function which contains the name of the function
1474 * with $courseid and $text parameters
1475 *
89dcb99d 1476 * @param string $text The text to be passed through format filters
1477 * @param int $courseid ?
d48b00b4 1478 * @return string
1479 * @todo Finish documenting this function
1480 */
c4ae4fa1 1481function filter_text($text, $courseid=NULL) {
c4ae4fa1 1482 global $CFG;
e67b9e31 1483
01af49bb 1484 require_once($CFG->libdir.'/filterlib.php');
d523d2ea 1485 if (!empty($CFG->textfilters)) {
1486 $textfilters = explode(',', $CFG->textfilters);
1487 foreach ($textfilters as $textfilter) {
b0ccd3fb 1488 if (is_readable($CFG->dirroot .'/'. $textfilter .'/filter.php')) {
1489 include_once($CFG->dirroot .'/'. $textfilter .'/filter.php');
df1c4611 1490 $functionname = basename($textfilter).'_filter';
1491 if (function_exists($functionname)) {
1492 $text = $functionname($courseid, $text);
1493 }
d523d2ea 1494 }
e67b9e31 1495 }
1496 }
d523d2ea 1497
c7444a36 1498 /// <nolink> tags removed for XHTML compatibility
1499 $text = str_replace('<nolink>', '', $text);
1500 $text = str_replace('</nolink>', '', $text);
1501
e67b9e31 1502 return $text;
1503}
1504
7d8a3cb0 1505/**
1506 * Is the text marked as trusted?
1507 *
1508 * @param string $text text to be searched for TRUSTTEXT marker
1509 * @return boolean
1510 */
1511function trusttext_present($text) {
1512 if (strpos($text, TRUSTTEXT) !== FALSE) {
1513 return true;
1514 } else {
1515 return false;
1516 }
1517}
1518
1519/**
1520 * This funtion MUST be called before the cleaning or any other
1521 * function that modifies the data! We do not know the origin of trusttext
1522 * in database, if it gets there in tweaked form we must not convert it
1523 * to supported form!!!
1524 *
1525 * Please be carefull not to use stripslashes on data from database
1526 * or twice stripslashes when processing data recieved from user.
1527 *
1528 * @param string $text text that may contain TRUSTTEXT marker
1529 * @return text without any TRUSTTEXT marker
1530 */
1531function trusttext_strip($text) {
1532 global $CFG;
1533
1534 while (true) { //removing nested TRUSTTEXT
1535 $orig = $text;
1536 $text = str_replace(TRUSTTEXT, '', $text);
1537 if (strcmp($orig, $text) === 0) {
1538 return $text;
1539 }
1540 }
1541}
1542
1543/**
1544 * Mark text as trusted, such text may contain any HTML tags because the
1545 * normal text cleaning will be bypassed.
1546 * Please make sure that the text comes from trusted user before storing
1547 * it into database!
1548 */
1549function trusttext_mark($text) {
1550 global $CFG;
1551 if (!empty($CFG->enabletrusttext) and (strpos($text, TRUSTTEXT) === FALSE)) {
1552 return TRUSTTEXT.$text;
1553 } else {
1554 return $text;
1555 }
1556}
1557function trusttext_after_edit(&$text, $context) {
1558 if (has_capability('moodle/site:trustcontent', $context)) {
daed1a2f 1559 $text = trusttext_strip($text);
7d8a3cb0 1560 $text = trusttext_mark($text);
1561 } else {
1562 $text = trusttext_strip($text);
1563 }
1564}
1565
1566function trusttext_prepare_edit(&$text, &$format, $usehtmleditor, $context) {
1567 global $CFG;
1568
1569 $options = new object();
1570 $options->smiley = false;
1571 $options->filter = false;
1572 if (!empty($CFG->enabletrusttext)
1573 and has_capability('moodle/site:trustcontent', $context)
1574 and trusttext_present($text)) {
1575 $options->noclean = true;
1576 } else {
1577 $options->noclean = false;
1578 }
1579 $text = trusttext_strip($text);
1580 if ($usehtmleditor) {
1581 $text = format_text($text, $format, $options);
1582 $format = FORMAT_HTML;
1583 } else if (!$options->noclean){
1584 $text = clean_text($text, $format);
1585 }
1586}
1587
d48b00b4 1588/**
1589 * Given raw text (eg typed in by a user), this function cleans it up
1590 * and removes any nasty tags that could mess up Moodle pages.
1591 *
89dcb99d 1592 * @uses FORMAT_MOODLE
1593 * @uses FORMAT_PLAIN
1594 * @uses ALLOWED_TAGS
1595 * @param string $text The text to be cleaned
772e78be 1596 * @param int $format Identifier of the text format to be used
89dcb99d 1597 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
1598 * @return string The cleaned up text
d48b00b4 1599 */
3da47524 1600function clean_text($text, $format=FORMAT_MOODLE) {
b7a3cf49 1601
fc120758 1602 global $ALLOWED_TAGS;
3fe3851d 1603
ab9f24ad 1604 switch ($format) {
e7cdcd18 1605 case FORMAT_PLAIN:
1606 return $text;
1607
1608 default:
1609
29939bea 1610 /// Fix non standard entity notations
1611 $text = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $text);
1612 $text = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $text);
1613
09cbeb40 1614 /// Remove tags that are not allowed
3fe3851d 1615 $text = strip_tags($text, $ALLOWED_TAGS);
e7cdcd18 1616
7789ffbf 1617 /// Clean up embedded scripts and , using kses
1618 $text = cleanAttributes($text);
1619
5b472756 1620 /// Remove script events
ab9f24ad 1621 $text = eregi_replace("([^a-z])language([[:space:]]*)=", "\\1Xlanguage=", $text);
1622 $text = eregi_replace("([^a-z])on([a-z]+)([[:space:]]*)=", "\\1Xon\\2=", $text);
6901fa79 1623
6901fa79 1624 return $text;
0095d5cd 1625 }
b7a3cf49 1626}
f9903ed0 1627
d48b00b4 1628/**
89dcb99d 1629 * This function takes a string and examines it for HTML tags.
d48b00b4 1630 * If tags are detected it passes the string to a helper function {@link cleanAttributes2()}
1631 * which checks for attributes and filters them for malicious content
1632 * 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie
1633 *
1634 * @param string $str The string to be examined for html tags
1635 * @return string
1636 */
3bd7ffec 1637function cleanAttributes($str){
4e8f2e6b 1638 $result = preg_replace_callback(
1639 '%(<[^>]*(>|$)|>)%m', #search for html tags
1640 "cleanAttributes2",
3bd7ffec 1641 $str
67ccec43 1642 );
3bd7ffec 1643 return $result;
67ccec43 1644}
1645
d48b00b4 1646/**
1647 * This function takes a string with an html tag and strips out any unallowed
1648 * protocols e.g. javascript:
1649 * It calls ancillary functions in kses which are prefixed by kses
1650* 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie
1651 *
4e8f2e6b 1652 * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st
1653 * element the html to be cleared
d48b00b4 1654 * @return string
1655 */
4e8f2e6b 1656function cleanAttributes2($htmlArray){
3bd7ffec 1657
037dcbb6 1658 global $CFG, $ALLOWED_PROTOCOLS;
b0ccd3fb 1659 require_once($CFG->libdir .'/kses.php');
3bd7ffec 1660
4e8f2e6b 1661 $htmlTag = $htmlArray[1];
037dcbb6 1662 if (substr($htmlTag, 0, 1) != '<') {
3bd7ffec 1663 return '&gt;'; //a single character ">" detected
1664 }
037dcbb6 1665 if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) {
67ccec43 1666 return ''; // It's seriously malformed
1667 }
3bd7ffec 1668 $slash = trim($matches[1]); //trailing xhtml slash
67ccec43 1669 $elem = $matches[2]; //the element name
3bd7ffec 1670 $attrlist = $matches[3]; // the list of attributes as a string
1671
037dcbb6 1672 $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
3bd7ffec 1673
67ccec43 1674 $attStr = '';
037dcbb6 1675 foreach ($attrArray as $arreach) {
29939bea 1676 $arreach['name'] = strtolower($arreach['name']);
1677 if ($arreach['name'] == 'style') {
1678 $value = $arreach['value'];
1679 while (true) {
1680 $prevvalue = $value;
1681 $value = kses_no_null($value);
1682 $value = preg_replace("/\/\*.*\*\//Us", '', $value);
1683 $value = kses_decode_entities($value);
1684 $value = preg_replace('/(&#[0-9]+)(;?)/', "\\1;", $value);
1685 $value = preg_replace('/(&#x[0-9a-fA-F]+)(;?)/', "\\1;", $value);
1686 if ($value === $prevvalue) {
1687 $arreach['value'] = $value;
1688 break;
1689 }
1690 }
1691 $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']);
1692 $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']);
1693 }
049bd7db 1694 $attStr .= ' '.$arreach['name'].'="'.$arreach['value'].'"';
3bd7ffec 1695 }
713126cd 1696
3bd7ffec 1697 $xhtml_slash = '';
037dcbb6 1698 if (preg_match('%/\s*$%', $attrlist)) {
67ccec43 1699 $xhtml_slash = ' /';
3bd7ffec 1700 }
b0ccd3fb 1701 return '<'. $slash . $elem . $attStr . $xhtml_slash .'>';
3bd7ffec 1702}
1703
d48b00b4 1704/**
1705 * Replaces all known smileys in the text with image equivalents
1706 *
1707 * @uses $CFG
1708 * @param string $text Passed by reference. The string to search for smily strings.
1709 * @return string
1710 */
5f350e8f 1711function replace_smilies(&$text) {
772e78be 1712///
2ea9027b 1713 global $CFG;
c1d57101 1714
5b472756 1715/// this builds the mapping array only once
617778f2 1716 static $runonce = false;
69081931 1717 static $e = array();
1718 static $img = array();
617778f2 1719 static $emoticons = array(
fbfc2675 1720 ':-)' => 'smiley',
1721 ':)' => 'smiley',
1722 ':-D' => 'biggrin',
1723 ';-)' => 'wink',
1724 ':-/' => 'mixed',
1725 'V-.' => 'thoughtful',
1726 ':-P' => 'tongueout',
1727 'B-)' => 'cool',
1728 '^-)' => 'approve',
1729 '8-)' => 'wideeyes',
1730 ':o)' => 'clown',
1731 ':-(' => 'sad',
1732 ':(' => 'sad',
1733 '8-.' => 'shy',
1734 ':-I' => 'blush',
1735 ':-X' => 'kiss',
1736 '8-o' => 'surprise',
1737 'P-|' => 'blackeye',
1738 '8-[' => 'angry',
1739 'xx-P' => 'dead',
1740 '|-.' => 'sleepy',
1741 '}-]' => 'evil',
35cf8b79 1742 '(h)' => 'heart',
1743 '(heart)' => 'heart',
1744 '(y)' => 'yes',
1745 '(n)' => 'no',
2eb662d2 1746 '(martin)' => 'martin',
e84ea106 1747 '( )' => 'egg'
2ea9027b 1748 );
1749
fbfc2675 1750 if ($runonce == false) { /// After the first time this is not run again
617778f2 1751 foreach ($emoticons as $emoticon => $image){
fbfc2675 1752 $alttext = get_string($image, 'pix');
1753
69081931 1754 $e[] = $emoticon;
d48b00b4 1755 $img[] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $CFG->pixpath .'/s/'. $image .'.gif" />';
617778f2 1756 }
1757 $runonce = true;
c0f728ba 1758 }
b7a3cf49 1759
8dcd43f3 1760 // Exclude from transformations all the code inside <script> tags
1761 // Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
1762 // Based on code from glossary fiter by Williams Castillo.
1763 // - Eloy
1764
1765 // Detect all the <script> zones to take out
1766 $excludes = array();
1767 preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
1768
1769 // Take out all the <script> zones from text
1770 foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
1771 $excludes['<+'.$key.'+>'] = $value;
1772 }
1773 if ($excludes) {
1774 $text = str_replace($excludes,array_keys($excludes),$text);
1775 }
1776
fbfc2675 1777/// this is the meat of the code - this is run every time
5f350e8f 1778 $text = str_replace($e, $img, $text);
8dcd43f3 1779
1780 // Recover all the <script> zones to text
1781 if ($excludes) {
1782 $text = str_replace(array_keys($excludes),$excludes,$text);
1783 }
1a072208 1784}
0095d5cd 1785
89dcb99d 1786/**
1787 * Given plain text, makes it into HTML as nicely as possible.
1788 * May contain HTML tags already
1789 *
1790 * @uses $CFG
1791 * @param string $text The string to convert.
1792 * @param boolean $smiley Convert any smiley characters to smiley images?
1793 * @param boolean $para If true then the returned string will be wrapped in paragraph tags
1794 * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
1795 * @return string
1796 */
1797
b7a3d3b2 1798function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
772e78be 1799///
f9903ed0 1800
27326a3e 1801 global $CFG;
1802
c1d57101 1803/// Remove any whitespace that may be between HTML tags
7b3be1b1 1804 $text = eregi_replace(">([[:space:]]+)<", "><", $text);
1805
c1d57101 1806/// Remove any returns that precede or follow HTML tags
0eae8049 1807 $text = eregi_replace("([\n\r])<", " <", $text);
1808 $text = eregi_replace(">([\n\r])", "> ", $text);
7b3be1b1 1809
5f350e8f 1810 convert_urls_into_links($text);
f9903ed0 1811
c1d57101 1812/// Make returns into HTML newlines.
b7a3d3b2 1813 if ($newlines) {
1814 $text = nl2br($text);
1815 }
f9903ed0 1816
c1d57101 1817/// Turn smileys into images.
d69cb7f4 1818 if ($smiley) {
5f350e8f 1819 replace_smilies($text);
d69cb7f4 1820 }
f9903ed0 1821
c1d57101 1822/// Wrap the whole thing in a paragraph tag if required
909f539d 1823 if ($para) {
b0ccd3fb 1824 return '<p>'.$text.'</p>';
909f539d 1825 } else {
1826 return $text;
1827 }
f9903ed0 1828}
1829
d48b00b4 1830/**
1831 * Given Markdown formatted text, make it into XHTML using external function
1832 *
89dcb99d 1833 * @uses $CFG
1834 * @param string $text The markdown formatted text to be converted.
1835 * @return string Converted text
d48b00b4 1836 */
e7cdcd18 1837function markdown_to_html($text) {
e7cdcd18 1838 global $CFG;
1839
b0ccd3fb 1840 require_once($CFG->libdir .'/markdown.php');
e7cdcd18 1841
1842 return Markdown($text);
1843}
1844
d48b00b4 1845/**
89dcb99d 1846 * Given HTML text, make it into plain text using external function
d48b00b4 1847 *
1848 * @uses $CFG
1849 * @param string $html The text to be converted.
1850 * @return string
1851 */
6ff45b59 1852function html_to_text($html) {
89dcb99d 1853
428aaa29 1854 global $CFG;
6ff45b59 1855
b0ccd3fb 1856 require_once($CFG->libdir .'/html2text.php');
6ff45b59 1857
1858 return html2text($html);
1859}
1860
d48b00b4 1861/**
1862 * Given some text this function converts any URLs it finds into HTML links
1863 *
1864 * @param string $text Passed in by reference. The string to be searched for urls.
1865 */
5f350e8f 1866function convert_urls_into_links(&$text) {
5f350e8f 1867/// Make lone URLs into links. eg http://moodle.com/
3405b212 1868 $text = eregi_replace("([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])",
bc01a2b8 1869 "\\1<a href=\"\\2://\\3\\4\" target=\"_blank\">\\2://\\3\\4</a>", $text);
5f350e8f 1870
1871/// eg www.moodle.com
ab9f24ad 1872 $text = eregi_replace("([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])",
bc01a2b8 1873 "\\1<a href=\"http://www.\\2\\3\" target=\"_blank\">www.\\2\\3</a>", $text);
5f350e8f 1874}
1875
d48b00b4 1876/**
1877 * This function will highlight search words in a given string
1878 * It cares about HTML and will not ruin links. It's best to use
1879 * this function after performing any conversions to HTML.
1880 * Function found here: http://forums.devshed.com/t67822/scdaa2d1c3d4bacb4671d075ad41f0854.html
1881 *
89dcb99d 1882 * @param string $needle The string to search for
1883 * @param string $haystack The string to search for $needle in
1884 * @param int $case ?
1885 * @return string
d48b00b4 1886 * @todo Finish documenting this function
1887 */
ab9f24ad 1888function highlight($needle, $haystack, $case=0,
b0ccd3fb 1889 $left_string='<span class="highlight">', $right_string='</span>') {
69d51d3a 1890 if (empty($needle)) {
1891 return $haystack;
1892 }
1893
5eecb8cb 1894 //$list_of_words = eregi_replace("[^-a-zA-Z0-9&.']", " ", $needle); // bug 3101
1895 $list_of_words = $needle;
b0ccd3fb 1896 $list_array = explode(' ', $list_of_words);
88438a58 1897 for ($i=0; $i<sizeof($list_array); $i++) {
1898 if (strlen($list_array[$i]) == 1) {
b0ccd3fb 1899 $list_array[$i] = '';
88438a58 1900 }
1901 }
b0ccd3fb 1902 $list_of_words = implode(' ', $list_array);
88438a58 1903 $list_of_words_cp = $list_of_words;
1904 $final = array();
1905 preg_match_all('/<(.+?)>/is',$haystack,$list_of_words);
1906
1907 foreach (array_unique($list_of_words[0]) as $key=>$value) {
1908 $final['<|'.$key.'|>'] = $value;
1909 }
1910
1911 $haystack = str_replace($final,array_keys($final),$haystack);
b0ccd3fb 1912 $list_of_words_cp = eregi_replace(' +', '|', $list_of_words_cp);
88438a58 1913
b0ccd3fb 1914 if ($list_of_words_cp{0}=='|') {
1915 $list_of_words_cp{0} = '';
88438a58 1916 }
b0ccd3fb 1917 if ($list_of_words_cp{strlen($list_of_words_cp)-1}=='|') {
1918 $list_of_words_cp{strlen($list_of_words_cp)-1}='';
88438a58 1919 }
88438a58 1920
9ccdcd97 1921 $list_of_words_cp = trim($list_of_words_cp);
1922
1923 if ($list_of_words_cp) {
1924
1925 $list_of_words_cp = "(". $list_of_words_cp .")";
1926
1927 if (!$case){
1928 $haystack = eregi_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1929 } else {
1930 $haystack = ereg_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
1931 }
88438a58 1932 }
1933 $haystack = str_replace(array_keys($final),$final,$haystack);
1934
f60e7cfe 1935 return $haystack;
88438a58 1936}
1937
d48b00b4 1938/**
1939 * This function will highlight instances of $needle in $haystack
1940 * It's faster that the above function and doesn't care about
1941 * HTML or anything.
1942 *
1943 * @param string $needle The string to search for
1944 * @param string $haystack The string to search for $needle in
1945 * @return string
1946 */
88438a58 1947function highlightfast($needle, $haystack) {
5af78ed2 1948
57f1b914 1949 $parts = explode(moodle_strtolower($needle), moodle_strtolower($haystack));
5af78ed2 1950
1951 $pos = 0;
1952
1953 foreach ($parts as $key => $part) {
1954 $parts[$key] = substr($haystack, $pos, strlen($part));
1955 $pos += strlen($part);
1956
b0ccd3fb 1957 $parts[$key] .= '<span class="highlight">'.substr($haystack, $pos, strlen($needle)).'</span>';
5af78ed2 1958 $pos += strlen($needle);
ab9f24ad 1959 }
5af78ed2 1960
1961 return (join('', $parts));
1962}
1963
f9903ed0 1964
9fa49e22 1965/// STANDARD WEB PAGE PARTS ///////////////////////////////////////////////////
1966
d48b00b4 1967/**
1968 * Print a standard header
1969 *
89dcb99d 1970 * @uses $USER
1971 * @uses $CFG
89dcb99d 1972 * @uses $SESSION
1973 * @param string $title Appears at the top of the window
1974 * @param string $heading Appears at the top of the page
1975 * @param string $navigation Premade navigation string (for use as breadcrumbs links)
1976 * @param string $focus Indicates form element to get cursor focus on load eg inputform.password
1977 * @param string $meta Meta tags to be added to the header
1978 * @param boolean $cache Should this page be cacheable?
1979 * @param string $button HTML code for a button (usually for module editing)
1980 * @param string $menu HTML code for a popup menu
1981 * @param boolean $usexml use XML for this page
1982 * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
36b6bcec 1983 * @param bool $return If true, return the visible elements of the header instead of echoing them.
d48b00b4 1984 */
36b6bcec 1985function print_header ($title='', $heading='', $navigation='', $focus='',
1986 $meta='', $cache=true, $button='&nbsp;', $menu='',
1987 $usexml=false, $bodytags='', $return=false) {
63f3cbbd 1988
be933850 1989 global $USER, $CFG, $THEME, $SESSION, $ME, $SITE, $HTTPSPAGEREQUIRED;
c06c8492 1990
32613b50 1991/// This makes sure that the header is never repeated twice on a page
f231e867 1992 if (defined('HEADER_PRINTED')) {
ea82d6b6 1993 debugging('print_header() was called more than once - this should not happen. Please check the code for this page closely. Note: error() and redirect() are now safe to call after print_header().');
32613b50 1994 return;
32613b50 1995 }
f231e867 1996 define('HEADER_PRINTED', 'true');
1997
be933850 1998
1999 global $course, $COURSE;
80acd6be 2000 if (!empty($COURSE->lang)) {
2001 $CFG->courselang = $COURSE->lang;
be933850 2002 moodle_setlocale();
2003 } else if (!empty($course->lang)) { // ugly backwards compatibility hack
2004 $CFG->courselang = $course->lang;
2005 moodle_setlocale();
b3153e4b 2006 }
80acd6be 2007 if (!empty($COURSE->theme)) {
32e2b302 2008 if (!empty($CFG->allowcoursethemes)) {
80acd6be 2009 $CFG->coursetheme = $COURSE->theme;
32e2b302 2010 theme_setup();
2011 }
be933850 2012 } else if (!empty($course->theme)) { // ugly backwards compatibility hack
2013 if (!empty($CFG->allowcoursethemes)) {
2014 $CFG->coursetheme = $course->theme;
2015 theme_setup();
2016 }
32e2b302 2017 }
b3153e4b 2018
f78b1dad 2019/// We have to change some URLs in styles if we are in a $HTTPSPAGEREQUIRED page
2020 if (!empty($HTTPSPAGEREQUIRED)) {
2c3432e6 2021 $CFG->themewww = str_replace('http:', 'https:', $CFG->themewww);
2022 $CFG->pixpath = str_replace('http:', 'https:', $CFG->pixpath);
2023 $CFG->modpixpath = str_replace('http:', 'https:', $CFG->modpixpath);
f78b1dad 2024 foreach ($CFG->stylesheets as $key => $stylesheet) {
2c3432e6 2025 $CFG->stylesheets[$key] = str_replace('http:', 'https:', $stylesheet);
f78b1dad 2026 }
2027 }
0d741155 2028
c3f55692 2029/// Add the required stylesheets
d74d4f20 2030 $stylesheetshtml = '';
2031 foreach ($CFG->stylesheets as $stylesheet) {
2032 $stylesheetshtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
c3f55692 2033 }
d74d4f20 2034 $meta = $stylesheetshtml.$meta;
e89fb61e 2035
9fa49e22 2036
b0ccd3fb 2037 if ($navigation == 'home') {
9fa49e22 2038 $home = true;
b0ccd3fb 2039 $navigation = '';
9d378732 2040 } else {
2041 $home = false;
9fa49e22 2042 }
2043
0d741155 2044/// This is another ugly hack to make navigation elements available to print_footer later
2045 $THEME->title = $title;
2046 $THEME->heading = $heading;
2047 $THEME->navigation = $navigation;
2048 $THEME->button = $button;
2049 $THEME->menu = $menu;
2507b2f5 2050 $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
0d741155 2051
b0ccd3fb 2052 if ($button == '') {
2053 $button = '&nbsp;';
9fa49e22 2054 }
2055
2056 if (!$menu and $navigation) {
8a33e371 2057 if (empty($CFG->loginhttps)) {
2058 $wwwroot = $CFG->wwwroot;
2059 } else {
2c3432e6 2060 $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
8a33e371 2061 }
0e18f827 2062 if (isset($course->id)) {
009f39be 2063 $menu = user_login_string($course);
9fa49e22 2064 } else {
c44d5d42 2065 $menu = user_login_string($SITE);
9fa49e22 2066 }
2067 }
67ccec43 2068
b4bac9b6 2069 if (isset($SESSION->justloggedin)) {
2070 unset($SESSION->justloggedin);
2071 if (!empty($CFG->displayloginfailures)) {
8f8ed475 2072 if (!empty($USER->username) and $USER->username != 'guest') {
b4bac9b6 2073 if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
2074 $menu .= '&nbsp;<font size="1">';
2075 if (empty($count->accounts)) {
2076 $menu .= get_string('failedloginattempts', '', $count);
2077 } else {
2078 $menu .= get_string('failedloginattemptsall', '', $count);
2079 }
51792df0 2080 if (has_capability('moodle/site:viewreports', get_context_instance(CONTEXT_SYSTEM, SITEID))) {
52af9a35 2081 $menu .= ' (<a href="'.$CFG->wwwroot.'/course/report/log/index.php'.
839f2456 2082 '?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
b4bac9b6 2083 }
2084 $menu .= '</font>';
2085 }
2086 }
2087 }
2088 }
9fa49e22 2089
47037513 2090
810944af 2091 $meta = '<meta http-equiv="content-type" content="text/html; charset=utf-8" />'. "\n". $meta ."\n";
03fe48e7 2092 if (!$usexml) {
810944af 2093 @header('Content-type: text/html; charset=utf-8');
03fe48e7 2094 }
9fa49e22 2095
b0ccd3fb 2096 if ( get_string('thisdirection') == 'rtl' ) {
2097 $direction = ' dir="rtl"';
9fa49e22 2098 } else {
b0ccd3fb 2099 $direction = ' dir="ltr"';
9fa49e22 2100 }
6575bfd4 2101 //Accessibility: added the 'lang' attribute to $direction, used in theme <html> tag.
2102 $language = str_replace('_utf8','',$CFG->lang);
2103 $direction .= ' lang="'.$language.'" xml:lang="'.$language.'"';
ab9f24ad 2104
5debee2d 2105 if ($cache) { // Allow caching on "back" (but not on normal clicks)
2106 @header('Cache-Control: private, pre-check=0, post-check=0, max-age=0');
2107 @header('Pragma: no-cache');
772e78be 2108 @header('Expires: ');
5debee2d 2109 } else { // Do everything we can to always prevent clients and proxies caching
03fe48e7 2110 @header('Cache-Control: no-store, no-cache, must-revalidate');
2111 @header('Cache-Control: post-check=0, pre-check=0', false);
2112 @header('Pragma: no-cache');
5debee2d 2113 @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT');
2114 @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
03fe48e7 2115
5debee2d 2116 $meta .= "\n<meta http-equiv=\"pragma\" content=\"no-cache\" />";
2117 $meta .= "\n<meta http-equiv=\"expires\" content=\"0\" />";
66a51452 2118 }
5debee2d 2119 @header('Accept-Ranges: none');
66a51452 2120
2121 if ($usexml) { // Added by Gustav Delius / Mad Alex for MathML output
8f0cd6ef 2122 // Modified by Julian Sedding
66a51452 2123 $currentlanguage = current_language();
8f0cd6ef 2124 $mathplayer = preg_match("/MathPlayer/i", $_SERVER['HTTP_USER_AGENT']);
2125 if(!$mathplayer) {
2126 header('Content-Type: application/xhtml+xml');
2127 }
b0ccd3fb 2128 echo '<?xml version="1.0" ?>'."\n";
66a51452 2129 if (!empty($CFG->xml_stylesheets)) {
b0ccd3fb 2130 $stylesheets = explode(';', $CFG->xml_stylesheets);
66a51452 2131 foreach ($stylesheets as $stylesheet) {
b0ccd3fb 2132 echo '<?xml-stylesheet type="text/xsl" href="'. $CFG->wwwroot .'/'. $stylesheet .'" ?>' . "\n";
66a51452 2133 }
2134 }
b0ccd3fb 2135 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1';
e4576482 2136 if (!empty($CFG->xml_doctype_extra)) {
b0ccd3fb 2137 echo ' plus '. $CFG->xml_doctype_extra;
e4576482 2138 }
b0ccd3fb 2139 echo '//' . strtoupper($currentlanguage) . '" "'. $CFG->xml_dtd .'">'."\n";
8f0cd6ef 2140 $direction = " xmlns=\"http://www.w3.org/1999/xhtml\"
2141 xmlns:math=\"http://www.w3.org/1998/Math/MathML\"
8f0cd6ef 2142 xmlns:xlink=\"http://www.w3.org/1999/xlink\"
2143 $direction";
2144 if($mathplayer) {
2145 $meta .= '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n";
b0ccd3fb 2146 $meta .= '<!--comment required to prevent this becoming an empty tag-->'."\n";
2147 $meta .= '</object>'."\n";
8f0cd6ef 2148 $meta .= '<?import namespace="math" implementation="#mathplayer" ?>' . "\n";
2149 }
9fa49e22 2150 }
2151
7bb6d80f 2152 // Clean up the title
2153
2eea2cce 2154 $title = str_replace('"', '&quot;', $title);
1d9fc417 2155 $title = strip_tags($title);
2eea2cce 2156
7bb6d80f 2157 // Create class and id for this page
2158
68d5f00a 2159 page_id_and_class($pageid, $pageclass);
7bb6d80f 2160
d6c66e12 2161 if (isset($course->id)) {
2162 $pageclass .= ' course-'.$course->id;
c09e00ba 2163 } else {
2164 $pageclass .= ' course-'.SITEID;
d6c66e12 2165 }
2166
e299f862 2167 if (!empty($USER->editing)) {
2168 $pageclass .= ' editing';
2169 }
2170
75c3849a 2171 if (!empty($CFG->blocksdrag)) {
2172 $pageclass .= ' drag';
2173 }
2174
7bb6d80f 2175 $bodytags .= ' class="'.$pageclass.'" id="'.$pageid.'"';
772e78be 2176
36b6bcec 2177 ob_start();
ea35ab87 2178 include($CFG->header);
36b6bcec 2179 $output = ob_get_contents();
2180 ob_end_clean();
e608dddd 2181
cdf39255 2182 if (!empty($CFG->messaging)) {
36b6bcec 2183 $output .= message_popup_window();
2184 }
2185
2186 if ($return) {
2187 return $output;
2188 } else {
2189 echo $output;
cdf39255 2190 }
9fa49e22 2191}
2192
d48b00b4 2193/**
2194 * This version of print_header is simpler because the course name does not have to be
2195 * provided explicitly in the strings. It can be used on the site page as in courses
2196 * Eventually all print_header could be replaced by print_header_simple
2197 *
89dcb99d 2198 * @param string $title Appears at the top of the window
2199 * @param string $heading Appears at the top of the page
2200 * @param string $navigation Premade navigation string (for use as breadcrumbs links)
2201 * @param string $focus Indicates form element to get cursor focus on load eg inputform.password
2202 * @param string $meta Meta tags to be added to the header
2203 * @param boolean $cache Should this page be cacheable?
2204 * @param string $button HTML code for a button (usually for module editing)
2205 * @param string $menu HTML code for a popup menu
2206 * @param boolean $usexml use XML for this page
2207 * @param string $bodytags This text will be included verbatim in the <body> tag (useful for onload() etc)
36b6bcec 2208 * @param bool $return If true, return the visible elements of the header instead of echoing them.
d48b00b4 2209 */
b0ccd3fb 2210function print_header_simple($title='', $heading='', $navigation='', $focus='', $meta='',
36b6bcec 2211 $cache=true, $button='&nbsp;', $menu='', $usexml=false, $bodytags='', $return=false) {
90fcc576 2212
9103fd10 2213 global $COURSE, $CFG;
90fcc576 2214
2215 $shortname ='';
c66c2c02 2216 if ($COURSE->id != SITEID) {
9103fd10 2217 $shortname = '<a href="'.$CFG->wwwroot.'/course/view.php?id='. $COURSE->id .'">'. $COURSE->shortname .'</a> ->';
90fcc576 2218 }
2219
050c78c4 2220 $output = print_header($COURSE->shortname .': '. $title, $COURSE->fullname .' '. $heading, $shortname .' '. $navigation, $focus, $meta,
da4124be 2221 $cache, $button, $menu, $usexml, $bodytags, true);
36b6bcec 2222
2223 if ($return) {
2224 return $output;
2225 } else {
2226 echo $output;
2227 }
90fcc576 2228}
629b5885 2229
2230
d48b00b4 2231/**
2232 * Can provide a course object to make the footer contain a link to
2233 * to the course home page, otherwise the link will go to the site home
2234 *
2235 * @uses $CFG
2236 * @uses $USER
89dcb99d 2237 * @param course $course {@link $COURSE} object containing course information
2238 * @param ? $usercourse ?
d48b00b4 2239 * @todo Finish documenting this function
2240 */
469b6501 2241function print_footer($course=NULL, $usercourse=NULL, $return=false) {
0d741155 2242 global $USER, $CFG, $THEME;
9fa49e22 2243
9fa49e22 2244/// Course links
2245 if ($course) {
a789577b 2246 if (is_string($course) && $course == 'none') { // Don't print any links etc
b3a2b9dd 2247 $homelink = '';
2248 $loggedinas = '';
0d741155 2249 $home = false;
a789577b 2250 } else if (is_string($course) && $course == 'home') { // special case for site home page - please do not remove
b3a2b9dd 2251 $course = get_site();
a77ac3dc 2252 $homelink = '<div class="sitelink">'.
2253 '<a title="moodle '. $CFG->release .' ('. $CFG->version .')" href="http://moodle.org/" target="_blank">'.
2254 '<br /><img width="100" height="30" src="pix/moodlelogo.gif" border="0" alt="moodlelogo" /></a></div>';
0d741155 2255 $home = true;
9fa49e22 2256 } else {
a77ac3dc 2257 $homelink = '<div class="homelink"><a target="'.$CFG->framename.'" href="'.$CFG->wwwroot.
2258 '/course/view.php?id='.$course->id.'">'.$course->shortname.'</a></div>';
0d741155 2259 $home = false;
9fa49e22 2260 }
2261 } else {
b3a2b9dd 2262 $course = get_site(); // Set course as site course by default
a77ac3dc 2263 $homelink = '<div class="homelink"><a target="'.$CFG->framename.'" href="'.$CFG->wwwroot.'/">'.get_string('home').'</a></div>';
0d741155 2264 $home = false;
9fa49e22 2265 }
2266
0d741155 2267/// Set up some other navigation links (passed from print_header by ugly hack)
2507b2f5 2268 $menu = isset($THEME->menu) ? str_replace('navmenu', 'navmenufooter', $THEME->menu) : '';
2269 $title = isset($THEME->title) ? $THEME->title : '';
2270 $button = isset($THEME->button) ? $THEME->button : '';
2271 $heading = isset($THEME->heading) ? $THEME->heading : '';
2272 $navigation = isset($THEME->navigation) ? $THEME->navigation : '';
2273 $navmenulist = isset($THEME->navmenulist) ? $THEME->navmenulist : '';
0d741155 2274
f940ee41 2275
b3a2b9dd 2276/// Set the user link if necessary
2277 if (!$usercourse and is_object($course)) {
1f2eec7b 2278 $usercourse = $course;
2279 }
2280
b3a2b9dd 2281 if (!isset($loggedinas)) {
2282 $loggedinas = user_login_string($usercourse, $USER);
2283 }
2284
0d741155 2285 if ($loggedinas == $menu) {
9e0d1983 2286 $menu = '';
0d741155 2287 }
2288
b8cea9b2 2289/// Provide some performance info if required
c2fd9e95 2290 $performanceinfo = '';
bb4a5741 2291 if (defined('MDL_PERF') || $CFG->perfdebug > 7) {
c2fd9e95 2292 $perf = get_performance_info();
853df85e 2293 if (defined('MDL_PERFTOLOG')) {
2294 error_log("PERF: " . $perf['txt']);
2295 }
ea82d6b6 2296 if (defined('MDL_PERFTOFOOT') || debugging() || $CFG->perfdebug > 7) {
c2fd9e95 2297 $performanceinfo = $perf['html'];
2298 }
572fe9ab 2299 }
b8cea9b2 2300
0d741155 2301
b3a2b9dd 2302/// Include the actual footer file
a282d0ff 2303
469b6501 2304 ob_start();
ea35ab87 2305 include($CFG->footer);
469b6501 2306 $output = ob_get_contents();
2307 ob_end_clean();
a9a9bdba 2308
469b6501 2309 if ($return) {
2310 return $output;
2311 } else {
2312 echo $output;
2313 }
a282d0ff 2314}
2315
c3f55692 2316/**
2317 * Returns the name of the current theme
2318 *
2319 * @uses $CFG
2320 * @param $USER
2321 * @param $SESSION
2322 * @return string
2323 */
2324function current_theme() {
32e2b302 2325 global $CFG, $USER, $SESSION, $course;
c3f55692 2326
417375b7 2327 if (!empty($CFG->pagetheme)) { // Page theme is for special page-only themes set by code
2328 return $CFG->pagetheme;
c3f55692 2329
59552aab 2330 } else if (!empty($CFG->coursetheme) and !empty($CFG->allowcoursethemes)) { // Course themes override others
c3f55692 2331 return $CFG->coursetheme;
2332
2333 } else if (!empty($SESSION->theme)) { // Session theme can override other settings
2334 return $SESSION->theme;
2335
32e2b302 2336 } else if (!empty($USER->theme) and !empty($CFG->allowuserthemes)) { // User theme can override site theme
c3f55692 2337 return $USER->theme;
2338
2339 } else {
2340 return $CFG->theme;
2341 }
2342}
2343
2344
d48b00b4 2345/**
2346 * This function is called by stylesheets to set up the header
2347 * approriately as well as the current path
2348 *
2349 * @uses $CFG
89dcb99d 2350 * @param int $lastmodified ?
2351 * @param int $lifetime ?
2352 * @param string $thename ?
d48b00b4 2353 */
55b734fb 2354function style_sheet_setup($lastmodified=0, $lifetime=300, $themename='', $forceconfig='', $lang='') {
6535be85 2355
9c4e6e21 2356 global $CFG, $THEME;
ab9f24ad 2357
a2e2bf64 2358 // Fix for IE6 caching - we don't want the filemtime('styles.php'), instead use now.
2359 $lastmodified = time();
2360
b0ccd3fb 2361 header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastmodified) . ' GMT');
2362 header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $lifetime) . ' GMT');
a2e2bf64 2363 header('Cache-Control: max-age='. $lifetime);
b0ccd3fb 2364 header('Pragma: ');
2365 header('Content-type: text/css'); // Correct MIME type
6535be85 2366
909ec807 2367 $DEFAULT_SHEET_LIST = array('styles_layout', 'styles_fonts', 'styles_color');
9c4e6e21 2368
2369 if (empty($themename)) {
2370 $themename = current_theme(); // So we have something. Normally not needed.
ab25ce31 2371 } else {
2372 $themename = clean_param($themename, PARAM_SAFEDIR);
9c4e6e21 2373 }
2374
2375 if (!empty($forceconfig)) { // Page wants to use the config from this theme instead
2376 unset($THEME);
a44091bf 2377 include($CFG->themedir.'/'.$forceconfig.'/'.'config.php');
9c4e6e21 2378 }
2379
2380/// If this is the standard theme calling us, then find out what sheets we need
2381
2382 if ($themename == 'standard') {
2383 if (!isset($THEME->standardsheets) or $THEME->standardsheets === true) { // Use all the sheets we have
2384 $THEME->sheets = $DEFAULT_SHEET_LIST;
2385 } else if (empty($THEME->standardsheets)) { // We can stop right now!
2386 echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2387 exit;
2388 } else { // Use the provided subset only
2389 $THEME->sheets = $THEME->standardsheets;
2390 }
2391
2392/// If we are a parent theme, then check for parent definitions
2393
2394 } else if (!empty($THEME->parent) && $themename == $THEME->parent) {
2395 if (!isset($THEME->parentsheets) or $THEME->parentsheets === true) { // Use all the sheets we have
2396 $THEME->sheets = $DEFAULT_SHEET_LIST;
2397 } else if (empty($THEME->parentsheets)) { // We can stop right now!
2398 echo "/***** Nothing required from this stylesheet by main theme *****/\n\n";
2399 exit;
2400 } else { // Use the provided subset only
2401 $THEME->sheets = $THEME->parentsheets;
2402 }
2403 }
2404
2405/// Work out the last modified date for this theme
2406
2407 foreach ($THEME->sheets as $sheet) {
a44091bf 2408 if (file_exists($CFG->themedir.'/'.$themename.'/'.$sheet.'.css')) {
2409 $sheetmodified = filemtime($CFG->themedir.'/'.$themename.'/'.$sheet.'.css');
9c4e6e21 2410 if ($sheetmodified > $lastmodified) {
2411 $lastmodified = $sheetmodified;
2412 }
2413 }
6535be85 2414 }
2415
6535be85 2416
6ba172fb 2417/// Get a list of all the files we want to include
2418 $files = array();
2419
2420 foreach ($THEME->sheets as $sheet) {
2421 $files[] = array($CFG->themedir, $themename.'/'.$sheet.'.css');
2422 }
2423
2424 if ($themename == 'standard') { // Add any standard styles included in any modules
2425 if (!empty($THEME->modsheets)) { // Search for styles.php within activity modules
2426 if ($mods = get_list_of_plugins('mod')) {
2427 foreach ($mods as $mod) {
2428 if (file_exists($CFG->dirroot.'/mod/'.$mod.'/styles.php')) {
2429 $files[] = array($CFG->dirroot, '/mod/'.$mod.'/styles.php');
2430 }
08396bb2 2431 }
2432 }
2433 }
a2b3f884 2434
6ba172fb 2435 if (!empty($THEME->blocksheets)) { // Search for styles.php within block modules
2436 if ($mods = get_list_of_plugins('blocks')) {
2437 foreach ($mods as $mod) {
2438 if (file_exists($CFG->dirroot.'/blocks/'.$mod.'/styles.php')) {
2439 $files[] = array($CFG->dirroot, '/blocks/'.$mod.'/styles.php');
2440 }
08396bb2 2441 }
2442 }
2443 }
a2b3f884 2444
ae628043 2445 if (!isset($THEME->courseformatsheets) || $THEME->courseformatsheets) { // Search for styles.php in course formats
2446 if ($mods = get_list_of_plugins('format','',$CFG->dirroot.'/course')) {
2447 foreach ($mods as $mod) {
2448 if (file_exists($CFG->dirroot.'/course/format/'.$mod.'/styles.php')) {
2449 $files[] = array($CFG->dirroot, '/course/format/'.$mod.'/styles.php');
2450 }
2451 }
2452 }
2453 }
2454
6ba172fb 2455 if (!empty($THEME->langsheets)) { // Search for styles.php within the current language
6ba172fb 2456 if (file_exists($CFG->dirroot.'/lang/'.$lang.'/styles.php')) {
2457 $files[] = array($CFG->dirroot, '/lang/'.$lang.'/styles.php');
2458 }
2459 }
08396bb2 2460 }
2461
6ba172fb 2462
2463 if ($files) {
2464 /// Produce a list of all the files first
2465 echo '/**************************************'."\n";
2466 echo ' * THEME NAME: '.$themename."\n *\n";
2467 echo ' * Files included in this sheet:'."\n *\n";
2468 foreach ($files as $file) {
2469 echo ' * '.$file[1]."\n";
08396bb2 2470 }
6ba172fb 2471 echo ' **************************************/'."\n\n";
08396bb2 2472
6ba172fb 2473
2474 /// Actually output all the files in order.
2475 foreach ($files as $file) {
2476 echo '/***** '.$file[1].' start *****/'."\n\n";
a44091bf 2477 @include_once($file[0].'/'.$file[1]);
6ba172fb 2478 echo '/***** '.$file[1].' end *****/'."\n\n";
2479 }
9c4e6e21 2480 }
2481
a44091bf 2482 return $CFG->themewww.'/'.$themename; // Only to help old themes (1.4 and earlier)
6535be85 2483}
2484
9c4e6e21 2485
2486function theme_setup($theme = '', $params=NULL) {
d74d4f20 2487/// Sets up global variables related to themes
2488
56766f85 2489 global $CFG, $THEME, $SESSION, $USER;
d74d4f20 2490
2491 if (empty($theme)) {
2492 $theme = current_theme();
2493 }
9c4e6e21 2494
09ad59dc 2495/// If the theme doesn't exist for some reason then revert to standardwhite
a44091bf 2496 if (!file_exists($CFG->themedir .'/'. $theme .'/config.php')) {
09ad59dc 2497 $CFG->theme = $theme = 'standardwhite';
2498 }
2499
55b734fb 2500/// Load up the theme config
2501 $THEME = NULL; // Just to be sure
a44091bf 2502 include($CFG->themedir .'/'. $theme .'/config.php'); // Main config for current theme
55b734fb 2503
9c4e6e21 2504/// Put together the parameters
2505 if (!$params) {
2506 $params = array();
2507 }
56766f85 2508 if ($theme != $CFG->theme) {
2509 $params[] = 'forceconfig='.$theme;
9c4e6e21 2510 }
55b734fb 2511
2512/// Force language too if required
2513 if (!empty($THEME->langsheets)) {
2514 $params[] = 'lang='.current_language();
2515 }
2516
2517/// Convert params to string
9c4e6e21 2518 if ($params) {
55b734fb 2519 $paramstring = '?'.implode('&', $params);
d74d4f20 2520 } else {
9c4e6e21 2521 $paramstring = '';
d74d4f20 2522 }
2523
9c4e6e21 2524/// Set up image paths
25580407 2525 if (empty($THEME->custompix)) { // Could be set in the above file
d74d4f20 2526 $CFG->pixpath = $CFG->wwwroot .'/pix';
2527 $CFG->modpixpath = $CFG->wwwroot .'/mod';
2528 } else {
a44091bf 2529 $CFG->pixpath = $CFG->themewww .'/'. $theme .'/pix';
2530 $CFG->modpixpath = $CFG->themewww .'/'. $theme .'/pix/mod';
d74d4f20 2531 }
2532
9c4e6e21 2533/// Header and footer paths
a44091bf 2534 $CFG->header = $CFG->themedir .'/'. $theme .'/header.html';
2535 $CFG->footer = $CFG->themedir .'/'. $theme .'/footer.html';
d74d4f20 2536
9c4e6e21 2537/// Define stylesheet loading order
d74d4f20 2538 $CFG->stylesheets = array();
2539 if ($theme != 'standard') { /// The standard sheet is always loaded first
a44091bf 2540 $CFG->stylesheets[] = $CFG->themewww.'/standard/styles.php'.$paramstring;
d74d4f20 2541 }
2542 if (!empty($THEME->parent)) { /// Parent stylesheets are loaded next
a44091bf 2543 $CFG->stylesheets[] = $CFG->themewww.'/'.$THEME->parent.'/styles.php'.$paramstring;
d74d4f20 2544 }
a44091bf 2545 $CFG->stylesheets[] = $CFG->themewww.'/'.$theme.'/styles.php'.$paramstring;
d74d4f20 2546
2547}
2548
c3f55692 2549
d48b00b4 2550/**
2551 * Returns text to be displayed to the user which reflects their login status
2552 *
2553 * @uses $CFG
2554 * @uses $USER
89dcb99d 2555 * @param course $course {@link $COURSE} object containing course information
2556 * @param user $user {@link $USER} object containing user information
2557 * @return string
d48b00b4 2558 */
c44d5d42 2559function user_login_string($course=NULL, $user=NULL) {
2560 global $USER, $CFG, $SITE;
a282d0ff 2561
86a1ba04 2562 if (empty($user) and !empty($USER->id)) {
a282d0ff 2563 $user = $USER;
2564 }
2565
c44d5d42 2566 if (empty($course)) {
2567 $course = $SITE;
2568 }
2569
a282d0ff 2570 if (isset($user->realuser)) {
b0ccd3fb 2571 if ($realuser = get_record('user', 'id', $user->realuser)) {
2d71e8ee 2572 $fullname = fullname($realuser, true);
2573 $realuserinfo = " [<a target=\"{$CFG->framename}\"
01eb4f5f 2574 href=\"$CFG->wwwroot/course/loginas.php?id=$course->id&amp;return=1\">$fullname</a>] ";
9fa49e22 2575 }
9d378732 2576 } else {
b0ccd3fb 2577 $realuserinfo = '';
9fa49e22 2578 }
2579
87180677 2580 if (empty($CFG->loginhttps)) {
2581 $wwwroot = $CFG->wwwroot;
2582 } else {
2c3432e6 2583 $wwwroot = str_replace('http:','https:',$CFG->wwwroot);
87180677 2584 }
2585
8743f4f3 2586 if (empty($course->id)) {
2587 // $course->id is not defined during installation
2588 return '';
2589 } else if (isset($user->id) and $user->id) {
2d07587b 2590 $context = get_context_instance(CONTEXT_COURSE, $course->id);
2591
2d71e8ee 2592 $fullname = fullname($user, true);
2593 $username = "<a target=\"{$CFG->framename}\" href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>";
8f8ed475 2594 if (isset($user->username) && $user->username == 'guest') {
158de846 2595 $loggedinas = $realuserinfo.get_string('loggedinasguest').
b0ccd3fb 2596 " (<a target=\"{$CFG->framename}\" href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
2d07587b 2597 } else if (!empty($user->switchrole[$context->id])) {
91ea292e 2598 $rolename = '';
2d07587b 2599 if ($role = get_record('role', 'id', $user->switchrole[$context->id])) {
91ea292e 2600 $rolename = ': '.format_string($role->name);
2601 }
2602 $loggedinas = get_string('loggedinas', 'moodle', $username).$rolename.
2603 " (<a target=\"{$CFG->framename}\"
2604 href=\"$CFG->wwwroot/course/view.php?id=$course->id&amp;switchrole=0&amp;sesskey=".sesskey()."\">".get_string('switchrolereturn').'</a>)';
0ae7e6f4 2605 } else {
d5a01638 2606 $loggedinas = $realuserinfo.get_string('loggedinas', 'moodle', $username).' '.
b0ccd3fb 2607 " (<a target=\"{$CFG->framename}\" href=\"$CFG->wwwroot/login/logout.php\">".get_string('logout').'</a>)';
0ae7e6f4 2608 }
9fa49e22 2609 } else {
b0ccd3fb 2610 $loggedinas = get_string('loggedinnot', 'moodle').
2611 " (<a target=\"{$CFG->framename}\" href=\"$wwwroot/login/index.php\">".get_string('login').'</a>)';
9fa49e22 2612 }
d3593a67 2613 return '<div class="logininfo">'.$loggedinas.'</div>';
9fa49e22 2614}
2615
d48b00b4 2616/**
57100a90 2617 * Tests whether $THEME->rarrow, $THEME->larrow have been set (theme/-/config.php).
2618 * If not it applies sensible defaults.
d48b00b4 2619 *
57100a90 2620 * Accessibility: right and left arrow Unicode characters for breadcrumb, calendar,
2621 * search forum block, etc. Important: these are 'silent' in a screen-reader
2622 * (unlike &gt; &raquo;), and must be accompanied by text.
2623 * @uses $THEME
d48b00b4 2624 */
57100a90 2625function check_theme_arrows() {
2626 global $THEME;
2627
2628 if (!isset($THEME->rarrow) and !isset($THEME->larrow)) {
ce27e6f4 2629 // Default, looks good in Win XP/IE 6, Win/Firefox 1.5, Win/Netscape 8...
2630 // Also OK in Win 9x/2K/IE 5.x
57100a90 2631 $THEME->rarrow = '&#x25BA;';
2632 $THEME->larrow = '&#x25C4;';
ce27e6f4 2633 $uagent = $_SERVER['HTTP_USER_AGENT'];
2634 if (false !== strpos($uagent, 'Opera')
2635 || false !== strpos($uagent, 'Mac')) {
2636 // Looks good in Win XP/Mac/Opera 8/9, Mac/Firefox 2, Camino, Safari.
2637 // Not broken in Mac/IE 5, Mac/Netscape 7 (?).
57100a90 2638 $THEME->rarrow = '&#x25B6;';
2639 $THEME->larrow = '&#x25C0;';
ce27e6f4 2640 }
2641 elseif (false !== strpos($uagent, 'Konqueror')) {
2642 $THEME->rarrow = '&rarr;';
2643 $THEME->larrow = '&larr;';
2644 }
2645 elseif (isset($_SERVER['HTTP_ACCEPT_CHARSET'])
2646 && false === stripos($_SERVER['HTTP_ACCEPT_CHARSET'], 'utf-8')) {
2647 // (Win/IE 5 doesn't set ACCEPT_CHARSET, but handles Unicode.)
2648 // To be safe, non-Unicode browsers!
2649 $THEME->rarrow = '&gt;';
2650 $THEME->larrow = '&lt;';
2651 }
57100a90 2652 }
2653}
da4124be 2654
57100a90 2655/**
2656 * Prints breadcrumb trail of links, called in theme/-/header.html
2657 *
2658 * @uses $CFG
2659 * @param string $navigation The breadcrumb navigation string to be printed
2660 * @param string $separator The breadcrumb trail separator. The default 0 leads to the use
2661 * of $THEME->rarrow, themes could use '&rarr;', '/', or '' for a style-sheet solution.
2662 * @param boolean $return False to echo the breadcrumb string (default), true to return it.
2663 */
2664function print_navigation ($navigation, $separator=0, $return=false) {
2665 global $CFG, $THEME;
da4124be 2666 $output = '';
57100a90 2667
2668 check_theme_arrows();
2669 if (0 === $separator) {
2670 $separator = $THEME->rarrow;
2671 }
2672 if (!empty($separator)) {
2673 $separator = '<span class="sep">'. $separator .'</span>';
2674 }
da4124be 2675
2676 if ($navigation) {
57100a90 2677 //Accessibility: breadcrumb links now in a list, &raquo; replaced with a 'silent' character.
da4124be 2678 $nav_text = get_string('youarehere','access');
57100a90 2679 $output .= '<h2 class="accesshide">'.$nav_text."</h2><ul>\n";
da4124be 2680 if (! $site = get_site()) {
2681 $site->shortname = get_string('home');
2682 }
57100a90 2683 $navigation = "<li>$separator ". str_replace('->', "</li>\n<li>$separator", $navigation) ."</li>\n";
51792df0 2684 $output .= '<li class="first"><a target="'. $CFG->framename .'" href="'. $CFG->wwwroot.((!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM, SITEID)) && !empty($USER->id) && !empty($CFG->mymoodleredirect) && !isguest())
9a477f90 2685 ? '/my' : '') .'/">'. $site->shortname ."</a></li>\n". $navigation;
da4124be 2686 $output .= "</ul>\n";
2687 }
2688
2689 if ($return) {
2690 return $output;
2691 } else {
2692 echo $output;
2693 }
9fa49e22 2694}
2695
d48b00b4 2696/**
bfca0094 2697 * Prints a string in a specified size (retained for backward compatibility)
d48b00b4 2698 *
89dcb99d 2699 * @param string $text The text to be displayed
2700 * @param int $size The size to set the font for text display.
d48b00b4 2701 */
da4124be 2702function print_headline($text, $size=2, $return=false) {
2703 $output = print_heading($text, 'left', $size, true);
2704 if ($return) {
2705 return $output;
2706 } else {
2707 echo $output;
2708 }
d4df9200 2709}
2710
d48b00b4 2711/**
2712 * Prints text in a format for use in headings.
2713 *
89dcb99d 2714 * @param string $text The text to be displayed
2715 * @param string $align The alignment of the printed paragraph of text
2716 * @param int $size The size to set the font for text display.
d48b00b4 2717 */
da4124be 2718function print_heading($text, $align='', $size=2, $class='main', $return=false) {
bfca0094 2719 if ($align) {
2720 $align = ' align="'.$align.'"';
2721 }
4419aa80 2722 if ($class) {
2723 $class = ' class="'.$class.'"';
2724 }
da4124be 2725 $output = "<h$size $align $class>".stripslashes_safe($text)."</h$size>";
2726
2727 if ($return) {
2728 return $output;
2729 } else {
2730 echo $output;
2731 }
9fa49e22 2732}
2733
d48b00b4 2734/**
2735 * Centered heading with attached help button (same title text)
2736 * and optional icon attached
2737 *
89dcb99d 2738 * @param string $text The text to be displayed
2739 * @param string $helppage The help page to link to
2740 * @param string $module The module whose help should be linked to
2741 * @param string $icon Image to display if needed
d48b00b4 2742 */
da4124be 2743function print_heading_with_help($text, $helppage, $module='moodle', $icon='', $return=false) {
2744 $output = '';
2745 $output .= '<h2 class="main help">'.$icon.stripslashes_safe($text);
2746 $output .= helpbutton($helppage, $text, $module, true, false, '', true);
2747 $output .= '</h2>';
2748
2749 if ($return) {
2750 return $output;
2751 } else {
2752 echo $output;
2753 }
9fa49e22 2754}
ab9f24ad 2755
6ee8277f 2756
da4124be 2757function print_heading_block($heading, $class='', $return=false) {
b6f30b18 2758 //Accessibility: 'headingblock' is now H1, see theme/standard/styles_*.css: ??
da4124be 2759 $output = '<h2 class="headingblock header '.$class.'">'.stripslashes($heading).'</h2>';
2760
2761 if ($return) {
2762 return $output;
2763 } else {
2764 echo $output;
2765 }
6ee8277f 2766}
2767
2768
d48b00b4 2769/**
2770 * Print a link to continue on to another page.
2771 *
2772 * @uses $CFG
2773 * @param string $link The url to create a link to.
2774 */
da4124be 2775function print_continue($link, $return=false) {
9fa49e22 2776
51a96819 2777 global $CFG;
2778
dedb2304 2779 // in case we are logging upgrade in admin/index.php stop it
2780 if (function_exists('upgrade_log_finish')) {
2781 upgrade_log_finish();
2782 }
2783
da4124be 2784 $output = '';
2785
9fa49e22 2786 if (!$link) {
b0ccd3fb 2787 $link = $_SERVER['HTTP_REFERER'];
9fa49e22 2788 }
2789
da4124be 2790 $output .= '<div class="continuebutton">';
2791 $output .= print_single_button($link, NULL, get_string('continue'), 'post', $CFG->framename, true);
2792 $output .= '</div>'."\n";
2793
2794 if ($return) {
2795 return $output;
2796 } else {
2797 echo $output;
2798 }
9fa49e22 2799}
2800
d48b00b4 2801/**
2802 * Print a message in a standard themed box.
408b65dd 2803 * See, {@link print_simple_box_start}.
d48b00b4 2804 *
408b65dd 2805 * @param string $align string, alignment of the box, not the text (default center, left, right).
2806 * @param string $width string, width of the box, including units %, for example '100%'.
2807 * @param string $color string, background colour of the box, for example '#eee'.
2808 * @param int $padding integer, padding in pixels, specified without units.
2809 * @param string $class string, space-separated class names.
d48b00b4 2810 * @todo Finish documenting this function
2811 */
da4124be 2812function print_simple_box($message, $align='', $width='', $color='', $padding=5, $class='generalbox', $id='', $return=false) {
2813 $output = '';
2814 $output .= print_simple_box_start($align, $width, $color, $padding, $class, $id, true);
2815 $output .= stripslashes_safe($message);
2816 $output .= print_simple_box_end(true);
2817
2818 if ($return) {
2819 return $output;
2820 } else {
2821 echo $output;
2822 }
9fa49e22 2823}
2824
d48b00b4 2825/**
30c9e694 2826 * Print the top portion of a standard themed box using a TABLE. Yes, we know.
2827 * See bug 4943 for details on some accessibility work regarding this that didn't make it into 1.6.
408b65dd 2828 *
2829 * @param string $align string, alignment of the box, not the text (default center, left, right).
2830 * @param string $width string, width of the box, including % units, for example '100%'.
97f76a1a 2831 * @param string $color string, background colour of the box, for example '#eee'.
2832 * @param int $padding integer, padding in pixels, specified without units.
2833 * @param string $class string, space-separated class names.
d48b00b4 2834 */
da4124be 2835function print_simple_box_start($align='', $width='', $color='', $padding=5, $class='generalbox', $id='', $return=false) {
2836
2837 $output = '';
9fa49e22 2838
c147b8ab 2839 if ($color) {
30c9e694 2840 $color = 'bgcolor="'. $color .'"';
8ceb3e67 2841 }
30c9e694 2842 if ($align) {
2843 $align = 'align="'. $align .'"';
8ceb3e67 2844 }
9fa49e22 2845 if ($width) {
30c9e694 2846 $width = 'width="'. $width .'"';
9fa49e22 2847 }
da17a899 2848 if ($id) {
2849 $id = 'id="'. $id .'"';
30c9e694 2850 }
da4124be 2851 $output .= "<table $align $width $id class=\"$class\" border=\"0\" cellpadding=\"$padding\" cellspacing=\"0\">".
30c9e694 2852 "<tr><td $color class=\"$class"."content\">";
da4124be 2853
2854 if ($return) {
2855 return $output;
2856 } else {
2857 echo $output;
2858 }
9fa49e22 2859}
2860
d48b00b4 2861/**
2862 * Print the end portion of a standard themed box.
d48b00b4 2863 */
da4124be 2864function print_simple_box_end($return=false) {
2865 $output = '</td></tr></table>';
2866 if ($return) {
2867 return $output;
2868 } else {
2869 echo $output;
2870 }
9fa49e22 2871}
2872
30c9e694 2873
d48b00b4 2874/**
2875 * Print a self contained form with a single submit button.
2876 *
89dcb99d 2877 * @param string $link ?
2878 * @param array $options ?
2879 * @param string $label ?
2880 * @param string $method ?
d48b00b4 2881 * @todo Finish documenting this function
2882 */
da4124be 2883function print_single_button($link, $options, $label='OK', $method='get', $target='_self', $return=false) {
2884 $output = '';
2885 $output .= '<div class="singlebutton">';
2886 $output .= '<form action="'. $link .'" method="'. $method .'" target="'.$target.'">';
9fa49e22 2887 if ($options) {
2888 foreach ($options as $name => $value) {
da4124be 2889 $output .= '<input type="hidden" name="'. $name .'" value="'. $value .'" />';
9fa49e22 2890 }
2891 }
da4124be 2892 $output .= '<input type="submit" value="'. $label .'" /></form></div>';
2893
2894 if ($return) {
2895 return $output;
2896 } else {
2897 echo $output;
2898 }
9fa49e22 2899}
2900
d48b00b4 2901/**
2902 * Print a spacer image with the option of including a line break.
2903 *
89dcb99d 2904 * @param int $height ?
2905 * @param int $width ?
2906 * @param boolean $br ?
d48b00b4 2907 * @todo Finish documenting this function
2908 */
da4124be 2909function print_spacer($height=1, $width=1, $br=true, $return=false) {
9fa49e22 2910 global $CFG;
da4124be 2911 $output = '';
2912
2913 $output .= '<img class="spacer" height="'. $height .'" width="'. $width .'" src="'. $CFG->wwwroot .'/pix/spacer.gif" alt="" />';
9fa49e22 2914 if ($br) {
da4124be 2915 $output .= '<br />'."\n";
2916 }
2917
2918 if ($return) {
2919 return $output;
2920 } else {
2921 echo $output;
9fa49e22 2922 }
2923}
2924
d48b00b4 2925/**
2926 * Given the path to a picture file in a course, or a URL,
2927 * this function includes the picture in the page.
2928 *
89dcb99d 2929 * @param string $path ?
2930 * @param int $courseid ?
2931 * @param int $height ?
2932 * @param int $width ?
2933 * @param string $link ?
d48b00b4 2934 * @todo Finish documenting this function
2935 */
da4124be 2936function print_file_picture($path, $courseid=0, $height='', $width='', $link='', $return=false) {
9fa49e22 2937 global $CFG;
da4124be 2938 $output = '';
9fa49e22 2939
2940 if ($height) {
b0ccd3fb 2941 $height = 'height="'. $height .'"';
9fa49e22 2942 }
2943 if ($width) {
b0ccd3fb 2944 $width = 'width="'. $width .'"';
9fa49e22 2945 }
2946 if ($link) {
da4124be 2947 $output .= '<a href="'. $link .'">';
9fa49e22 2948 }
b0ccd3fb 2949 if (substr(strtolower($path), 0, 7) == 'http://') {
da4124be 2950 $output .= '<img border="0" '.$height . $width .' src="'. $path .'" />';
9fa49e22 2951
2952 } else if ($courseid) {
da4124be 2953 $output .= '<img border="0" '. $height . $width .' src="';
9fa49e22 2954 if ($CFG->slasharguments) { // Use this method if possible for better caching
da4124be 2955 $output .= $CFG->wwwroot .'/file.php/'. $courseid .'/'. $path;
9fa49e22 2956 } else {
da4124be 2957 $output .= $CFG->wwwroot .'/file.php?file=/'. $courseid .'/'. $path;
9fa49e22 2958 }
da4124be 2959 $output .= '" />';
9fa49e22 2960 } else {
da4124be 2961 $output .= 'Error: must pass URL or course';
9fa49e22 2962 }
2963 if ($link) {
da4124be 2964 $output .= '</a>';
2965 }
2966
2967 if ($return) {
2968 return $output;
2969 } else {
2970 echo $output;
9fa49e22 2971 }
2972}
2973
d48b00b4 2974/**
2975 * Print the specified user's avatar.
2976 *
89dcb99d 2977 * @param int $userid ?
2978 * @param int $courseid ?
2979 * @param boolean $picture Print the user picture?
4c6c0e01 2980 * @param int $size Size in pixels. Special values are (true/1 = 100px) and (false/0 = 35px) for backward compatability
da4124be 2981 * @param boolean $return If false print picture to current page, otherwise return the output as string
89dcb99d 2982 * @param boolean $link Enclose printed image in a link to view specified course?
2983 * return string
d48b00b4 2984 * @todo Finish documenting this function
2985 */
da4124be 2986function print_user_picture($userid, $courseid, $picture, $size=0, $return=false, $link=true, $target='') {
f374fb10 2987 global $CFG;
9fa49e22 2988
2989 if ($link) {
f66e1977 2990 if ($target) {
2991 $target=' target="_blank"';
2992 }
2993 $output = '<a '.$target.' href="'. $CFG->wwwroot .'/user/view.php?id='. $userid .'&amp;course='. $courseid .'">';
9fa49e22 2994 } else {
b0ccd3fb 2995 $output = '';
9fa49e22 2996 }
4c6c0e01 2997 if (empty($size)) {
2998 $file = 'f2';
da7a785b 2999 $size = 35;
4c6c0e01 3000 } else if ($size === true or $size == 1) {
b0ccd3fb 3001 $file = 'f1';
9fa49e22 3002 $size = 100;
4c6c0e01 3003 } else if ($size >= 50) {
3004 $file = 'f1';
9fa49e22 3005 } else {
b0ccd3fb 3006 $file = 'f2';
9fa49e22 3007 }
113a79e2 3008 $class = "userpicture";
67a63a30 3009 if ($picture) { // Print custom user picture
9fa49e22 3010 if ($CFG->slasharguments) { // Use this method if possible for better caching
113a79e2 3011 $src = $CFG->wwwroot .'/user/pix.php/'. $userid .'/'. $file .'.jpg"';
9fa49e22 3012 } else {
113a79e2 3013 $src = $CFG->wwwroot .'/user/pix.php?file=/'. $userid .'/'. $file .'.jpg"';
9fa49e22 3014 }
67a63a30 3015 } else { // Print default user pictures (use theme version if available)
113a79e2 3016 $class .= " defaultuserpic";
3017 $src = "$CFG->pixpath/u/$file.png\"";
9fa49e22 3018 }
5a86df5e 3019 if ($user = get_record('user','id',$userid)) {
3020 if (!empty($user->imagealt)) {
3021 $imagealt = $user->imagealt;
3022 } else {
3023 $imagealt = get_string('pictureof','',fullname($user));
3024 }
2bbaf749 3025 }
113a79e2 3026 $output .= "<img class=\"$class\" align=\"middle\" src=\"$src".
5a86df5e 3027 " border=\"0\" width=\"$size\" height=\"$size\" alt=\"".$imagealt."\" />";
9fa49e22 3028 if ($link) {
b0ccd3fb 3029 $output .= '</a>';
9fa49e22 3030 }
3031
da4124be 3032 if ($return) {
9fa49e22 3033 return $output;
3034 } else {
3035 echo $output;
3036 }
3037}
3038
d48b00b4 3039/**
3040 * Prints a summary of a user in a nice little box.
3041 *
89dcb99d 3042 * @uses $CFG
3043 * @uses $USER
3044 * @param user $user A {@link $USER} object representing a user
3045 * @param course $course A {@link $COURSE} object representing a course
d48b00b4 3046 */
da4124be 3047function print_user($user, $course, $messageselect=false, $return=false) {
951b22a8 3048
89dcb99d 3049 global $CFG, $USER;
499795e8 3050
da4124be 3051 $output = '';
3052
951b22a8 3053 static $string;
3054 static $datestring;
3055 static $countries;
951b22a8 3056
ec7a8b79 3057 $context = get_context_instance(CONTEXT_COURSE, $course->id);
04186cd9 3058
951b22a8 3059 if (empty($string)) { // Cache all the strings for the rest of the page
3060
b0ccd3fb 3061 $string->email = get_string('email');
3062 $string->location = get_string('location');
3063 $string->lastaccess = get_string('lastaccess');
3064 $string->activity = get_string('activity');
3065 $string->unenrol = get_string('unenrol');
3066 $string->loginas = get_string('loginas');
3067 $string->fullprofile = get_string('fullprofile');
3068 $string->role = get_string('role');
3069 $string->name = get_string('name');
3070 $string->never = get_string('never');
3071
3072 $datestring->day = get_string('day');
3073 $datestring->days = get_string('days');
3074 $datestring->hour = get_string('hour');
3075 $datestring->hours = get_string('hours');
3076 $datestring->min = get_string('min');
3077 $datestring->mins = get_string('mins');
3078 $datestring->sec = get_string('sec');
3079 $datestring->secs = get_string('secs');
951b22a8 3080
3081 $countries = get_list_of_countries();
951b22a8 3082 }
3083
3468d58a 3084/// Get the hidden field list
04186cd9 3085 if (has_capability('moodle/course:viewhiddenuserfields', $context)) {
3468d58a 3086 $hiddenfields = array();
3087 } else {
3088 $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
3089 }
3090
da4124be 3091 $output .= '<table class="userinfobox">';
3092 $output .= '<tr>';
3093 $output .= '<td class="left side">';
3094 $output .= print_user_picture($user->id, $course->id, $user->picture, true, true);
3095 $output .= '</td>';
3096 $output .= '<td class="content">';
ec7a8b79 3097 $output .= '<div class="username">'.fullname($user, has_capability('moodle/site:viewfullnames', $context)).'</div>';
da4124be 3098 $output .= '<div class="info">';
951b22a8 3099 if (!empty($user->role) and ($user->role <> $course->teacher)) {
da4124be 3100 $output .= $string->role .': '. $user->role .'<br />';
951b22a8 3101 }
1936c10e 3102 if ($user->maildisplay == 1 or ($user->maildisplay == 2 and ($course->id != SITEID) and !isguest()) or
04186cd9 3103has_capability('moodle/course:viewhiddenuserfields', $context)) {
da4124be 3104 $output .= $string->email .': <a href="mailto:'. $user->email .'">'. $user->email .'</a><br />';
951b22a8 3105 }
3468d58a 3106 if (($user->city or $user->country) and (!isset($hiddenfields['city']) or !isset($hiddenfields['country']))) {
da4124be 3107 $output .= $string->location .': ';
3468d58a 3108 if ($user->city && !isset($hiddenfields['city'])) {
da4124be 3109 $output .= $user->city;
b40bc478 3110 }
3468d58a 3111 if (!empty($countries[$user->country]) && !isset($hiddenfields['country'])) {
3112 if ($user->city && !isset($hiddenfields['city'])) {
da4124be 3113 $output .= ', ';
b40bc478 3114 }
da4124be 3115 $output .= $countries[$user->country];
b40bc478 3116 }
da4124be 3117 $output .= '<br />';
951b22a8 3118 }
ae8a5ff0 3119
3468d58a 3120 if (!isset($hiddenfields['lastaccess'])) {
3121 if ($user->lastaccess) {
da4124be 3122 $output .= $string->lastaccess .': '. userdate($user->lastaccess);
18894ee4 3123 $output .= '&nbsp; ('. format_time(time() - $user->lastaccess, $datestring) .')';
3468d58a 3124 } else {
da4124be 3125 $output .= $string->lastaccess .': '. $string->never;
3468d58a 3126 }
951b22a8 3127 }
da4124be 3128 $output .= '</div></td><td class="links">';
ae8a5ff0 3129 //link to blogs
b7425e2e 3130 if ($CFG->bloglevel > 0) {
da4124be 3131 $output .= '<a href="'.$CFG->wwwroot.'/blog/index.php?userid='.$user->id.'">'.get_string('blogs','blog').'</a><br />';
b7425e2e 3132 }
da4124be 3133
04186cd9 3134 if (has_capability('moodle/site:viewreports', $context)) {
951b22a8 3135 $timemidnight = usergetmidnight(time());
da4124be 3136 $output .= '<a href="'. $CFG->wwwroot .'/course/user.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->activity .'</a><br />';
04186cd9 3137 }
3138 if (has_capability('moodle/role:assign', $context, NULL)) { // Includes admins
3139 $output .= '<a href="'. $CFG->wwwroot .'/course/unenrol.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->unenrol .'</a><br />';
3140 }
3141 if ($USER->id != $user->id && has_capability('moodle/user:loginas', $context)) {
3142 $output .= '<a href="'. $CFG->wwwroot .'/course/loginas.php?id='. $course->id .'&amp;user='. $user->id .'">'. $string->loginas .'</a><br />';
ab9f24ad 3143 }
da4124be 3144 $output .= '<a href="'. $CFG->wwwroot .'/user/view.php?id='. $user->id .'&amp;course='. $course->id .'">'. $string->fullprofile .'...</a>';
951b22a8 3145
04186cd9 3146 if (!empty($messageselect)) {
3147 $output .= '<br /><input type="checkbox" name="user'.$user->id.'" /> ';
f19570d0 3148 }
3149
da4124be 3150 $output .= '</td></tr></table>';
3151
3152 if ($return) {
3153 return $output;
3154 } else {
3155 echo $output;
3156 }
951b22a8 3157}
3158
d48b00b4 3159/**
3160 * Print a specified group's avatar.
3161 *
fdcd0f05 3162 * @param group $group A {@link group} object representing a group or array of groups
89dcb99d 3163 * @param int $courseid ?
3164 * @param boolean $large ?
da4124be 3165 * @param boolean $return ?
89dcb99d 3166 * @param boolean $link ?
3167 * @return string
d48b00b4 3168 * @todo Finish documenting this function
3169 */
da4124be 3170function print_group_picture($group, $courseid, $large=false, $return=false, $link=true) {
f374fb10 3171 global $CFG;
3172
fdcd0f05 3173 if (is_array($group)) {
3174 $output = '';
3175 foreach($group as $g) {
3176 $output .= print_group_picture($g, $courseid, $large, true, $link);
3177 }
da4124be 3178 if ($return) {
fdcd0f05 3179 return $output;
3180 } else {
3181 echo $output;
3182 return;
3183 }
3184 }
3185
ec7a8b79 3186 $context = get_context_instance(CONTEXT_COURSE, $courseid);
97ea4833 3187
ec7a8b79 3188 if ($group->hidepicture and !has_capability('moodle/course:managegroups', $context)) {
3c0561cf 3189 return '';
3190 }
c3cbfe7f 3191
ec7a8b79 3192 if ($link or has_capability('moodle/site:accessallgroups', $context)) {
a756cf1d 3193 $output = '<a href="'. $CFG->wwwroot .'/user/index.php?id='. $courseid .'&amp;group='. $group->id .'">';
3c0561cf 3194 } else {
3195 $output = '';
3196 }
3197 if ($large) {
b0ccd3fb 3198 $file = 'f1';
3c0561cf 3199 $size = 100;
3200 } else {
b0ccd3fb 3201 $file = 'f2';
3c0561cf 3202 $size = 35;
3203 }
3204 if ($group->picture) { // Print custom group picture
3205 if ($CFG->slasharguments) { // Use this method if possible for better caching
ed3136ff 3206 $output .= '<img class="grouppicture" align="middle" src="'.$CFG->wwwroot.'/user/pixgroup.php/'.$group->id.'/'.$file.'.jpg"'.
2bbaf749 3207 ' border="0" width="'.$size.'" height="'.$size.'" alt="'.s(get_string('group').' '.$group->name).'" title="'.s($group->name).'"/>';
f2c80965 3208 } else {
ed3136ff 3209 $output .= '<img class="grouppicture" align="middle" src="'.$CFG->wwwroot.'/user/pixgroup.php?file=/'.$group->id.'/'.$file.'.jpg"'.
2bbaf749 3210 ' border="0" width="'.$size.'" height="'.$size.'" alt="'.s(get_string('group').' '.$group->name).'" title="'.s($group->name).'"/>';
f2c80965 3211 }
f374fb10 3212 }
ec7a8b79 3213 if ($link or has_capability('moodle/site:accessallgroups', $context)) {
b0ccd3fb 3214 $output .= '</a>';
3c0561cf 3215 }
f374fb10 3216
da4124be 3217 if ($return) {
f374fb10 3218 return $output;
3219 } else {
3220 echo $output;
3221 }
3222}
3223
d48b00b4 3224/**
3225 * Print a png image.
3226 *
89dcb99d 3227 * @param string $url ?
3228 * @param int $sizex ?
3229 * @param int $sizey ?
da4124be 3230 * @param boolean $return ?
89dcb99d 3231 * @param string $parameters ?
d48b00b4 3232 * @todo Finish documenting this function
3233 */
da4124be 3234function print_png($url, $sizex, $sizey, $return, $parameters='alt=""') {
35067c43 3235 global $CFG;
3236 static $recentIE;
3237
3238 if (!isset($recentIE)) {
3239 $recentIE = check_browser_version('MSIE', '5.0');
3240 }
3241
3242 if ($recentIE) { // work around the HORRIBLE bug IE has with alpha transparencies
89dcb99d 3243 $output .= '<img src="'. $CFG->pixpath .'/spacer.gif" width="'. $sizex .'" height="'. $sizey .'"'.
430c6392 3244 ' border="0" class="png" style="width: '. $sizex .'px; height: '. $sizey .'px; '.
89dcb99d 3245 ' filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='.
3246 "'$url', sizingMethod='scale') ".
3247 ' '. $parameters .' />';
35067c43 3248 } else {
89dcb99d 3249 $output .= '<img src="'. $url .'" border="0" width="'. $sizex .'" height="'. $sizey .'" '.
3250 ' '. $parameters .' />';
35067c43 3251 }
3252
da4124be 3253 if ($return) {
35067c43 3254 return $output;
3255 } else {
3256 echo $output;
3257 }
3258}
3259
d48b00b4