Applied patch #411
[elgg.git] / lib / elgglib.php
blob43a08b7425932dd4658f487a7a5d130e0c35dc18
1 <?php
3 /**
4 * Library of functions for handling input validation
5 * and some HTML generation
6 *
7 * This library incorporates portions of bits of lib/weblib.php
8 * and lib/moodlelib.php from moodle
9 * http://moodle.org || http://sourceforge.net/projects/moodle
11 * @copyright Copyright (C) 2001-2003 Martin Dougiamas http://dougiamas.com
12 * @author Curverider Ltd
13 * @author Martin Dougiamas and many others
14 * @link http://elgg.org/
15 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
16 * @package elgg
17 * @subpackage elgg.lib
21 /// PARAMETER HANDLING ////////////////////////////////////////////////////
23 /**
24 * Returns a particular value for the named variable, taken from
25 * POST or GET. If the parameter doesn't exist then an error is
26 * thrown because we require this variable.
28 * This function should be used to initialise all required values
29 * in a script that are based on parameters. Usually it will be
30 * used like this:
31 * $id = required_param('id');
33 * @param string $varname the name of the parameter variable we want
34 * @param int $options a bit field that specifies any cleaning needed
35 * @return mixed
37 function required_param($varname, $options=PARAM_CLEAN) {
39 // detect_unchecked_vars addition
40 global $CFG;
41 if (!empty($CFG->detect_unchecked_vars)) {
42 global $UNCHECKED_VARS;
43 unset ($UNCHECKED_VARS->vars[$varname]);
46 if (isset($_POST[$varname])) { // POST has precedence
47 $param = $_POST[$varname];
48 } else if (isset($_GET[$varname])) {
49 $param = $_GET[$varname];
50 } else {
51 error('A required parameter ('.$varname.') was missing');
54 return clean_param($param, $options);
57 /**
58 * Returns a particular value for the named variable, taken from
59 * POST or GET, otherwise returning a given default.
61 * This function should be used to initialise all optional values
62 * in a script that are based on parameters. Usually it will be
63 * used like this:
64 * $name = optional_param('name', 'Fred');
66 * @param string $varname the name of the parameter variable we want
67 * @param mixed $default the default value to return if nothing is found
68 * @param int $options a bit field that specifies any cleaning needed
69 * @return mixed
71 function optional_param($varname, $default=NULL, $options=PARAM_CLEAN) {
73 // detect_unchecked_vars addition
74 global $CFG;
75 if (!empty($CFG->detect_unchecked_vars)) {
76 global $UNCHECKED_VARS;
77 unset ($UNCHECKED_VARS->vars[$varname]);
80 if (isset($_POST[$varname])) { // POST has precedence
81 $param = $_POST[$varname];
82 } else if (isset($_GET[$varname])) {
83 $param = $_GET[$varname];
84 } else {
85 return $default;
88 return clean_param($param, $options);
92 /**
93 * Used by {@link optional_param()} and {@link required_param()} to
94 * clean the variables and/or cast to specific types, based on
95 * an options field.
96 * <code>
97 * $course->format = clean_param($course->format, PARAM_ALPHA);
98 * $selectedgrade_item = clean_param($selectedgrade_item, PARAM_CLEAN);
99 * </code>
101 * @uses $CFG
102 * @uses PARAM_CLEAN
103 * @uses PARAM_INT
104 * @uses PARAM_INTEGER
105 * @uses PARAM_ALPHA
106 * @uses PARAM_ALPHANUM
107 * @uses PARAM_NOTAGS
108 * @uses PARAM_ALPHATEXT
109 * @uses PARAM_BOOL
110 * @uses PARAM_SAFEDIR
111 * @uses PARAM_CLEANFILE
112 * @uses PARAM_FILE
113 * @uses PARAM_PATH
114 * @uses PARAM_HOST
115 * @uses PARAM_URL
116 * @uses PARAM_LOCALURL
117 * @uses PARAM_CLEANHTML
118 * @param mixed $param the variable we are cleaning
119 * @param int $options a bit field that specifies the cleaning needed. This field is specified by combining PARAM_* definitions together with a logical or.
120 * @return mixed
122 function clean_param($param, $options) {
124 global $CFG;
126 if (is_array($param)) { // Let's loop
127 $newparam = array();
128 foreach ($param as $key => $value) {
129 $newparam[$key] = clean_param($value, $options);
131 return $newparam;
134 if (!$options) {
135 return $param; // Return raw value
138 //this corrupts data - Sven
139 //if ((string)$param == (string)(int)$param) { // It's just an integer
140 // return (int)$param;
143 if ($options & PARAM_CLEAN) {
144 // this breaks backslashes in user input
145 // $param = stripslashes($param); // Needed by kses to work fine
146 $param = clean_text($param); // Sweep for scripts, etc
147 // and this unnecessarily escapes quotes, etc in user input
148 // $param = addslashes($param); // Restore original request parameter slashes
151 if ($options & PARAM_INT) {
152 $param = (int)$param; // Convert to integer
155 if ($options & PARAM_ALPHA) { // Remove everything not a-z
156 $param = eregi_replace('[^a-zA-Z]', '', $param);
159 if ($options & PARAM_ALPHANUM) { // Remove everything not a-zA-Z0-9
160 $param = eregi_replace('[^A-Za-z0-9]', '', $param);
163 if ($options & PARAM_ALPHAEXT) { // Remove everything not a-zA-Z/_-
164 $param = eregi_replace('[^a-zA-Z/_-]', '', $param);
167 if ($options & PARAM_BOOL) { // Convert to 1 or 0
168 $tempstr = strtolower($param);
169 if ($tempstr == 'on') {
170 $param = 1;
171 } else if ($tempstr == 'off') {
172 $param = 0;
173 } else {
174 $param = empty($param) ? 0 : 1;
178 if ($options & PARAM_NOTAGS) { // Strip all tags completely
179 $param = strip_tags($param);
182 if ($options & PARAM_SAFEDIR) { // Remove everything not a-zA-Z0-9_-
183 $param = eregi_replace('[^a-zA-Z0-9_-]', '', $param);
186 if ($options & PARAM_CLEANFILE) { // allow only safe characters
187 $param = clean_filename($param);
190 if ($options & PARAM_FILE) { // Strip all suspicious characters from filename
191 $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':\\/]', '', $param);
192 $param = ereg_replace('\.\.+', '', $param);
193 if($param == '.') {
194 $param = '';
198 if ($options & PARAM_PATH) { // Strip all suspicious characters from file path
199 $param = str_replace('\\\'', '\'', $param);
200 $param = str_replace('\\"', '"', $param);
201 $param = str_replace('\\', '/', $param);
202 $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':]', '', $param);
203 $param = ereg_replace('\.\.+', '', $param);
204 $param = ereg_replace('//+', '/', $param);
205 $param = ereg_replace('/(\./)+', '/', $param);
208 if ($options & PARAM_HOST) { // allow FQDN or IPv4 dotted quad
209 preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars
210 // match ipv4 dotted quad
211 if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){
212 // confirm values are ok
213 if ( $match[0] > 255
214 || $match[1] > 255
215 || $match[3] > 255
216 || $match[4] > 255 ) {
217 // hmmm, what kind of dotted quad is this?
218 $param = '';
220 } elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers
221 && !preg_match('/^[\.-]/', $param) // no leading dots/hyphens
222 && !preg_match('/[\.-]$/', $param) // no trailing dots/hyphens
224 // all is ok - $param is respected
225 } else {
226 // all is not ok...
227 $param='';
231 if ($options & PARAM_URL) { // allow safe ftp, http, mailto urls
233 include_once($CFG->dirroot . 'lib/validateurlsyntax.php');
236 // Parameters to validateurlsyntax()
238 // s? scheme is optional
239 // H? http optional
240 // S? https optional
241 // F? ftp optional
242 // E? mailto optional
243 // u- user section not allowed
244 // P- password not allowed
245 // a? address optional
246 // I? Numeric IP address optional (can use IP or domain)
247 // p- port not allowed -- restrict to default port
248 // f? "file" path section optional
249 // q? query section optional
250 // r? fragment (anchor) optional
252 if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p-f?q?r?')) {
253 // all is ok, param is respected
254 } else {
255 $param =''; // not really ok
257 $options ^= PARAM_URL; // Turn off the URL bit so that simple PARAM_URLs don't test true for PARAM_LOCALURL
260 if ($options & PARAM_LOCALURL) {
261 // assume we passed the PARAM_URL test...
262 // allow http absolute, root relative and relative URLs within wwwroot
263 if (!empty($param)) {
264 if (preg_match(':^/:', $param)) {
265 // root-relative, ok!
266 } elseif (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i',$param)) {
267 // absolute, and matches our wwwroot
268 } else {
269 // relative - let's make sure there are no tricks
270 if (validateUrlSyntax($param, 's-u-P-a-p-f+q?r?')) {
271 // looks ok.
272 } else {
273 $param = '';
279 if ($options & PARAM_CLEANHTML) {
280 // $param = stripslashes($param); // Remove any slashes
281 $param = clean_text($param); // Sweep for scripts, etc
282 // $param = trim($param); // Sweep for scripts, etc
285 return $param;
289 * Retrieves the list of plugins available in the $plugin
290 * directory. Defaults to 'mod'.
292 * NOTE: To get the list of enabled modules, do
293 * get_records('modules', 'enabled', true) instead.
295 * @return array
297 function get_list_of_plugins($plugin='mod', $exclude='') {
299 global $CFG;
300 if ($plugin == 'mod') {
301 $plugin = $CFG->dirroot . $plugin;
303 static $plugincache = array();
304 $plugincachename = $plugin . "_" . $exclude;
306 if (isset($plugincache[$plugincachename])) {
307 $plugins = $plugincache[$plugincachename];
308 } else {
309 $plugins = array();
310 if ($basedir = opendir($plugin)) {
311 while (false !== ($dir = readdir($basedir))) {
312 $firstchar = substr($dir, 0, 1);
313 if ($firstchar == '.' or $dir == 'CVS' or $dir == '_vti_cnf' or $dir == $exclude) {
314 continue;
316 if ((filetype($plugin .'/'. $dir) != 'dir') && ((filetype($plugin .'/'. $dir) != 'link'))) {
317 continue;
319 $plugins[] = $dir;
322 if ($plugins) {
323 asort($plugins);
325 $plugincache[$plugincachename] = $plugins;
327 return $plugins;
330 // Adds a function to the variables used to cycle through plugin extensions
331 // to actions on objects
332 function listen_for_event($object_type, $event, $function) {
334 global $CFG;
335 $CFG->event_hooks[$object_type][$event][] = $function;
339 function plugin_hook($object_type,$event,$object = null) {
341 global $CFG;
343 if (!empty($CFG->event_hooks['all']['all']) && is_array($CFG->event_hooks['all']['all'])) {
344 foreach($CFG->event_hooks['all']['all'] as $hook) {
345 $object = $hook($object_type,$event,$object);
348 if (!empty($CFG->event_hooks[$object_type]['all']) && is_array($CFG->event_hooks[$object_type]['all'])) {
349 foreach($CFG->event_hooks[$object_type]['all'] as $hook) {
350 $object = $hook($object_type,$event,$object);
353 if (!empty($CFG->event_hooks['all'][$event]) && is_array($CFG->event_hooks['all'][$event])) {
354 foreach($CFG->event_hooks['all'][$event] as $hook) {
355 $object = $hook($object_type,$event,$object);
358 if (!empty($CFG->event_hooks[$object_type][$event]) && is_array($CFG->event_hooks[$object_type][$event])) {
359 foreach($CFG->event_hooks[$object_type][$event] as $hook) {
360 $object = $hook($object_type,$event,$object);
364 return $object;
368 function report_session_error() {
369 global $CFG, $FULLME;
371 //clear session cookies
372 setcookie('ElggSession'.$CFG->sessioncookie, '', time() - 3600, $CFG->cookiepath);
373 setcookie('ElggSessionTest'.$CFG->sessioncookie, '', time() - 3600, $CFG->cookiepath);
374 //increment database error counters
375 //if (isset($CFG->session_error_counter)) {
376 // set_config('session_error_counter', 1 + $CFG->session_error_counter);
377 //} else {
378 // set_config('session_error_counter', 1);
380 //called from setup.php, so gettext module hasn't been loaded yet
381 redirect($FULLME, '', 1);
384 // never called
386 * For security purposes, this function will check that the currently
387 * given sesskey (passed as a parameter to the script or this function)
388 * matches that of the current user.
390 * @param string $sesskey optionally provided sesskey
391 * @return bool
393 // function confirm_sesskey($sesskey=NULL) {
394 // global $USER;
396 // if (!empty($USER->ignoresesskey) || !empty($CFG->ignoresesskey)) {
397 // return true;
398 // }
400 // if (empty($sesskey)) {
401 // $sesskey = required_param('sesskey'); // Check script parameters
402 // }
404 // if (!isset($USER->sesskey)) {
405 // return false;
406 // }
408 // return ($USER->sesskey === $sesskey);
409 // }
413 * Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
414 * if one does not already exist, but does not overwrite existing sesskeys. Returns the
415 * sesskey string if $USER exists, or boolean false if not.
417 * @uses $USER
418 * @return string
420 function sesskey() {
421 global $USER;
423 if(!isset($USER)) {
424 return false;
427 if (empty($USER->sesskey)) {
428 $USER->sesskey = random_string(10);
431 return $USER->sesskey;
436 * Send an email to a specified user
438 * @uses $CFG
439 * @param user $user A {@link $USER} object
440 * @param user $from A {@link $USER} object
441 * @param string $subject plain text subject line of the email
442 * @param string $messagetext plain text version of the message
443 * @param string $messagehtml complete html version of the message (optional)
444 * @param string $attachment a file on the filesystem
445 * @param string $attachname the name of the file (extension indicates MIME)
446 * @param bool $usetrueaddress determines whether $from email address should
447 * be sent out. Will be overruled by user profile setting for maildisplay
448 * @return bool|string Returns "true" if mail was sent OK, "emailstop" if email
449 * was blocked by user and "false" if there was another sort of error.
451 function email_to_user($user, $from, $subject, $messagetext, $messagehtml='', $attachment='', $attachname='', $usetrueaddress=true, $replyto='', $replytoname='') {
453 global $CFG;
454 $textlib = textlib_get_instance();
456 include_once($CFG->libdir .'/phpmailer/class.phpmailer.php');
458 if (empty($user) || empty($user->email)) {
459 return false;
463 if (over_bounce_threshold($user)) {
464 error_log("User $user->id (".fullname($user).") is over bounce threshold! Not sending.");
465 return false;
467 */ // this doesn't exist right now, we may bring it in later though.
469 $mail = new phpmailer;
471 $mail->Version = 'Elgg '; // mailer version (should have $CFG->version on here but we don't have it yet)
472 $mail->PluginDir = $CFG->libdir .'/phpmailer/'; // plugin directory (eg smtp plugin)
475 $mail->CharSet = 'UTF-8'; // everything is now uft8
477 if (empty($CFG->smtphosts)) {
478 $mail->IsMail(); // use PHP mail() = sendmail
479 } else if ($CFG->smtphosts == 'qmail') {
480 $mail->IsQmail(); // use Qmail system
481 } else {
482 $mail->IsSMTP(); // use SMTP directly
483 if ($CFG->debug > 7) {
484 echo '<pre>' . "\n";
485 $mail->SMTPDebug = true;
487 $mail->Host = $CFG->smtphosts; // specify main and backup servers
489 if ($CFG->smtpuser) { // Use SMTP authentication
490 $mail->SMTPAuth = true;
491 $mail->Username = $CFG->smtpuser;
492 $mail->Password = $CFG->smtppass;
496 /* not here yet, leave it in just in case.
497 // make up an email address for handling bounces
498 if (!empty($CFG->handlebounces)) {
499 $modargs = 'B'.base64_encode(pack('V',$user->ident)).substr(md5($user->email),0,16);
500 $mail->Sender = generate_email_processing_address(0,$modargs);
502 else {
503 $mail->Sender = $CFG->sysadminemail;
506 $mail->Sender = $CFG->sysadminemail; // for elgg. delete if we change the above.
508 // TODO add a preference for maildisplay
509 if (is_string($from)) { // So we can pass whatever we want if there is need
510 $mail->From = $CFG->noreplyaddress;
511 $mail->FromName = $from;
512 } else if (empty($from)) { // make stuff up
513 $mail->From = $CFG->sysadminemail;
514 $mail->FromName = $CFG->sitename.' '.__gettext('Administrator');
515 } else if ($usetrueaddress and !empty($from->maildisplay)) {
516 $mail->From = $from->email;
517 $mail->FromName = $from->name;
518 } else {
519 $mail->From = $CFG->noreplyaddress;
520 $mail->FromName = $from->name;
521 if (empty($replyto)) {
522 $mail->AddReplyTo($CFG->noreplyaddress,__gettext('Do not reply'));
526 if (!empty($replyto)) {
527 $mail->AddReplyTo($replyto,$replytoname);
530 $mail->Subject = $textlib->substr(stripslashes($subject), 0, 900);
532 $mail->AddAddress($user->email, $user->name);
534 $mail->WordWrap = 79; // set word wrap
536 if (!empty($from->customheaders)) { // Add custom headers
537 if (is_array($from->customheaders)) {
538 foreach ($from->customheaders as $customheader) {
539 $mail->AddCustomHeader($customheader);
541 } else {
542 $mail->AddCustomHeader($from->customheaders);
546 if (!empty($from->priority)) {
547 $mail->Priority = $from->priority;
550 //TODO add a user preference for this. right now just send plaintext
551 $user->mailformat = 0;
552 if ($messagehtml && $user->mailformat == 1) { // Don't ever send HTML to users who don't want it
553 $mail->IsHTML(true);
554 $mail->Encoding = 'quoted-printable'; // Encoding to use
555 $mail->Body = $messagehtml;
556 $mail->AltBody = "\n$messagetext\n";
557 } else {
558 $mail->IsHTML(false);
559 $mail->Body = "\n$messagetext\n";
562 if ($attachment && $attachname) {
563 if (ereg( "\\.\\." ,$attachment )) { // Security check for ".." in dir path
564 $mail->AddAddress($CFG->sysadminemail,$CFG->sitename.' '.__gettext('Administrator'));
565 $mail->AddStringAttachment('Error in attachment. User attempted to attach a filename with a unsafe name.', 'error.txt', '8bit', 'text/plain');
566 } else {
567 require_once($CFG->libdir.'/filelib.php');
568 $mimetype = mimeinfo('type', $attachname);
569 $mail->AddAttachment($attachment, $attachname, 'base64', $mimetype);
573 if ($mail->Send()) {
574 // set_send_count($user); // later
575 return true;
576 } else {
577 mtrace('ERROR: '. $mail->ErrorInfo);
578 return false;
583 * Returns an array with all the filenames in
584 * all subdirectories, relative to the given rootdir.
585 * If excludefile is defined, then that file/directory is ignored
586 * If getdirs is true, then (sub)directories are included in the output
587 * If getfiles is true, then files are included in the output
588 * (at least one of these must be true!)
590 * @param string $rootdir ?
591 * @param string $excludefile If defined then the specified file/directory is ignored
592 * @param bool $descend ?
593 * @param bool $getdirs If true then (sub)directories are included in the output
594 * @param bool $getfiles If true then files are included in the output
595 * @return array An array with all the filenames in
596 * all subdirectories, relative to the given rootdir
597 * @todo Finish documenting this function. Add examples of $excludefile usage.
599 function get_directory_list($rootdir, $excludefile='', $descend=true, $getdirs=false, $getfiles=true) {
601 $dirs = array();
603 if (!$getdirs and !$getfiles) { // Nothing to show
604 return $dirs;
607 if (!is_dir($rootdir)) { // Must be a directory
608 return $dirs;
611 if (!$dir = opendir($rootdir)) { // Can't open it for some reason
612 return $dirs;
615 while (false !== ($file = readdir($dir))) {
616 $firstchar = substr($file, 0, 1);
617 if ($firstchar == '.' or $file == 'CVS' or $file == $excludefile) {
618 continue;
620 $fullfile = $rootdir .'/'. $file;
621 if (filetype($fullfile) == 'dir') {
622 if ($getdirs) {
623 $dirs[] = $file;
625 if ($descend) {
626 $subdirs = get_directory_list($fullfile, $excludefile, $descend, $getdirs, $getfiles);
627 foreach ($subdirs as $subdir) {
628 $dirs[] = $file .'/'. $subdir;
631 } else if ($getfiles) {
632 $dirs[] = $file;
635 closedir($dir);
637 asort($dirs);
639 return $dirs;
643 * handy function to loop through an array of files and resolve any filename conflicts
644 * both in the array of filenames and for what is already on disk.
647 function resolve_filename_collisions($destination,$files,$format='%s_%d.%s') {
648 foreach ($files as $k => $f) {
649 if (check_potential_filename($destination,$f,$files)) {
650 $bits = explode('.', $f);
651 for ($i = 1; true; $i++) {
652 $try = sprintf($format, $bits[0], $i, $bits[1]);
653 if (!check_potential_filename($destination,$try,$files)) {
654 $files[$k] = $try;
655 break;
660 return $files;
664 * @used by resolve_filename_collisions
666 function check_potential_filename($destination,$filename,$files) {
667 if (file_exists($destination.'/'.$filename)) {
668 return true;
670 if (count(array_keys($files,$filename)) > 1) {
671 return true;
673 return false;
677 * Adds up all the files in a directory and works out the size.
679 * @param string $rootdir ?
680 * @param string $excludefile ?
681 * @return array
682 * @todo Finish documenting this function
684 function get_directory_size($rootdir, $excludefile='') {
686 global $CFG;
687 $textlib = textlib_get_instance();
689 // do it this way if we can, it's much faster
690 if (!empty($CFG->pathtodu) && is_executable(trim($CFG->pathtodu))) {
691 $command = trim($CFG->pathtodu).' -sk --apparent-size '.escapeshellarg($rootdir);
692 exec($command,$output,$return);
693 if (is_array($output)) {
694 return get_real_size(intval($output[0]).'k'); // we told it to return k.
698 $size = 0;
700 if (!is_dir($rootdir)) { // Must be a directory
701 return $dirs;
704 if (!$dir = @opendir($rootdir)) { // Can't open it for some reason
705 return $dirs;
708 while (false !== ($file = readdir($dir))) {
709 $firstchar = $textlib->substr($file, 0, 1);
710 if ($firstchar == '.' or $file == 'CVS' or $file == $excludefile) {
711 continue;
713 $fullfile = $rootdir .'/'. $file;
714 if (filetype($fullfile) == 'dir') {
715 $size += get_directory_size($fullfile, $excludefile);
716 } else {
717 $size += filesize($fullfile);
720 closedir($dir);
722 return $size;
726 * Converts numbers like 10M into bytes.
728 * @param mixed $size The size to be converted
729 * @return mixed
731 function get_real_size($size=0) {
732 if (!$size) {
733 return 0;
735 $scan['GB'] = 1073741824;
736 $scan['Gb'] = 1073741824;
737 $scan['G'] = 1073741824;
738 $scan['g'] = 1073741824;
739 $scan['MB'] = 1048576;
740 $scan['Mb'] = 1048576;
741 $scan['M'] = 1048576;
742 $scan['m'] = 1048576;
743 $scan['KB'] = 1024;
744 $scan['Kb'] = 1024;
745 $scan['K'] = 1024;
746 $scan['k'] = 1024;
748 while (list($key) = each($scan)) {
749 if ((strlen($size)>strlen($key))&&(substr($size, strlen($size) - strlen($key))==$key)) {
750 $size = substr($size, 0, strlen($size) - strlen($key)) * $scan[$key];
751 break;
754 return $size;
758 * Converts bytes into display form
760 * @param string $size ?
761 * @return string
762 * @staticvar string $gb Localized string for size in gigabytes
763 * @staticvar string $mb Localized string for size in megabytes
764 * @staticvar string $kb Localized string for size in kilobytes
765 * @staticvar string $b Localized string for size in bytes
766 * @todo Finish documenting this function. Verify return type.
768 function display_size($size) {
770 static $gb, $mb, $kb, $b;
772 if (empty($gb)) {
773 $gb = __gettext('GB');
774 $mb = __gettext('MB');
775 $kb = __gettext('KB');
776 $b = __gettext('bytes');
779 if ($size >= 1073741824) {
780 $size = round($size / 1073741824 * 10) / 10 . $gb;
781 } else if ($size >= 1048576) {
782 $size = round($size / 1048576 * 10) / 10 . $mb;
783 } else if ($size >= 1024) {
784 $size = round($size / 1024 * 10) / 10 . $kb;
785 } else {
786 $size = $size .' '. $b;
788 return $size;
792 * Convert high ascii characters into low ascii
793 * This code is from http://kalsey.com/2004/07/dirify_in_php/
796 function convert_high_ascii($s) {
797 $HighASCII = array(
798 "!\xc0!" => 'A', # A`
799 "!\xe0!" => 'a', # a`
800 "!\xc1!" => 'A', # A'
801 "!\xe1!" => 'a', # a'
802 "!\xc2!" => 'A', # A^
803 "!\xe2!" => 'a', # a^
804 "!\xc4!" => 'Ae', # A:
805 "!\xe4!" => 'ae', # a:
806 "!\xc3!" => 'A', # A~
807 "!\xe3!" => 'a', # a~
808 "!\xc8!" => 'E', # E`
809 "!\xe8!" => 'e', # e`
810 "!\xc9!" => 'E', # E'
811 "!\xe9!" => 'e', # e'
812 "!\xca!" => 'E', # E^
813 "!\xea!" => 'e', # e^
814 "!\xcb!" => 'Ee', # E:
815 "!\xeb!" => 'ee', # e:
816 "!\xcc!" => 'I', # I`
817 "!\xec!" => 'i', # i`
818 "!\xcd!" => 'I', # I'
819 "!\xed!" => 'i', # i'
820 "!\xce!" => 'I', # I^
821 "!\xee!" => 'i', # i^
822 "!\xcf!" => 'Ie', # I:
823 "!\xef!" => 'ie', # i:
824 "!\xd2!" => 'O', # O`
825 "!\xf2!" => 'o', # o`
826 "!\xd3!" => 'O', # O'
827 "!\xf3!" => 'o', # o'
828 "!\xd4!" => 'O', # O^
829 "!\xf4!" => 'o', # o^
830 "!\xd6!" => 'Oe', # O:
831 "!\xf6!" => 'oe', # o:
832 "!\xd5!" => 'O', # O~
833 "!\xf5!" => 'o', # o~
834 "!\xd8!" => 'Oe', # O/
835 "!\xf8!" => 'oe', # o/
836 "!\xd9!" => 'U', # U`
837 "!\xf9!" => 'u', # u`
838 "!\xda!" => 'U', # U'
839 "!\xfa!" => 'u', # u'
840 "!\xdb!" => 'U', # U^
841 "!\xfb!" => 'u', # u^
842 "!\xdc!" => 'Ue', # U:
843 "!\xfc!" => 'ue', # u:
844 "!\xc7!" => 'C', # ,C
845 "!\xe7!" => 'c', # ,c
846 "!\xd1!" => 'N', # N~
847 "!\xf1!" => 'n', # n~
848 "!\xdf!" => 'ss'
850 $find = array_keys($HighASCII);
851 $replace = array_values($HighASCII);
852 $s = preg_replace($find,$replace,$s);
853 return $s;
857 * Cleans a given filename by removing suspicious or troublesome characters
858 * Only these are allowed:
859 * alphanumeric _ - .
861 * @param string $string ?
862 * @return string
864 function clean_filename($string) {
865 $string = convert_high_ascii($string);
866 $string = eregi_replace("\.\.+", '', $string);
867 $string = preg_replace('/[^\.a-zA-Z\d\_-]/','_', $string ); // only allowed chars
868 $string = eregi_replace("_+", '_', $string);
869 return $string;
875 * Function to raise the memory limit to a new value.
876 * Will respect the memory limit if it is higher, thus allowing
877 * settings in php.ini, apache conf or command line switches
878 * to override it
880 * The memory limit should be expressed with a string (eg:'64M')
882 * @param string $newlimit the new memory limit
883 * @return bool
885 function raise_memory_limit ($newlimit) {
887 if (empty($newlimit)) {
888 return false;
891 $cur = @ini_get('memory_limit');
892 if (empty($cur)) {
893 // if php is compiled without --enable-memory-limits
894 // apparently memory_limit is set to ''
895 $cur=0;
896 } else {
897 if ($cur == -1){
898 return true; // unlimited mem!
900 $cur = get_real_size($cur);
903 $new = get_real_size($newlimit);
904 if ($new > $cur) {
905 ini_set('memory_limit', $newlimit);
906 return true;
908 return false;
912 * Converts string to lowercase using most compatible function available.
914 * @param string $string The string to convert to all lowercase characters.
915 * @param string $encoding The encoding on the string.
916 * @return string
917 * @todo Add examples of calling this function with/without encoding types
919 function elgg_strtolower ($string, $encoding='') {
920 $textlib = textlib_get_instance();
921 return $textlib->strtolower($string, $encoding?$encoding:'utf-8');
927 * Given a simple array, this shuffles it up just like shuffle()
928 * Unlike PHP's shuffle() ihis function works on any machine.
930 * @param array $array The array to be rearranged
931 * @return array
933 function swapshuffle($array) {
935 srand ((double) microtime() * 10000000);
936 $last = count($array) - 1;
937 for ($i=0;$i<=$last;$i++) {
938 $from = rand(0,$last);
939 $curr = $array[$i];
940 $array[$i] = $array[$from];
941 $array[$from] = $curr;
943 return $array;
947 * Like {@link swapshuffle()}, but works on associative arrays
949 * @param array $array The associative array to be rearranged
950 * @return array
952 function swapshuffle_assoc($array) {
955 $newkeys = swapshuffle(array_keys($array));
956 foreach ($newkeys as $newkey) {
957 $newarray[$newkey] = $array[$newkey];
959 return $newarray;
963 * Given an arbitrary array, and a number of draws,
964 * this function returns an array with that amount
965 * of items. The indexes are retained.
967 * @param array $array ?
968 * @param ? $draws ?
969 * @return ?
970 * @todo Finish documenting this function
972 function draw_rand_array($array, $draws) {
973 srand ((double) microtime() * 10000000);
975 $return = array();
977 $last = count($array);
979 if ($draws > $last) {
980 $draws = $last;
983 while ($draws > 0) {
984 $last--;
986 $keys = array_keys($array);
987 $rand = rand(0, $last);
989 $return[$keys[$rand]] = $array[$keys[$rand]];
990 unset($array[$keys[$rand]]);
992 $draws--;
995 return $return;
1000 * Function to check the passed address is within the passed subnet
1002 * The parameter is a comma separated string of subnet definitions.
1003 * Subnet strings can be in one of two formats:
1004 * 1: xxx.xxx.xxx.xxx/xx
1005 * 2: xxx.xxx
1006 * Code for type 1 modified from user posted comments by mediator at
1007 * {@link http://au.php.net/manual/en/function.ip2long.php}
1009 * @param string $addr The address you are checking
1010 * @param string $subnetstr The string of subnet addresses
1011 * @return bool
1013 function address_in_subnet($addr, $subnetstr) {
1015 $subnets = explode(',', $subnetstr);
1016 $found = false;
1017 $addr = trim($addr);
1019 foreach ($subnets as $subnet) {
1020 $subnet = trim($subnet);
1021 if (strpos($subnet, '/') !== false) { /// type 1
1023 list($ip, $mask) = explode('/', $subnet);
1024 $mask = 0xffffffff << (32 - $mask);
1025 $found = ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
1027 } else { /// type 2
1028 $found = (strpos($addr, $subnet) === 0);
1031 if ($found) {
1032 break;
1036 return $found;
1040 * For outputting debugging info
1042 * @uses STDOUT
1043 * @param string $string ?
1044 * @param string $eol ?
1045 * @todo Finish documenting this function
1047 function mtrace($string, $eol="\n", $sleep=0) {
1049 if (defined('STDOUT')) {
1050 fwrite(STDOUT, $string.$eol);
1051 } else {
1052 echo $string . $eol;
1055 flush();
1057 //delay to keep message on user's screen in case of subsequent redirect
1058 if ($sleep) {
1059 sleep($sleep);
1063 //Replace 1 or more slashes or backslashes to 1 slash
1064 function cleardoubleslashes ($path) {
1065 return preg_replace('/(\/|\\\){1,}/','/',$path);
1071 * Returns most reliable client address
1073 * @return string The remote IP address
1075 function getremoteaddr() {
1076 if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
1077 return cleanremoteaddr($_SERVER['HTTP_CLIENT_IP']);
1079 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
1080 return cleanremoteaddr($_SERVER['HTTP_X_FORWARDED_FOR']);
1082 if (!empty($_SERVER['REMOTE_ADDR'])) {
1083 return cleanremoteaddr($_SERVER['REMOTE_ADDR']);
1085 return '';
1088 /**
1089 * Cleans a remote address ready to put into the log table
1091 function cleanremoteaddr($addr) {
1092 $originaladdr = $addr;
1093 $matches = array();
1094 // first get all things that look like IP addresses.
1095 if (!preg_match_all('/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/',$addr,$matches,PREG_SET_ORDER)) {
1096 return '';
1098 $goodmatches = array();
1099 $lanmatches = array();
1100 foreach ($matches as $match) {
1101 // print_r($match);
1102 // check to make sure it's not an internal address.
1103 // the following are reserved for private lans...
1104 // 10.0.0.0 - 10.255.255.255
1105 // 172.16.0.0 - 172.31.255.255
1106 // 192.168.0.0 - 192.168.255.255
1107 // 169.254.0.0 -169.254.255.255
1108 $bits = explode('.',$match[0]);
1109 if (count($bits) != 4) {
1110 // weird, preg match shouldn't give us it.
1111 continue;
1113 if (($bits[0] == 10)
1114 || ($bits[0] == 172 && $bits[1] >= 16 && $bits[1] <= 31)
1115 || ($bits[0] == 192 && $bits[1] == 168)
1116 || ($bits[0] == 169 && $bits[1] == 254)) {
1117 $lanmatches[] = $match[0];
1118 continue;
1120 // finally, it's ok
1121 $goodmatches[] = $match[0];
1123 if (!count($goodmatches)) {
1124 // perhaps we have a lan match, it's probably better to return that.
1125 if (!count($lanmatches)) {
1126 return '';
1127 } else {
1128 return array_pop($lanmatches);
1131 if (count($goodmatches) == 1) {
1132 return $goodmatches[0];
1134 error_log("NOTICE: cleanremoteaddr gives us something funny: $originaladdr had ".count($goodmatches)." matches");
1135 // we need to return something, so
1136 return array_pop($goodmatches);
1140 * html_entity_decode is only supported by php 4.3.0 and higher
1141 * so if it is not predefined, define it here
1143 * @param string $string ?
1144 * @return string
1145 * @todo Finish documenting this function
1147 if(!function_exists('html_entity_decode')) {
1148 function html_entity_decode($string, $quote_style = ENT_COMPAT, $charset = 'ISO-8859-1') {
1149 $trans_tbl = get_html_translation_table(HTML_ENTITIES, $quote_style);
1150 $trans_tbl = array_flip($trans_tbl);
1151 return strtr($string, $trans_tbl);
1156 * The clone keyword is only supported from PHP 5 onwards.
1157 * The behaviour of $obj2 = $obj1 differs fundamentally
1158 * between PHP 4 and PHP 5. In PHP 4 a copy of $obj1 was
1159 * created, in PHP 5 $obj1 is referenced. To create a copy
1160 * in PHP 5 the clone keyword was introduced. This function
1161 * simulates this behaviour for PHP < 5.0.0.
1162 * See also: http://mjtsai.com/blog/2004/07/15/php-5-object-references/
1164 * Modified 2005-09-29 by Eloy (from Julian Sedding proposal)
1165 * Found a better implementation (more checks and possibilities) from PEAR:
1166 * http://cvs.php.net/co.php/pear/PHP_Compat/Compat/Function/clone.php
1168 * @param object $obj
1169 * @return object
1171 if(!check_php_version('5.0.0')) {
1172 // the eval is needed to prevent PHP 5 from getting a parse error!
1173 eval('
1174 function clone($obj) {
1175 /// Sanity check
1176 if (!is_object($obj)) {
1177 user_error(\'clone() __clone method called on non-object\', E_USER_WARNING);
1178 return;
1181 /// Use serialize/unserialize trick to deep copy the object
1182 $obj = unserialize(serialize($obj));
1184 /// If there is a __clone method call it on the "new" class
1185 if (method_exists($obj, \'__clone\')) {
1186 $obj->__clone();
1189 return $obj;
1196 * microtime_diff
1198 * @param string $a ?
1199 * @param string $b ?
1200 * @return string
1201 * @todo Finish documenting this function
1203 function microtime_diff($a, $b) {
1204 list($a_dec, $a_sec) = explode(' ', $a);
1205 list($b_dec, $b_sec) = explode(' ', $b);
1206 return $b_sec - $a_sec + $b_dec - $a_dec;
1211 *** get_performance_info() pairs up with init_performance_info()
1212 *** loaded in setup.php. Returns an array with 'html' and 'txt'
1213 *** values ready for use, and each of the individual stats provided
1214 *** separately as well.
1217 function get_performance_info() {
1218 global $CFG, $PERF;
1220 $info = array();
1221 $info['html'] = ''; // holds userfriendly HTML representation
1222 $info['txt'] = me() . ' '; // holds log-friendly representation
1224 $info['realtime'] = microtime_diff($PERF->starttime, microtime());
1226 $info['html'] .= '<span class="timeused">'.$info['realtime'].' secs</span> ';
1227 $info['txt'] .= 'time: '.$info['realtime'].'s ';
1229 if (function_exists('memory_get_usage')) {
1230 $info['memory_total'] = memory_get_usage();
1231 $info['memory_growth'] = memory_get_usage() - $PERF->startmemory;
1232 $info['html'] .= '<span class="memoryused">RAM: '.display_size($info['memory_total']).'</span> ';
1233 $info['txt'] .= 'memory_total: '.$info['memory_total'].'B (' . display_size($info['memory_total']).') memory_growth: '.$info['memory_growth'].'B ('.display_size($info['memory_growth']).') ';
1236 $inc = get_included_files();
1237 //error_log(print_r($inc,1));
1238 $info['includecount'] = count($inc);
1239 $info['html'] .= '<span class="included">Included '.$info['includecount'].' files</span> ';
1240 $info['txt'] .= 'includecount: '.$info['includecount'].' ';
1242 if (!empty($PERF->dbqueries)) {
1243 $info['dbqueries'] = $PERF->dbqueries;
1244 $info['html'] .= '<span class="dbqueries">DB queries '.$info['dbqueries'].'</span> ';
1245 $info['txt'] .= 'dbqueries: '.$info['dbqueries'].' ';
1248 if (!empty($PERF->logwrites)) {
1249 $info['logwrites'] = $PERF->logwrites;
1250 $info['html'] .= '<span class="logwrites">Log writes '.$info['logwrites'].'</span> ';
1251 $info['txt'] .= 'logwrites: '.$info['logwrites'].' ';
1254 if (function_exists('posix_times')) {
1255 $ptimes = posix_times();
1256 if ($ptimes) {
1257 foreach ($ptimes as $key => $val) {
1258 $info[$key] = $ptimes[$key] - $PERF->startposixtimes[$key];
1260 $info['html'] .= "<span class=\"posixtimes\">ticks: $info[ticks] user: $info[utime] sys: $info[stime] cuser: $info[cutime] csys: $info[cstime]</span> ";
1261 $info['txt'] .= "ticks: $info[ticks] user: $info[utime] sys: $info[stime] cuser: $info[cutime] csys: $info[cstime] ";
1265 // Grab the load average for the last minute
1266 // /proc will only work under some linux configurations
1267 // while uptime is there under MacOSX/Darwin and other unices
1268 if (!@ini_get('open_basedir') && is_readable('/proc/loadavg') && $loadavg = @file('/proc/loadavg')) {
1269 list($server_load) = explode(' ', $loadavg[0]);
1270 unset($loadavg);
1271 } else if (!@ini_get('open_basedir') && function_exists('is_executable') && is_executable('/usr/bin/uptime') && $loadavg = `/usr/bin/uptime` ) {
1272 if (preg_match('/load averages?: (\d+[\.:]\d+)/', $loadavg, $matches)) {
1273 $server_load = $matches[1];
1274 } else {
1275 trigger_error('Could not parse uptime output!');
1278 if (!empty($server_load)) {
1279 $info['serverload'] = $server_load;
1280 $info['html'] .= '<span class="serverload">Load average: '.$info['serverload'].'</span> ';
1281 $info['txt'] .= 'serverload: '.$info['serverload'];
1285 $info['html'] = '<div class="performanceinfo">'.$info['html'].'</div>';
1286 return $info;
1289 if (!function_exists('file_get_contents')) {
1290 function file_get_contents($file) {
1291 $file = file($file);
1292 return $file ? implode('', $file) : false;
1300 * Detect if an object or a class contains a given property
1301 * will take an actual object or the name of a class
1302 * @param mix $obj Name of class or real object to test
1303 * @param string $property name of property to find
1304 * @return bool true if property exists
1306 function object_property_exists( $obj, $property ) {
1307 if (is_string( $obj )) {
1308 $properties = get_class_vars( $obj );
1310 else {
1311 $properties = get_object_vars( $obj );
1313 return array_key_exists( $property, $properties );
1318 * Add quotes to HTML characters
1320 * Returns $var with HTML characters (like "<", ">", etc.) properly quoted.
1321 * This function is very similar to {@link p()}
1323 * @param string $var the string potentially containing HTML characters
1324 * @return string
1326 function s($var) {
1327 if ($var == '0') { // for integer 0, boolean false, string '0'
1328 return '0';
1330 return preg_replace("/&amp;(#\d+);/iu", '&$1;', htmlspecialchars(stripslashes_safe($var), ENT_COMPAT, 'utf-8'));
1334 * Add quotes to HTML characters
1336 * Prints $var with HTML characters (like "<", ">", etc.) properly quoted.
1337 * This function is very similar to {@link s()}
1339 * @param string $var the string potentially containing HTML characters
1340 * @return string
1342 function p($var) {
1343 echo s($var);
1348 * Ensure that a variable is set
1350 * Return $var if it is defined, otherwise return $default,
1351 * This function is very similar to {@link optional_variable()}
1353 * @param mixed $var the variable which may be unset
1354 * @param mixed $default the value to return if $var is unset
1355 * @return mixed
1357 function nvl(&$var, $default='') {
1359 return isset($var) ? $var : $default;
1363 * Remove query string from url
1365 * Takes in a URL and returns it without the querystring portion
1367 * @param string $url the url which may have a query string attached
1368 * @return string
1370 function strip_querystring($url) {
1371 $textlib = textlib_get_instance();
1373 if ($commapos = $textlib->strpos($url, '?')) {
1374 return $textlib->substr($url, 0, $commapos);
1375 } else {
1376 return $url;
1381 * Returns the URL of the HTTP_REFERER, less the querystring portion
1382 * @return string
1384 function get_referer() {
1386 return strip_querystring(nvl($_SERVER['HTTP_REFERER']));
1391 * Returns the name of the current script, WITH the querystring portion.
1392 * this function is necessary because PHP_SELF and REQUEST_URI and SCRIPT_NAME
1393 * return different things depending on a lot of things like your OS, Web
1394 * server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.)
1395 * <b>NOTE:</b> This function returns false if the global variables needed are not set.
1397 * @return string
1399 function me() {
1401 if (!empty($_SERVER['REQUEST_URI'])) {
1402 return $_SERVER['REQUEST_URI'];
1404 } else if (!empty($_SERVER['PHP_SELF'])) {
1405 if (!empty($_SERVER['QUERY_STRING'])) {
1406 return $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
1408 return $_SERVER['PHP_SELF'];
1410 } else if (!empty($_SERVER['SCRIPT_NAME'])) {
1411 if (!empty($_SERVER['QUERY_STRING'])) {
1412 return $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
1414 return $_SERVER['SCRIPT_NAME'];
1416 } else if (!empty($_SERVER['URL'])) { // May help IIS (not well tested)
1417 if (!empty($_SERVER['QUERY_STRING'])) {
1418 return $_SERVER['URL'] .'?'. $_SERVER['QUERY_STRING'];
1420 return $_SERVER['URL'];
1422 } else {
1423 notify('Warning: Could not find any of these web server variables: $REQUEST_URI, $PHP_SELF, $SCRIPT_NAME or $URL');
1424 return false;
1429 * Like {@link me()} but returns a full URL
1430 * @see me()
1431 * @return string
1433 function qualified_me() {
1435 global $CFG;
1437 if (!empty($CFG->wwwroot)) {
1438 $url = parse_url($CFG->wwwroot);
1441 if (!empty($url['host'])) {
1442 $hostname = $url['host'];
1443 } else if (!empty($_SERVER['SERVER_NAME'])) {
1444 $hostname = $_SERVER['SERVER_NAME'];
1445 } else if (!empty($_ENV['SERVER_NAME'])) {
1446 $hostname = $_ENV['SERVER_NAME'];
1447 } else if (!empty($_SERVER['HTTP_HOST'])) {
1448 $hostname = $_SERVER['HTTP_HOST'];
1449 } else if (!empty($_ENV['HTTP_HOST'])) {
1450 $hostname = $_ENV['HTTP_HOST'];
1451 } else {
1452 notify('Warning: could not find the name of this server!');
1453 return false;
1456 if (!empty($url['port'])) {
1457 $hostname .= ':'.$url['port'];
1458 } else if (!empty($_SERVER['SERVER_PORT'])) {
1459 if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
1460 $hostname .= ':'.$_SERVER['SERVER_PORT'];
1464 if (isset($_SERVER['HTTPS'])) {
1465 $protocol = ($_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';
1466 } else if (isset($_SERVER['SERVER_PORT'])) { # Apache2 does not export $_SERVER['HTTPS']
1467 $protocol = ($_SERVER['SERVER_PORT'] == '443') ? 'https://' : 'http://';
1468 } else {
1469 $protocol = 'http://';
1472 $url_prefix = $protocol.$hostname;
1473 return $url_prefix . me();
1477 * Determine if a web referer is valid
1479 * Returns true if the referer is the same as the goodreferer. If
1480 * the referer to test is not specified, use {@link qualified_me()}.
1481 * If the admin has not set secure forms ($CFG->secureforms) then
1482 * this function returns true regardless of a match.
1484 * @uses $CFG
1485 * @param string $goodreferer the url to compare to referer
1486 * @return boolean
1488 function match_referer($goodreferer = '') {
1489 global $CFG;
1491 if (empty($CFG->secureforms)) { // Don't bother checking referer
1492 return true;
1495 if ($goodreferer == 'nomatch') { // Don't bother checking referer
1496 return true;
1499 if (empty($goodreferer)) {
1500 $goodreferer = qualified_me();
1503 $referer = get_referer();
1505 return (($referer == $goodreferer) or ($referer == $CFG->wwwroot) or ($referer == $CFG->wwwroot .'index.php'));
1509 * Determine if there is data waiting to be processed from a form
1511 * Used on most forms in Moodle to check for data
1512 * Returns the data as an object, if it's found.
1513 * This object can be used in foreach loops without
1514 * casting because it's cast to (array) automatically
1516 * Checks that submitted POST data exists, and also
1517 * checks the referer against the given url (it uses
1518 * the current page if none was specified.
1520 * @uses $CFG
1521 * @param string $url the url to compare to referer for secure forms
1522 * @return boolean
1524 function data_submitted($url='') {
1527 global $CFG;
1529 if (empty($_POST)) {
1530 return false;
1532 } else {
1533 if (match_referer($url)) {
1534 return (object)$_POST;
1535 } else {
1536 if ($CFG->debug > 10) {
1537 notice('The form did not come from this page! (referer = '. get_referer() .')');
1539 return false;
1545 * Moodle replacement for php stripslashes() function
1547 * The standard php stripslashes() removes ALL backslashes
1548 * even from strings - so C:\temp becomes C:temp - this isn't good.
1549 * This function should work as a fairly safe replacement
1550 * to be called on quoted AND unquoted strings (to be sure)
1552 * @param string the string to remove unsafe slashes from
1553 * @return string
1555 function stripslashes_safe($string) {
1557 $string = str_replace("\\'", "'", $string);
1558 $string = str_replace('\\"', '"', $string);
1559 $string = str_replace('\\\\', '\\', $string);
1560 return $string;
1564 * Recursive implementation of stripslashes()
1566 * This function will allow you to strip the slashes from a variable.
1567 * If the variable is an array or object, slashes will be stripped
1568 * from the items (or properties) it contains, even if they are arrays
1569 * or objects themselves.
1571 * @param mixed the variable to remove slashes from
1572 * @return mixed
1574 function stripslashes_recursive($var) {
1575 if(is_object($var)) {
1576 $properties = get_object_vars($var);
1577 foreach($properties as $property => $value) {
1578 $var->$property = stripslashes_recursive($value);
1581 else if(is_array($var)) {
1582 foreach($var as $property => $value) {
1583 $var[$property] = stripslashes_recursive($value);
1586 else if(is_string($var)) {
1587 $var = stripslashes($var);
1589 return $var;
1593 * This does a search and replace, ignoring case
1594 * This function is only used for versions of PHP older than version 5
1595 * which do not have a native version of this function.
1596 * Taken from the PHP manual, by bradhuizenga @ softhome.net
1598 * @param string $find the string to search for
1599 * @param string $replace the string to replace $find with
1600 * @param string $string the string to search through
1601 * return string
1603 if (!function_exists('str_ireplace')) { /// Only exists in PHP 5
1604 function str_ireplace($find, $replace, $string) {
1605 $textlib = textlib_get_instance();
1607 if (!is_array($find)) {
1608 $find = array($find);
1611 if(!is_array($replace)) {
1612 if (!is_array($find)) {
1613 $replace = array($replace);
1614 } else {
1615 // this will duplicate the string into an array the size of $find
1616 $c = count($find);
1617 $rString = $replace;
1618 unset($replace);
1619 for ($i = 0; $i < $c; $i++) {
1620 $replace[$i] = $rString;
1625 foreach ($find as $fKey => $fItem) {
1626 $between = explode($textlib->strtolower($fItem),$textlib->strtolower($string));
1627 $pos = 0;
1628 foreach($between as $bKey => $bItem) {
1629 $between[$bKey] = $textlib->substr($string,$pos,$textlib->strlen($bItem));
1630 $pos += $textlib->strlen($bItem) + $textlib->strlen($fItem);
1632 $string = implode($replace[$fKey],$between);
1634 return ($string);
1639 * Locate the position of a string in another string
1641 * This function is only used for versions of PHP older than version 5
1642 * which do not have a native version of this function.
1643 * Taken from the PHP manual, by dmarsh @ spscc.ctc.edu
1645 * @param string $haystack The string to be searched
1646 * @param string $needle The string to search for
1647 * @param int $offset The position in $haystack where the search should begin.
1649 if (!function_exists('stripos')) { /// Only exists in PHP 5
1650 function stripos($haystack, $needle, $offset=0) {
1651 $textlib = textlib_get_instance();
1653 return $textlib->strpos($textlib->strtoupper($haystack), $textlib->strtoupper($needle), $offset);
1659 * Returns true if the current version of PHP is greater that the specified one.
1661 * @param string $version The version of php being tested.
1662 * @return boolean
1663 * @todo Finish documenting this function
1665 function check_php_version($version='4.1.0') {
1666 return (version_compare(phpversion(), $version) >= 0);
1671 * Checks to see if is a browser matches the specified
1672 * brand and is equal or better version.
1674 * @uses $_SERVER
1675 * @param string $brand The browser identifier being tested
1676 * @param int $version The version of the browser
1677 * @return boolean
1678 * @todo Finish documenting this function
1680 function check_browser_version($brand='MSIE', $version=5.5) {
1681 $agent = $_SERVER['HTTP_USER_AGENT'];
1683 if (empty($agent)) {
1684 return false;
1687 switch ($brand) {
1689 case 'Gecko': /// Gecko based browsers
1691 if (substr_count($agent, 'Camino')) {
1692 // MacOS X Camino support
1693 $version = 20041110;
1696 // the proper string - Gecko/CCYYMMDD Vendor/Version
1697 // Faster version and work-a-round No IDN problem.
1698 if (preg_match("/Gecko\/([0-9]+)/i", $agent, $match)) {
1699 if ($match[1] > $version) {
1700 return true;
1703 break;
1706 case 'MSIE': /// Internet Explorer
1708 if (strpos($agent, 'Opera')) { // Reject Opera
1709 return false;
1711 $string = explode(';', $agent);
1712 if (!isset($string[1])) {
1713 return false;
1715 $string = explode(' ', trim($string[1]));
1716 if (!isset($string[0]) and !isset($string[1])) {
1717 return false;
1719 if ($string[0] == $brand and (float)$string[1] >= $version ) {
1720 return true;
1722 break;
1726 return false;
1731 * Set a variable's value depending on whether or not it already has a value.
1733 * If variable is set, set it to the set_value otherwise set it to the
1734 * unset_value. used to handle checkboxes when you are expecting them from
1735 * a form
1737 * @param mixed $var Passed in by reference. The variable to check.
1738 * @param mixed $set_value The value to set $var to if $var already has a value.
1739 * @param mixed $unset_value The value to set $var to if $var does not already have a value.
1741 function checked(&$var, $set_value = 1, $unset_value = 0) {
1743 if (empty($var)) {
1744 $var = $unset_value;
1745 } else {
1746 $var = $set_value;
1751 * Prints the word "checked" if a variable is true, otherwise prints nothing,
1752 * used for printing the word "checked" in a checkbox form element.
1754 * @param boolean $var Variable to be checked for true value
1755 * @param string $true_value Value to be printed if $var is true
1756 * @param string $false_value Value to be printed if $var is false
1758 function frmchecked(&$var, $true_value = 'checked', $false_value = '') {
1760 if ($var) {
1761 echo $true_value;
1762 } else {
1763 echo $false_value;
1768 * Prints a simple button to close a window
1770 function close_window_button($name='closewindow') {
1772 echo '<div style="text-align: center;">' . "\n";
1773 echo '<script type="text/javascript">' . "\n";
1774 echo '<!--' . "\n";
1775 echo "document.write('<form>');\n";
1776 echo "document.write('<input type=\"button\" onclick=\"self.close();\" value=\"".__gettext("Close this window")."\" />');\n";
1777 echo "document.write('<\/form>');\n";
1778 echo '-->' . "\n";
1779 echo '</script>' . "\n";
1780 echo '<noscript>' . "\n";
1781 print_string($name);
1782 echo '</noscript>' . "\n";
1783 echo '</div>' . "\n";
1787 * Try and close the current window immediately using Javascript
1789 function close_window($delay=0) {
1790 echo '<script language="JavaScript" type="text/javascript">'."\n";
1791 echo '<!--'."\n";
1792 if ($delay) {
1793 sleep($delay);
1795 echo 'self.close();'."\n";
1796 echo '-->'."\n";
1797 echo '</script>'."\n";
1798 exit;
1803 * Given an array of value, creates a popup menu to be part of a form
1804 * $options["value"]["label"]
1806 * @param type description
1807 * @todo Finish documenting this function
1809 function choose_from_menu ($options, $name, $selected='', $nothing='choose', $script='',
1810 $nothingvalue='0', $return=false, $disabled=false, $tabindex=0) {
1812 if ($nothing == 'choose') {
1813 $nothing = __gettext('Choose') .'...';
1816 $attributes = ($script) ? 'onchange="'. $script .'"' : '';
1817 if ($disabled) {
1818 $attributes .= ' disabled="disabled"';
1821 if ($tabindex) {
1822 $attributes .= ' tabindex="'.$tabindex.'"';
1825 $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
1826 if ($nothing) {
1827 $output .= ' <option value="'. $nothingvalue .'"'. "\n";
1828 if ($nothingvalue === $selected) {
1829 $output .= ' selected="selected"';
1831 $output .= '>'. $nothing .'</option>' . "\n";
1833 if (!empty($options)) {
1834 foreach ($options as $value => $label) {
1835 $output .= ' <option value="'. $value .'"';
1836 if ($value === $selected) {
1837 $output .= ' selected="selected"';
1839 if ($label === '') {
1840 $output .= '>'. $value .'</option>' . "\n";
1841 } else {
1842 $output .= '>'. $label .'</option>' . "\n";
1846 $output .= '</select>' . "\n";
1848 if ($return) {
1849 return $output;
1850 } else {
1851 echo $output;
1856 * Just like choose_from_menu, but takes a nested array (2 levels) and makes a dropdown menu
1857 * including option headings with the first level.
1859 function choose_from_menu_nested($options,$name,$selected='',$nothing='choose',$script = '',
1860 $nothingvalue=0,$return=false,$disabled=false,$tabindex=0) {
1862 if ($nothing == 'choose') {
1863 $nothing = __gettext('Choose') .'...';
1866 $attributes = ($script) ? 'onchange="'. $script .'"' : '';
1867 if ($disabled) {
1868 $attributes .= ' disabled="disabled"';
1871 if ($tabindex) {
1872 $attributes .= ' tabindex="'.$tabindex.'"';
1875 $output = '<select id="menu'.$name.'" name="'. $name .'" '. $attributes .'>' . "\n";
1876 if ($nothing) {
1877 $output .= ' <option value="'. $nothingvalue .'"'. "\n";
1878 if ($nothingvalue === $selected) {
1879 $output .= ' selected="selected"';
1881 $output .= '>'. $nothing .'</option>' . "\n";
1883 if (!empty($options)) {
1884 foreach ($options as $section => $values) {
1885 $output .= ' <optgroup label="'.$section.'">'."\n";
1886 foreach ($values as $value => $label) {
1887 $output .= ' <option value="'. $value .'"';
1888 if ($value === $selected) {
1889 $output .= ' selected="selected"';
1891 if ($label === '') {
1892 $output .= '>'. $value .'</option>' . "\n";
1893 } else {
1894 $output .= '>'. $label .'</option>' . "\n";
1897 $output .= ' </optgroup>'."\n";
1900 $output .= '</select>' . "\n";
1902 if ($return) {
1903 return $output;
1904 } else {
1905 echo $output;
1911 * Given an array of values, creates a group of radio buttons to be part of a form
1913 * @param array $options An array of value-label pairs for the radio group (values as keys)
1914 * @param string $name Name of the radiogroup (unique in the form)
1915 * @param string $checked The value that is already checked
1917 function choose_from_radio ($options, $name, $checked='') {
1919 static $idcounter = 0;
1921 if (!$name) {
1922 $name = 'unnamed';
1925 $output = '<span class="radiogroup '.$name."\">\n";
1927 if (!empty($options)) {
1928 $currentradio = 0;
1929 foreach ($options as $value => $label) {
1930 $htmlid = 'auto-rb'.sprintf('%04d', ++$idcounter);
1931 $output .= ' <span class="radioelement '.$name.' rb'.$currentradio."\">";
1932 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="radio" value="'.$value.'"';
1933 if ($value == $checked) {
1934 $output .= ' checked="checked"';
1936 if ($label === '') {
1937 $output .= ' /> <label for="'.$htmlid.'">'. $value .'</label></span>' . "\n";
1938 } else {
1939 $output .= ' /> <label for="'.$htmlid.'">'. $label .'</label></span>' . "\n";
1941 $currentradio = ($currentradio + 1) % 2;
1945 $output .= '</span>' . "\n";
1947 echo $output;
1950 /** Display an standard html checkbox with an optional label
1952 * @param string $name The name of the checkbox
1953 * @param string $value The valus that the checkbox will pass when checked
1954 * @param boolean $checked The flag to tell the checkbox initial state
1955 * @param string $label The label to be showed near the checkbox
1956 * @param string $alt The info to be inserted in the alt tag
1958 function print_checkbox ($name, $value, $checked = true, $label = '', $alt = '', $script='',$return=false) {
1960 static $idcounter = 0;
1962 if (!$name) {
1963 $name = 'unnamed';
1966 if (!$alt) {
1967 $alt = 'checkbox';
1970 if ($checked) {
1971 $strchecked = ' checked="checked"';
1974 $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
1975 $output = '<span class="checkbox '.$name."\">";
1976 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="checkbox" value="'.$value.'" alt="'.$alt.'"'.$strchecked.' '.((!empty($script)) ? ' onclick="'.$script.'" ' : '').' />';
1977 if(!empty($label)) {
1978 $output .= ' <label for="'.$htmlid.'">'.$label.'</label>';
1980 $output .= '</span>'."\n";
1982 if (empty($return)) {
1983 echo $output;
1984 } else {
1985 return $output;
1990 /** Display an standard html text field with an optional label
1992 * @param string $name The name of the text field
1993 * @param string $value The value of the text field
1994 * @param string $label The label to be showed near the text field
1995 * @param string $alt The info to be inserted in the alt tag
1997 function print_textfield ($name, $value, $alt = '',$size=50,$maxlength= 0,$return=false) {
1999 static $idcounter = 0;
2001 if (empty($name)) {
2002 $name = 'unnamed';
2005 if (empty($alt)) {
2006 $alt = 'textfield';
2009 if (!empty($maxlength)) {
2010 $maxlength = ' maxlength="'.$maxlength.'" ';
2013 $htmlid = 'auto-cb'.sprintf('%04d', ++$idcounter);
2014 $output = '<span class="textfield '.$name."\">";
2015 $output .= '<input name="'.$name.'" id="'.$htmlid.'" type="text" value="'.$value.'" size="'.$size.'" '.$maxlength.' alt="'.$alt.'" />';
2017 $output .= '</span>'."\n";
2019 if (empty($return)) {
2020 echo $output;
2021 } else {
2022 return $output;
2029 * Validates an email to make sure it makes sense and adheres
2030 * to the email filter if it's set.
2032 * @param string $address The email address to validate.
2033 * @return boolean
2035 function validate_email($address) {
2037 global $CFG;
2039 if (ereg('^[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+'.
2040 '@'.
2041 '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'.
2042 '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$',
2043 $address)) {
2045 if ($CFG->emailfilter != "") {
2046 $domain = substr($address,strpos($address,"@")+1);
2047 if (substr_count($CFG->emailfilter, $domain) == 0) {
2048 return false;
2052 return true;
2054 } else {
2055 return false;
2060 * Check for bad characters ?
2062 * @param string $string ?
2063 * @param int $allowdots ?
2064 * @todo Finish documenting this function - more detail needed in description as well as details on arguments
2066 function detect_munged_arguments($string, $allowdots=1) {
2067 if (substr_count($string, '..') > $allowdots) { // Sometimes we allow dots in references
2068 return true;
2070 if (ereg('[\|\`]', $string)) { // check for other bad characters
2071 return true;
2073 if (empty($string) or $string == '/') {
2074 return true;
2077 return false;
2083 * Just returns an array of text formats suitable for a popup menu
2085 * @uses FORMAT_MOODLE
2086 * @uses FORMAT_HTML
2087 * @uses FORMAT_PLAIN
2088 * @uses FORMAT_MARKDOWN
2089 * @return array
2091 function format_text_menu() {
2093 return array (FORMAT_MOODLE => __gettext('Elgg auto-format'),
2094 FORMAT_HTML => __gettext('HTML format'),
2095 FORMAT_PLAIN => __gettext('Plain text format'),
2096 FORMAT_MARKDOWN => __gettext('Markdown format'));
2100 * Given text in a variety of format codings, this function returns
2101 * the text as safe HTML.
2103 * @uses $CFG
2104 * @uses FORMAT_MOODLE
2105 * @uses FORMAT_HTML
2106 * @uses FORMAT_PLAIN
2107 * @uses FORMAT_WIKI
2108 * @uses FORMAT_MARKDOWN
2109 * @param string $text The text to be formatted. This is raw text originally from user input.
2110 * @param int $format Identifier of the text format to be used
2111 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
2112 * @param array $options ?
2113 * @param int $courseid ?
2114 * @return string
2115 * @todo Finish documenting this function
2117 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL ) {
2119 global $CFG, $course;
2121 if (!isset($options->noclean)) {
2122 $options->noclean=false;
2124 if (!isset($options->smiley)) {
2125 $options->smiley=true;
2127 if (!isset($options->filter)) {
2128 $options->filter=true;
2130 if (!isset($options->para)) {
2131 $options->para=true;
2133 if (!isset($options->newlines)) {
2134 $options->newlines=true;
2137 if (empty($courseid)) {
2138 if (!empty($course->id)) { // An ugly hack for better compatibility
2139 $courseid = $course->id;
2144 if (!empty($CFG->cachetext)) {
2145 $time = time() - $CFG->cachetext;
2146 $md5key = md5($text.'-'.$courseid.$options->noclean.$options->smiley.$options->filter.$options->para.$options->newlines);
2147 if ($cacheitem = get_record_select('cache_text', "md5key = '$md5key' AND timemodified > '$time'")) {
2148 return $cacheitem->formattedtext;
2151 */ // DISABLED - there is no cache_text - Penny
2153 $CFG->currenttextiscacheable = true; // Default status - can be changed by any filter
2155 switch ($format) {
2156 case FORMAT_HTML:
2158 if (!empty($options->smiley)) {
2159 replace_smilies($text);
2162 if (!isset($options->noclean)) {
2163 $text = clean_text($text, $format, !empty($options->cleanuserfile));
2166 if (!empty($options->filter)) {
2167 $text = filter_text($text, $courseid);
2169 break;
2171 case FORMAT_PLAIN:
2172 $text = s($text);
2173 $text = rebuildnolinktag($text);
2174 $text = str_replace(' ', '&nbsp; ', $text);
2175 $text = nl2br($text);
2176 break;
2178 case FORMAT_WIKI:
2179 // this format is deprecated
2180 $text = '<p>NOTICE: Wiki-like formatting has been removed from Moodle. You should not be seeing
2181 this message as all texts should have been converted to Markdown format instead.
2182 Please post a bug report to http://moodle.org/bugs with information about where you
2183 saw this message.</p>'.s($text);
2184 break;
2186 case FORMAT_MARKDOWN:
2187 $text = markdown_to_html($text);
2188 if (!empty($options->smiley)) {
2189 replace_smilies($text);
2191 if (empty($options->noclean)) {
2192 $text = clean_text($text, $format);
2194 if (!empty($options->filter)) {
2195 $text = filter_text($text, $courseid);
2197 break;
2199 default: // FORMAT_MOODLE or anything else
2200 $text = text_to_html($text, $options->smiley, $options->para, $options->newlines);
2201 if (empty($options->noclean)) {
2202 $text = clean_text($text, $format);
2204 if (!empty($options->filter)) {
2205 $text = filter_text($text, $courseid);
2207 break;
2210 if (!empty($CFG->cachetext) and $CFG->currenttextiscacheable) {
2211 $newrecord->md5key = $md5key;
2212 $newrecord->formattedtext = $text;
2213 $newrecord->timemodified = time();
2214 @insert_record('cache_text', $newrecord);
2217 return $text;
2220 /** Converts the text format from the value to the 'internal'
2221 * name or vice versa. $key can either be the value or the name
2222 * and you get the other back.
2224 * @param mixed int 0-4 or string one of 'moodle','html','plain','markdown'
2225 * @return mixed as above but the other way around!
2227 function text_format_name( $key ) {
2228 $lookup = array();
2229 $lookup[FORMAT_MOODLE] = 'moodle';
2230 $lookup[FORMAT_HTML] = 'html';
2231 $lookup[FORMAT_PLAIN] = 'plain';
2232 $lookup[FORMAT_MARKDOWN] = 'markdown';
2233 $value = "error";
2234 if (!is_numeric($key)) {
2235 $key = strtolower( $key );
2236 $value = array_search( $key, $lookup );
2238 else {
2239 if (isset( $lookup[$key] )) {
2240 $value = $lookup[ $key ];
2243 return $value;
2246 /** Given a simple string, this function returns the string
2247 * processed by enabled filters if $CFG->filterall is enabled
2249 * @param string $string The string to be filtered.
2250 * @param boolean $striplinks To strip any link in the result text.
2251 * @param int $courseid Current course as filters can, potentially, use it
2252 * @return string
2254 function format_string ($string, $striplinks = false, $courseid=NULL ) {
2256 global $CFG, $course;
2258 //We'll use a in-memory cache here to speed up repeated strings
2259 static $strcache;
2261 //Calculate md5
2262 $md5 = md5($string.'<+>'.$striplinks);
2264 //Fetch from cache if possible
2265 if(isset($strcache[$md5])) {
2266 return $strcache[$md5];
2269 if (empty($courseid)) {
2270 if (!empty($course->id)) { // An ugly hack for better compatibility
2271 $courseid = $course->id; // (copied from format_text)
2275 if (!empty($CFG->filterall)) {
2276 $string = filter_text($string, $courseid);
2279 if ($striplinks) { //strip links in string
2280 $string = preg_replace('/(<a[^>]+?>)(.+?)(<\/a>)/is','$2',$string);
2283 //Store to cache
2284 $strcache[$md5] = $string;
2286 return $string;
2290 * Given text in a variety of format codings, this function returns
2291 * the text as plain text suitable for plain email.
2293 * @uses FORMAT_MOODLE
2294 * @uses FORMAT_HTML
2295 * @uses FORMAT_PLAIN
2296 * @uses FORMAT_WIKI
2297 * @uses FORMAT_MARKDOWN
2298 * @param string $text The text to be formatted. This is raw text originally from user input.
2299 * @param int $format Identifier of the text format to be used
2300 * (FORMAT_MOODLE, FORMAT_HTML, FORMAT_PLAIN, FORMAT_WIKI, FORMAT_MARKDOWN)
2301 * @return string
2303 function format_text_email($text, $format) {
2305 switch ($format) {
2307 case FORMAT_PLAIN:
2308 return $text;
2309 break;
2311 case FORMAT_WIKI:
2312 $text = wiki_to_html($text);
2313 /// This expression turns links into something nice in a text format. (Russell Jungwirth)
2314 /// From: http://php.net/manual/en/function.eregi-replace.php and simplified
2315 $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
2316 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
2317 break;
2319 case FORMAT_HTML:
2320 return html_to_text($text);
2321 break;
2323 case FORMAT_MOODLE:
2324 case FORMAT_MARKDOWN:
2325 default:
2326 $text = eregi_replace('(<a [^<]*href=["|\']?([^ "\']*)["|\']?[^>]*>([^<]*)</a>)','\\3 [ \\2 ]', $text);
2327 return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
2328 break;
2333 * This function takes a string and examines it for HTML tags.
2334 * If tags are detected it passes the string to a helper function {@link cleanAttributes2()}
2335 * which checks for attributes and filters them for malicious content
2336 * 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie
2338 * @param string $str The string to be examined for html tags
2339 * @return string
2341 function cleanAttributes($str){
2342 $result = preg_replace_callback(
2343 '%(<[^>]*(>|$)|>)%m', #search for html tags
2344 "cleanAttributes2",
2345 $str
2347 return $result;
2351 * This function takes a string with an html tag and strips out any unallowed
2352 * protocols e.g. javascript:
2353 * It calls ancillary functions in kses which are prefixed by kses
2354 * 17/08/2004 :: Eamon DOT Costello AT dcu DOT ie
2356 * @param array $htmlArray An array from {@link cleanAttributes()}, containing in its 1st
2357 * element the html to be cleared
2358 * @return string
2360 function cleanAttributes2($htmlArray){
2362 global $CFG, $ALLOWED_PROTOCOLS;
2363 require_once($CFG->libdir .'/kses.php');
2365 $htmlTag = $htmlArray[1];
2366 if (substr($htmlTag, 0, 1) != '<') {
2367 return '&gt;'; //a single character ">" detected
2369 if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?$%', $htmlTag, $matches)) {
2370 return ''; // It's seriously malformed
2372 $slash = trim($matches[1]); //trailing xhtml slash
2373 $elem = $matches[2]; //the element name
2374 $attrlist = $matches[3]; // the list of attributes as a string
2376 $attrArray = kses_hair($attrlist, $ALLOWED_PROTOCOLS);
2378 $attStr = '';
2379 foreach ($attrArray as $arreach) {
2380 $attStr .= ' '.strtolower($arreach['name']).'="'.$arreach['value'].'" ';
2383 // Remove last space from attribute list
2384 $attStr = rtrim($attStr);
2386 $xhtml_slash = '';
2387 if (preg_match('%/\s*$%', $attrlist)) {
2388 $xhtml_slash = ' /';
2390 return '<'. $slash . $elem . $attStr . $xhtml_slash .'>';
2394 * Replaces all known smileys in the text with image equivalents
2396 * @uses $CFG
2397 * @param string $text Passed by reference. The string to search for smily strings.
2398 * @return string
2400 function replace_smilies(&$text) {
2402 global $CFG;
2404 /// this builds the mapping array only once
2405 static $runonce = false;
2406 static $e = array();
2407 static $img = array();
2408 static $emoticons = array(
2409 ':-)' => 'smiley',
2410 ':)' => 'smiley',
2411 ':-D' => 'biggrin',
2412 ';-)' => 'wink',
2413 ':-/' => 'mixed',
2414 'V-.' => 'thoughtful',
2415 ':-P' => 'tongueout',
2416 'B-)' => 'cool',
2417 '^-)' => 'approve',
2418 '8-)' => 'wideeyes',
2419 ':o)' => 'clown',
2420 ':-(' => 'sad',
2421 ':(' => 'sad',
2422 '8-.' => 'shy',
2423 ':-I' => 'blush',
2424 ':-X' => 'kiss',
2425 '8-o' => 'surprise',
2426 'P-|' => 'blackeye',
2427 '8-[' => 'angry',
2428 'xx-P' => 'dead',
2429 '|-.' => 'sleepy',
2430 '}-]' => 'evil',
2433 if ($runonce == false) { /// After the first time this is not run again
2434 foreach ($emoticons as $emoticon => $image){
2435 $alttext = get_string($image, 'pix');
2437 $e[] = $emoticon;
2438 $img[] = '<img alt="'. $alttext .'" width="15" height="15" src="'. $CFG->pixpath .'/s/'. $image .'.gif" />';
2440 $runonce = true;
2443 // Exclude from transformations all the code inside <script> tags
2444 // Needed to solve Bug 1185. Thanks to jouse 2001 detecting it. :-)
2445 // Based on code from glossary fiter by Williams Castillo.
2446 // - Eloy
2448 // Detect all the <script> zones to take out
2449 $excludes = array();
2450 preg_match_all('/<script language(.+?)<\/script>/is',$text,$list_of_excludes);
2452 // Take out all the <script> zones from text
2453 foreach (array_unique($list_of_excludes[0]) as $key=>$value) {
2454 $excludes['<+'.$key.'+>'] = $value;
2456 if ($excludes) {
2457 $text = str_replace($excludes,array_keys($excludes),$text);
2460 /// this is the meat of the code - this is run every time
2461 $text = str_replace($e, $img, $text);
2463 // Recover all the <script> zones to text
2464 if ($excludes) {
2465 $text = str_replace(array_keys($excludes),$excludes,$text);
2470 * Given plain text, makes it into HTML as nicely as possible.
2471 * May contain HTML tags already
2473 * @uses $CFG
2474 * @param string $text The string to convert.
2475 * @param boolean $smiley Convert any smiley characters to smiley images?
2476 * @param boolean $para If true then the returned string will be wrapped in paragraph tags
2477 * @param boolean $newlines If true then lines newline breaks will be converted to HTML newline breaks.
2478 * @return string
2481 function text_to_html($text, $smiley=true, $para=true, $newlines=true) {
2484 global $CFG;
2486 /// Remove any whitespace that may be between HTML tags
2487 $text = eregi_replace(">([[:space:]]+)<", "><", $text);
2489 /// Remove any returns that precede or follow HTML tags
2490 $text = eregi_replace("([\n\r])<", " <", $text);
2491 $text = eregi_replace(">([\n\r])", "> ", $text);
2493 convert_urls_into_links($text);
2495 /// Make returns into HTML newlines.
2496 if ($newlines) {
2497 $text = nl2br($text);
2500 /// Turn smileys into images.
2501 if ($smiley) {
2502 replace_smilies($text);
2505 /// Wrap the whole thing in a paragraph tag if required
2506 if ($para) {
2507 return '<p>'.$text.'</p>';
2508 } else {
2509 return $text;
2514 * Given Markdown formatted text, make it into XHTML using external function
2516 * @uses $CFG
2517 * @param string $text The markdown formatted text to be converted.
2518 * @return string Converted text
2520 function markdown_to_html($text) {
2521 global $CFG;
2523 require_once($CFG->libdir .'/markdown.php');
2525 return Markdown($text);
2529 * Given HTML text, make it into plain text using external function
2531 * @uses $CFG
2532 * @param string $html The text to be converted.
2533 * @return string
2535 function html_to_text($html) {
2537 global $CFG;
2539 require_once($CFG->libdir .'/html2text.php');
2541 return html2text($html);
2545 * Given some text this function converts any URLs it finds into HTML links
2547 * @param string $text Passed in by reference. The string to be searched for urls.
2549 function convert_urls_into_links(&$text) {
2550 /// Make lone URLs into links. eg http://moodle.com/
2551 $text = eregi_replace("([[:space:]]|^|\(|\[)([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])",
2552 "\\1<a href=\"\\2://\\3\\4\" target=\"_blank\">\\2://\\3\\4</a>", $text);
2554 /// eg www.moodle.com
2555 $text = eregi_replace("([[:space:]]|^|\(|\[)www\.([^[:space:]]*)([[:alnum:]#?/&=])",
2556 "\\1<a href=\"http://www.\\2\\3\" target=\"_blank\">www.\\2\\3</a>", $text);
2562 * This function will highlight search words in a given string
2563 * It cares about HTML and will not ruin links. It's best to use
2564 * this function after performing any conversions to HTML.
2565 * Function found here: http://forums.devshed.com/t67822/scdaa2d1c3d4bacb4671d075ad41f0854.html
2567 * @param string $needle The string to search for
2568 * @param string $haystack The string to search for $needle in
2569 * @param int $case ?
2570 * @return string
2571 * @todo Finish documenting this function
2573 function highlight($needle, $haystack, $case=0,
2574 $left_string='<span class="highlight">', $right_string='</span>') {
2575 if (empty($needle)) {
2576 return $haystack;
2579 //$list_of_words = eregi_replace("[^-a-zA-Z0-9&.']", " ", $needle); // bug 3101
2580 $list_of_words = $needle;
2581 $list_array = explode(' ', $list_of_words);
2582 for ($i=0; $i<sizeof($list_array); $i++) {
2583 if (strlen($list_array[$i]) == 1) {
2584 $list_array[$i] = '';
2587 $list_of_words = implode(' ', $list_array);
2588 $list_of_words_cp = $list_of_words;
2589 $final = array();
2590 preg_match_all('/<(.+?)>/is',$haystack,$list_of_words);
2592 foreach (array_unique($list_of_words[0]) as $key=>$value) {
2593 $final['<|'.$key.'|>'] = $value;
2596 $haystack = str_replace($final,array_keys($final),$haystack);
2597 $list_of_words_cp = eregi_replace(' +', '|', $list_of_words_cp);
2599 if ($list_of_words_cp{0}=='|') {
2600 $list_of_words_cp{0} = '';
2602 if ($list_of_words_cp{strlen($list_of_words_cp)-1}=='|') {
2603 $list_of_words_cp{strlen($list_of_words_cp)-1}='';
2606 $list_of_words_cp = trim($list_of_words_cp);
2608 if ($list_of_words_cp) {
2610 $list_of_words_cp = "(". $list_of_words_cp .")";
2612 if (!$case){
2613 $haystack = eregi_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
2614 } else {
2615 $haystack = ereg_replace("$list_of_words_cp", "$left_string"."\\1"."$right_string", $haystack);
2618 $haystack = str_replace(array_keys($final),$final,$haystack);
2620 return $haystack;
2624 * This function will highlight instances of $needle in $haystack
2625 * It's faster that the above function and doesn't care about
2626 * HTML or anything.
2628 * @param string $needle The string to search for
2629 * @param string $haystack The string to search for $needle in
2630 * @return string
2632 function highlightfast($needle, $haystack) {
2633 $textlib = textlib_get_instance();
2635 $parts = explode($textlib->strtolower($needle), $textlib->strtolower($haystack));
2637 $pos = 0;
2639 foreach ($parts as $key => $part) {
2640 $parts[$key] = $textlib->substr($haystack, $pos, $textlib->strlen($part));
2641 $pos += $textlib->strlen($part);
2643 $parts[$key] .= '<span class="highlight">'.$textlib->substr($haystack, $pos, $textlib->strlen($needle)).'</span>';
2644 $pos += $textlib->strlen($needle);
2647 return (join('', $parts));
2651 * Print a link to continue on to another page.
2653 * @uses $CFG
2654 * @param string $link The url to create a link to.
2656 function print_continue($link) {
2658 global $CFG;
2660 if (!$link) {
2661 $link = $_SERVER['HTTP_REFERER'];
2664 echo '<div class="continuebutton">';
2665 print_single_button($link, NULL, __gettext('Continue'), 'post', $CFG->framename);
2666 echo '</div>'."\n";
2670 * Print a message in a standard themed box.
2672 * @param string $message ?
2673 * @param string $align ?
2674 * @param string $width ?
2675 * @param string $color ?
2676 * @param int $padding ?
2677 * @param string $class ?
2678 * @todo Finish documenting this function
2680 function print_simple_box($message, $align='', $width='', $color='', $padding=5, $class='generalbox', $id='') {
2681 print_simple_box_start($align, $width, $color, $padding, $class, $id);
2682 echo stripslashes_safe($message);
2683 print_simple_box_end();
2687 * Print the top portion of a standard themed box.
2689 * @param string $align ?
2690 * @param string $width ?
2691 * @param string $color ?
2692 * @param int $padding ?
2693 * @param string $class ?
2694 * @todo Finish documenting this function
2696 function print_simple_box_start($align='', $width='', $color='', $padding=5, $class='generalbox', $id='') {
2698 if ($color) {
2699 $color = 'bgcolor="'. $color .'"';
2701 if ($align) {
2702 $align = 'align="'. $align .'"';
2704 if ($width) {
2705 $width = 'width="'. $width .'"';
2707 if ($id) {
2708 $id = 'id="'. $id .'"';
2710 $class = trim($class);
2711 $classcontent = preg_replace('/(\s+|$)/','content ', $class);
2713 echo "<table $align $width $id class=\"$class\" border=\"0\" cellpadding=\"$padding\" cellspacing=\"0\">".
2714 "<tr><td $color class=\"$classcontent\">";
2718 * Print the end portion of a standard themed box.
2720 function print_simple_box_end() {
2721 echo '</td></tr></table>';
2725 * Print a self contained form with a single submit button.
2727 * @param string $link ?
2728 * @param array $options ?
2729 * @param string $label ?
2730 * @param string $method ?
2731 * @todo Finish documenting this function
2733 function print_single_button($link, $options, $label='OK', $method='get', $target='_self') {
2734 echo '<div class="singlebutton">';
2735 echo '<form action="'. $link .'" method="'. $method .'" target="'.$target.'">';
2736 if ($options) {
2737 foreach ($options as $name => $value) {
2738 echo '<input type="hidden" name="'. $name .'" value="'. $value .'" />';
2741 echo '<input type="submit" value="'. $label .'" /></form></div>';
2745 * Print a png image.
2747 * @param string $url ?
2748 * @param int $sizex ?
2749 * @param int $sizey ?
2750 * @param boolean $returnstring ?
2751 * @param string $parameters ?
2752 * @todo Finish documenting this function
2754 function print_png($url, $sizex, $sizey, $returnstring, $parameters='alt=""') {
2755 global $CFG;
2756 static $recentIE;
2758 if (!isset($recentIE)) {
2759 $recentIE = check_browser_version('MSIE', '5.0');
2762 if ($recentIE) { // work around the HORRIBLE bug IE has with alpha transparencies
2763 $output .= '<img src="'. $CFG->pixpath .'/spacer.gif" width="'. $sizex .'" height="'. $sizey .'"'.
2764 ' border="0" class="png" style="width: '. $sizex .'px; height: '. $sizey .'px; '.
2765 ' filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='.
2766 "'$url', sizingMethod='scale') ".
2767 ' '. $parameters .' />';
2768 } else {
2769 $output .= '<img src="'. $url .'" border="0" width="'. $sizex .'" height="'. $sizey .'" '.
2770 ' '. $parameters .' />';
2773 if ($returnstring) {
2774 return $output;
2775 } else {
2776 echo $output;
2781 * Print a nicely formatted table.
2783 * @param array $table is an object with several properties.
2784 * <ul<li>$table->head - An array of heading names.
2785 * <li>$table->align - An array of column alignments
2786 * <li>$table->size - An array of column sizes
2787 * <li>$table->wrap - An array of "nowrap"s or nothing
2788 * <li>$table->data[] - An array of arrays containing the data.
2789 * <li>$table->width - A percentage of the page
2790 * <li>$table->cellpadding - Padding on each cell
2791 * <li>$table->cellspacing - Spacing between cells
2792 * </ul>
2793 * @return boolean
2794 * @todo Finish documenting this function
2796 function print_table($table) {
2798 if (isset($table->align)) {
2799 foreach ($table->align as $key => $aa) {
2800 if ($aa) {
2801 $align[$key] = ' align="'. $aa .'"';
2802 } else {
2803 $align[$key] = '';
2807 if (isset($table->size)) {
2808 foreach ($table->size as $key => $ss) {
2809 if ($ss) {
2810 $size[$key] = ' width="'. $ss .'"';
2811 } else {
2812 $size[$key] = '';
2816 if (isset($table->wrap)) {
2817 foreach ($table->wrap as $key => $ww) {
2818 if ($ww) {
2819 $wrap[$key] = ' nowrap="nowrap" ';
2820 } else {
2821 $wrap[$key] = '';
2826 if (empty($table->width)) {
2827 $table->width = '80%';
2830 if (empty($table->cellpadding)) {
2831 $table->cellpadding = '5';
2834 if (empty($table->cellspacing)) {
2835 $table->cellspacing = '1';
2838 if (empty($table->class)) {
2839 $table->class = 'generaltable';
2842 $tableid = empty($table->id) ? '' : 'id="'.$table->id.'"';
2844 print_simple_box_start('center', $table->width, '#ffffff', 0);
2845 echo '<table width="100%" border="0" align="center" ';
2846 echo " cellpadding=\"$table->cellpadding\" cellspacing=\"$table->cellspacing\" class=\"$table->class\" $tableid>\n";
2848 $countcols = 0;
2850 if (!empty($table->head)) {
2851 $countcols = count($table->head);
2852 echo '<tr>';
2853 foreach ($table->head as $key => $heading) {
2855 if (!isset($size[$key])) {
2856 $size[$key] = '';
2858 if (!isset($align[$key])) {
2859 $align[$key] = '';
2861 echo '<th valign="top" '. $align[$key].$size[$key] .' nowrap="nowrap" class="header c'.$key.'">'. $heading .'</th>';
2863 echo '</tr>'."\n";
2866 if (!empty($table->data)) {
2867 $oddeven = 1;
2868 foreach ($table->data as $key => $row) {
2869 $oddeven = $oddeven ? 0 : 1;
2870 echo '<tr class="r'.$oddeven.'">'."\n";
2871 if ($row == 'hr' and $countcols) {
2872 echo '<td colspan="'. $countcols .'"><div class="tabledivider"></div></td>';
2873 } else { /// it's a normal row of data
2874 foreach ($row as $key => $item) {
2875 if (!isset($size[$key])) {
2876 $size[$key] = '';
2878 if (!isset($align[$key])) {
2879 $align[$key] = '';
2881 if (!isset($wrap[$key])) {
2882 $wrap[$key] = '';
2884 echo '<td '. $align[$key].$size[$key].$wrap[$key] .' class="cell c'.$key.'">'. $item .'</td>';
2887 echo '</tr>'."\n";
2890 echo '</table>'."\n";
2891 print_simple_box_end();
2893 return true;
2897 * Prints form items with the names $day, $month and $year
2899 * @param int $day ?
2900 * @param int $month ?
2901 * @param int $year ?
2902 * @param int $currenttime A default timestamp in GMT
2903 * @todo Finish documenting this function
2905 function print_date_selector($day, $month, $year, $currenttime=0) {
2907 if (!$currenttime) {
2908 $currenttime = time();
2910 $currentdate = usergetdate($currenttime);
2912 for ($i=1; $i<=31; $i++) {
2913 $days[$i] = $i;
2915 for ($i=1; $i<=12; $i++) {
2916 $months[$i] = userdate(gmmktime(12,0,0,$i,1,2000), "%B");
2918 for ($i=2000; $i<=2010; $i++) {
2919 $years[$i] = $i;
2921 choose_from_menu($days, $day, $currentdate['mday'], '');
2922 choose_from_menu($months, $month, $currentdate['mon'], '');
2923 choose_from_menu($years, $year, $currentdate['year'], '');
2927 *Prints form items with the names $hour and $minute
2929 * @param ? $hour ?
2930 * @param ? $minute ?
2931 * @param $currenttime A default timestamp in GMT
2932 * @param int $step ?
2933 * @todo Finish documenting this function
2935 function print_time_selector($hour, $minute, $currenttime=0, $step=5 ,$return=false) {
2937 if (!$currenttime) {
2938 $currenttime = time();
2940 $currentdate = usergetdate($currenttime);
2941 if ($step != 1) {
2942 $currentdate['minutes'] = ceil($currentdate['minutes']/$step)*$step;
2944 for ($i=0; $i<=23; $i++) {
2945 $hours[$i] = sprintf("%02d",$i);
2947 for ($i=0; $i<=59; $i+=$step) {
2948 $minutes[$i] = sprintf("%02d",$i);
2951 return choose_from_menu($hours, $hour, $currentdate['hours'], '','','',$return)
2952 .choose_from_menu($minutes, $minute, $currentdate['minutes'], '','','',$return);
2956 *Returns an A link tag
2958 * @param $url
2959 * @param $str
2960 * @param $extrattr Extra attribs for the A tag
2961 * @return string
2963 function a_href($url, $str, $extrattr='') {
2964 $str = htmlspecialchars($str, ENT_COMPAT, 'utf-8');
2965 return "<a href=\"$url\" $extrattr >$str</a>";
2969 *Returns an A link tag using __gettext()
2971 * @param $url
2972 * @param $str for __gettext()
2973 * @param $extrattr Extra attribs for the A tag
2974 * @return string
2976 function a_hrefg($url, $str, $extrattr='') {
2977 $str = htmlspecialchars(__gettext($str), ENT_COMPAT, 'utf-8');
2978 return "<a href=\"$url\" $extrattr >$str</a>";
2982 * Print an error page displaying an error message.
2983 * Old method, don't call directly in new code - use print_error instead.
2986 * @uses $SESSION
2987 * @uses $CFG
2988 * @param string $message The message to display to the user about the error.
2989 * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
2991 function error ($message, $link='') {
2992 global $CFG, $SESSION;
2994 @header('HTTP/1.0 404 Not Found');
2996 print_header(__gettext('Error'));
2997 echo '<br />';
2999 $message = clean_text($message); // In case nasties are in here
3001 print_simple_box($message, 'center', '', '#FFBBBB', 5, 'errorbox');
3003 if (!$link) {
3004 if ( !empty($SESSION->fromurl) ) {
3005 $link = $SESSION->fromurl;
3006 unset($SESSION->fromurl);
3007 } else {
3008 $link = $CFG->wwwroot;
3011 print_continue($link);
3012 print_footer();
3013 for ($i=0;$i<512;$i++) { // Padding to help IE work with 404
3014 echo ' ';
3016 die;
3020 * Print an error page displaying an error message. New method - use this for new code.
3022 * @uses $SESSION
3023 * @uses $CFG
3024 * @param string $string The name of the string from error.php to print
3025 * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page.
3027 function print_error ($string, $link='') {
3029 $string = get_string($string, 'error');
3030 error($string, $link);
3036 * Print a message and exit.
3038 * @uses $CFG
3039 * @param string $message ?
3040 * @param string $link ?
3041 * @todo Finish documenting this function
3043 function notice ($message, $link='') {
3044 global $CFG;
3046 $message = clean_text($message);
3047 $link = clean_text($link);
3049 if (!$link) {
3050 if (!empty($_SERVER['HTTP_REFERER'])) {
3051 $link = $_SERVER['HTTP_REFERER'];
3052 } else {
3053 $link = $CFG->wwwroot;
3057 echo '<br />';
3058 print_simple_box($message, 'center', '50%', '', '20', 'noticebox');
3059 print_continue($link);
3060 print_footer(get_site());
3061 die;
3065 * Print a message along with "Yes" and "No" links for the user to continue.
3067 * @param string $message The text to display
3068 * @param string $linkyes The link to take the user to if they choose "Yes"
3069 * @param string $linkno The link to take the user to if they choose "No"
3071 function notice_yesno ($message, $linkyes, $linkno) {
3073 global $CFG;
3075 $message = clean_text($message);
3076 $linkyes = clean_text($linkyes);
3077 $linkno = clean_text($linkno);
3079 print_simple_box_start('center', '60%', '', 5, 'noticebox', 'notice');
3080 echo '<p align="center">'. $message .'</p>';
3081 echo '<table align="center" cellpadding="20"><tr><td>';
3082 print_single_button($linkyes, NULL, __gettext('Yes'), 'post', $CFG->framename);
3083 echo '</td><td>';
3084 print_single_button($linkno, NULL, __gettext('No'), 'post', $CFG->framename);
3085 echo '</td></tr></table>';
3086 print_simple_box_end();
3090 * Redirects the user to another page, after printing a notice
3092 * @param string $url The url to take the user to
3093 * @param string $message The text message to display to the user about the redirect, if any
3094 * @param string $delay How long before refreshing to the new page at $url?
3095 * @todo '&' needs to be encoded into '&amp;' for XHTML compliance,
3096 * however, this is not true for javascript. Therefore we
3097 * first decode all entities in $url (since we cannot rely on)
3098 * the correct input) and then encode for where it's needed
3099 * echo "<script type='text/javascript'>alert('Redirect $url');</script>";
3101 function redirect($url, $message='', $delay='0') {
3103 $url = clean_text($url);
3104 $message = clean_text($message);
3106 $url = htmlspecialchars_decode($url, ENT_COMPAT); // for php < 5.1.0 this is defined in elgglib.php
3107 $url = str_replace(array("\n", "\r"), '', $url); // some more cleaning
3108 $encodedurl = htmlspecialchars($url, ENT_COMPAT, 'utf-8');
3110 if (empty($message)) {
3111 echo '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />';
3112 echo '<script type="text/javascript">'. "\n" .'<!--'. "\n". "location.replace('$url');". "\n". '//-->'. "\n". '</script>'; // To cope with Mozilla bug
3113 } else {
3115 if (empty($delay)) {
3116 $delay = 3; // There's no point having a message with no delay
3118 print_header('', '', '', '', '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />');
3119 echo '<div style="text-align: center;">';
3120 echo '<p>'. $message .'</p>';
3121 //called from setup.php, so gettext module hasn't been loaded yet
3122 if (function_exists("__gettext")) {
3123 $continue = __gettext('Continue');
3124 } else {
3125 $continue = 'Continue';
3127 echo '<p>( <a href="'. $encodedurl .'">'. $continue .'</a> )</p>';
3128 echo '</div>';
3131 <script type="text/javascript">
3132 <!--
3134 function redirect() {
3135 document.location.replace('<?php echo $url ?>');
3137 setTimeout("redirect()", <?php echo ($delay * 1000) ?>);
3139 </script>
3140 <?php
3143 die;
3147 * Print a bold message in an optional color.
3149 * @param string $message The message to print out
3150 * @param string $style Optional style to display message text in
3151 * @param string $align Alignment option
3153 function notify ($message, $style='notifyproblem', $align='center') {
3155 if ($style == 'green') {
3156 $style = 'notifysuccess'; // backward compatible with old color system
3159 $message = clean_text($message);
3161 echo '<div class="'.$style.'" align="'. $align .'">'. $message .'</div>'."<br />\n";
3166 * This function is used to rebuild the <nolink> tag because some formats (PLAIN and WIKI)
3167 * will transform it to html entities
3169 * @param string $text Text to search for nolink tag in
3170 * @return string
3172 function rebuildnolinktag($text) {
3174 $text = preg_replace('/&lt;(\/*nolink)&gt;/i','<$1>',$text);
3176 return $text;
3181 * Adjust the list of allowed tags based on $CFG->allowobjectembed and user roles (admin)
3183 function adjust_allowed_tags() {
3185 global $CFG, $ALLOWED_TAGS;
3187 if (!empty($CFG->allowobjectembed)) {
3188 $ALLOWED_TAGS .= '<embed><object><param>';
3194 * This function makes the return value of ini_get consistent if you are
3195 * setting server directives through the .htaccess file in apache.
3196 * Current behavior for value set from php.ini On = 1, Off = [blank]
3197 * Current behavior for value set from .htaccess On = On, Off = Off
3198 * Contributed by jdell @ unr.edu
3200 * @param string $ini_get_arg ?
3201 * @return boolean
3202 * @todo Finish documenting this function
3204 function ini_get_bool($ini_get_arg) {
3205 $temp = ini_get($ini_get_arg);
3207 if ($temp == '1' or strtolower($temp) == 'on') {
3208 return true;
3210 return false;
3214 * Generate and return a random string of the specified length.
3216 * @param int $length The length of the string to be created.
3217 * @return string
3219 function random_string ($length=15) {
3220 $pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
3221 $pool .= 'abcdefghijklmnopqrstuvwxyz';
3222 $pool .= '0123456789';
3223 $poollen = strlen($pool);
3224 mt_srand ((double) microtime() * 1000000);
3225 $string = '';
3226 for ($i = 0; $i < $length; $i++) {
3227 $string .= substr($pool, (mt_rand()%($poollen)), 1);
3229 return $string;
3233 * Fills user information
3235 function init_user_var($user) {
3237 global $CFG;
3239 $user->loggedin = true;
3240 $user->site = $CFG->wwwroot; // for added security, store the site in the session
3241 $user->sesskey = random_string(10);
3242 $user->sessionIP = md5(getremoteaddr()); // Store the current IP in the session
3243 // backwards compatibility (TODO this will have to go eventually)
3244 fill_legacy_user_session($user);
3245 return $user;
3251 * Authentication Function
3252 * @param string $username
3253 * @param string $password plaintext password
3254 * @return boolean
3256 function authenticate_account($username,$password) {
3258 global $CFG,$USER;
3260 if (empty($CFG->auth)) {
3261 $CFG->auth = 'internal';
3263 if (!file_exists($CFG->dirroot . 'auth/' . $CFG->auth . '/lib.php')) {
3264 $CFG->auth = 'internal';
3267 require_once($CFG->dirroot . 'auth/' . $CFG->auth . '/lib.php');
3269 // Module authentication function
3270 $function = $CFG->auth.'_authenticate_user_login';
3272 // Does the function exist
3273 if (!function_exists($function)) {
3274 print 'Error: function '.$function.' not found in auth/' . $CFG->auth . '/lib.php';
3275 return false;
3278 if (!$user = $function($username,$password)) {
3279 return false;
3282 $ok = true;
3283 if (user_flag_get("banned", $user->ident)) { // this needs to change.
3284 $ok = false;
3285 $user = false;
3286 $USER = false;
3287 global $messages;
3288 $messages[] = __gettext("You have been banned from the system!");
3289 return false;
3292 // Set Persistent Cookie
3293 $rememberme = optional_param('remember',0);
3294 if (!empty($rememberme)) {
3295 remember_login($user->ident);
3299 $USER = init_user_var($user);
3300 return $ok;
3304 * Attempts to get login from a cookie
3306 function cookied_login() {
3307 global $USER;
3308 if((!empty($_COOKIE[AUTH_COOKIE])) && $ticket = md5($_COOKIE[AUTH_COOKIE])) {
3309 if ($user = get_record('users','code',$ticket)) {
3310 $USER = $user;
3312 /*** TODO: Create Proper Abstraction Interface - don't use file binding -- ugh ***/
3313 if (!user_flag_get("banned",$USER->ident)) {
3314 $USER = init_user_var($USER);
3315 return true;
3316 } else {
3317 global $messages;
3318 $messages[] = __gettext("You have been banned from the system!");
3319 return false;
3326 * elgg doesn't have a 'login' page yet, but it will so this can stay here for now
3328 function require_login() {
3329 global $USER, $SESSION,$FULLME;
3331 // Check to see if there's a persistent cookie
3332 cookied_login();
3334 // First check that the user is logged in to the site.
3335 if (empty($USER->loggedin) || $USER->site != $CFG->wwwroot) {
3336 $SESSION->wantsurl = $FULLME;
3337 if (!empty($_SERVER['HTTP_REFERER'])) {
3338 $SESSION->fromurl = $_SERVER['HTTP_REFERER'];
3340 $USER = NULL;
3341 redirect($CFG->wwwroot .'login/index.php');
3342 exit;
3345 // Make sure current IP matches the one for this session (if required)
3346 if (!empty($CFG->tracksessionip)) {
3347 if ($USER->sessionIP != md5(getremoteaddr())) {
3348 error(__gettext('Sorry, but your IP number seems to have changed from when you first logged in. This security feature prevents crackers stealing your identity while logged in to this site. Normal users should not be seeing this message - please ask the site administrator for help.'));
3352 // Make sure the USER has a sesskey set up. Used for checking script parameters.
3353 sesskey();
3355 return true;
3359 function remember_login($id) {
3360 global $CFG;
3361 $id = (int) $id;
3362 if(!$id) return 0;
3364 // Double MD5
3365 if (!defined("SECRET_SALT")) {
3366 define("SECRET_SALT", "SECRET_SALT");
3368 $ticket = md5(SECRET_SALT . $id . time());
3369 $md5ticket = md5($ticket);
3371 // Update MD5 of authticket
3372 $user->code = $md5ticket;
3373 $user->ident = $id;
3374 update_record('users',$user);
3376 setcookie(AUTH_COOKIE, $ticket, time()+AUTH_COOKIE_LENGTH, $CFG->cookiepath);
3377 global $messages;
3378 $messages[] = __gettext("The system will remember you and automatically log you in next time.");
3380 return 1;
3384 * Returns whether the user is logged in or not;
3385 * if not logged in, checks for persistent cookie
3386 * @return boolean
3388 function isloggedin() {
3389 global $USER;
3390 if (empty($USER->ident) && empty($USER->loggedin)) {
3391 cookied_login();
3393 return (!empty($USER->ident) && !empty($USER->loggedin));
3396 function get_string($s) {
3397 return __gettext($s);
3400 function print_header() {
3401 $args = func_get_args();
3402 echo $args[0];
3405 function print_footer() {
3406 $args = func_get_args();
3407 echo $args[0];
3410 function clean_text($text, $format=FORMAT_MOODLE) {
3412 global $ALLOWED_TAGS;
3414 switch ($format) {
3415 case FORMAT_PLAIN:
3416 return $text;
3418 default:
3420 /// Remove tags that are not allowed
3421 $text = strip_tags($text, $ALLOWED_TAGS);
3423 /// Add some breaks into long strings of &nbsp;
3424 $text = preg_replace('/((&nbsp;){10})&nbsp;/', '\\1 ', $text);
3426 /// Clean up embedded scripts and , using kses
3427 $text = cleanAttributes($text);
3429 /// Remove script events
3430 $text = eregi_replace("([^a-z])language([[:space:]]*)=", "\\1Xlanguage=", $text);
3431 $text = eregi_replace("([^a-z])on([a-z]+)([[:space:]]*)=", "\\1Xon\\2=", $text);
3433 return $text;
3438 * Set a key in global configuration
3440 * Set a key/value pair in both this session's {@link $CFG} global variable
3441 * and in the 'config' database table for future sessions.
3443 * Can also be used to update keys for plugin-scoped configs in config_plugin table.
3444 * In that case it doesn't affect $CFG.
3446 * @param string $name the key to set
3447 * @param string $value the value to set
3448 * @uses $CFG
3449 * @return bool
3451 function set_config($name, $value) {
3452 /// No need for get_config because they are usually always available in $CFG
3454 global $CFG;
3456 $CFG->$name = $value; // So it's defined for this invocation at least
3458 if (get_field('datalists', 'name', 'name', $name)) {
3459 return set_field('datalists', 'value', $value, 'name', $name);
3460 } else {
3461 $config->name = $name;
3462 $config->value = $value;
3463 return insert_record('datalists', $config);
3468 * Get configuration values from the global config table
3469 * or the config_plugins table.
3471 * If called with no parameters it will do the right thing
3472 * generating $CFG safely from the database without overwriting
3473 * existing values.
3475 * @param string $name
3476 * @uses $CFG
3477 * @return hash-like object or single value
3480 function get_config($name=NULL) {
3482 global $CFG;
3484 if (!empty($name)) { // the user is asking for a specific value
3485 return get_record('datalists', 'name', $name);
3488 // this was originally in setup.php
3489 if ($configs = get_records('datalists')) {
3490 $localcfg = (array)$CFG;
3491 foreach ($configs as $config) {
3492 if (!isset($localcfg[$config->name])) {
3493 $localcfg[$config->name] = $config->value;
3494 } else {
3495 if ($localcfg[$config->name] != $config->value ) {
3496 // complain if the DB has a different
3497 // value than config.php does
3498 error_log("\$CFG->{$config->name} in config.php ({$localcfg[$config->name]}) overrides database setting ({$config->value})");
3503 $localcfg = (object)$localcfg;
3504 return $localcfg;
3505 } else {
3506 // preserve $CFG if DB returns nothing or error
3507 return $CFG;
3512 function guest_user() {
3513 $user = new stdClass();
3515 $user->ident = 0;
3516 $user->username = '';
3517 $user->name = '';
3518 $user->email = '';
3519 $user->icon = -1;
3520 $user->icon_quota = 0;
3522 return $user;
3525 function fill_legacy_user_session($user = NULL) {
3527 if (!$user || $user == NULL) {
3528 $user = guest_user();
3531 /// Fills up all legacy user session data
3532 /// This function provides backward compatibility
3533 $_SESSION['userid'] = (int) $user->ident;
3534 $_SESSION['username'] = $user->username;
3535 $_SESSION['name'] = stripslashes($user->name);
3536 $_SESSION['email'] = stripslashes($user->email);
3537 $iconid = (int) $user->icon;
3538 if ($iconid == -1) {
3539 $_SESSION['icon'] = "default.png";
3540 } else {
3541 if ($icon = get_record('icons','ident',$iconid)) {
3542 $_SESSION['icon'] = $icon->filename;
3543 } else {
3544 $_SESSION['icon'] = "default.png";
3547 $_SESSION['icon_quota'] = (int) $user->icon_quota;
3551 * Replace function htmlspecialchars_decode()
3553 * @category PHP
3554 * @package PHP_Compat
3555 * @link http://php.net/function.htmlspecialchars_decode
3556 * @author Aidan Lister <aidan@php.net>
3557 * @version $Revision: 1.3 $
3558 * @since PHP 5.1.0
3559 * @require PHP 4.0.0 (user_error)
3561 if (!function_exists('htmlspecialchars_decode')) {
3562 function htmlspecialchars_decode($string, $quote_style = null)
3564 // Sanity check
3565 if (!is_scalar($string)) {
3566 user_error('htmlspecialchars_decode() expects parameter 1 to be string, ' .
3567 gettype($string) . ' given', E_USER_WARNING);
3568 return;
3571 if (!is_int($quote_style) && $quote_style !== null) {
3572 user_error('htmlspecialchars_decode() expects parameter 2 to be integer, ' .
3573 gettype($quote_style) . ' given', E_USER_WARNING);
3574 return;
3577 // Init
3578 $from = array('&amp;', '&lt;', '&gt;');
3579 $to = array('&', '<', '>');
3581 // The function does not behave as documented
3582 // This matches the actual behaviour of the function
3583 if ($quote_style & ENT_COMPAT || $quote_style & ENT_QUOTES) {
3584 $from[] = '&quot;';
3585 $to[] = '"';
3587 $from[] = '&#039;';
3588 $to[] = "'";
3591 return str_replace($from, $to, $string);
3597 * Returns the maximum size for uploading files.
3599 * There are five possible upload limits:
3600 * 1. in Apache using LimitRequestBody (no way of checking or changing this)
3601 * 2. in php.ini for 'upload_max_filesize' (can not be changed inside PHP)
3602 * 3. in .htaccess for 'upload_max_filesize' (can not be changed inside PHP)
3603 * 4. in php.ini for 'post_max_size' (can not be changed inside PHP)
3604 * 5. by the limitations on the current situation (eg file quota)
3606 * The last one is passed to this function as an argument (in bytes).
3607 * Anything defined as 0 is ignored.
3608 * The smallest of all the non-zero numbers is returned.
3610 * @param int $maxbytes Current maxbytes (in bytes)
3611 * @return int The maximum size for uploading files.
3612 * @todo Finish documenting this function
3614 function get_max_upload_file_size($maxbytes=0) {
3615 global $CFG;
3617 if (! $filesize = ini_get('upload_max_filesize')) {
3618 if (!empty($CFG->absmaxuploadsize)) {
3619 $filesize = $CFG->absmaxuploadsize;
3620 } else {
3621 $filesize = '5M';
3624 $minimumsize = get_real_size($filesize);
3626 if ($postsize = ini_get('post_max_size')) {
3627 $postsize = get_real_size($postsize);
3628 if ($postsize < $minimumsize) {
3629 $minimumsize = $postsize;
3633 if ($maxbytes and $maxbytes < $minimumsize) {
3634 $minimumsize = $maxbytes;
3637 return $minimumsize;
3640 function remove_dir($dir, $content_only=false) {
3641 // if content_only=true then delete all but
3642 // the directory itself
3644 $handle = opendir($dir);
3645 while (false!==($item = readdir($handle))) {
3646 if($item != '.' && $item != '..') {
3647 if(is_dir($dir.'/'.$item)) {
3648 remove_dir($dir.'/'.$item);
3649 }else{
3650 unlink($dir.'/'.$item);
3654 closedir($handle);
3655 if ($content_only) {
3656 return true;
3658 return rmdir($dir);
3662 * Function to check if a directory exists and, optionally, create it
3664 function check_dir_exists($dir,$create=false) {
3666 global $CFG;
3668 $status = true;
3669 if(!is_dir($dir)) {
3670 if (!$create) {
3671 $status = false;
3672 } else {
3673 umask(0000);
3674 $status = mkdir ($dir,$CFG->directorypermissions);
3677 return $status;
3680 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3681 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3682 //This functions are used to copy any file or directory ($from_file)
3683 //to a new file or directory ($to_file). It works recursively and
3684 //mantains file perms.
3685 //I've copied it from: http://www.php.net/manual/en/function.copy.php
3686 //Little modifications done
3688 function copy_file ($from_file,$to_file) {
3690 global $CFG;
3692 if (is_file($from_file)) {
3693 umask(0000);
3694 if (copy($from_file,$to_file)) {
3695 chmod($to_file,$CFG->filepermissions);
3696 return true;
3698 return false;
3700 else if (is_dir($from_file)) {
3701 return copy_dir($from_file,$to_file);
3703 else{
3704 return false;
3708 function copy_dir($from_file,$to_file) {
3710 global $CFG;
3712 if (!is_dir($to_file)) {
3713 umask(0000);
3714 $status = mkdir($to_file,$CFG->directorypermissions);
3716 $dir = opendir($from_file);
3717 while ($file=readdir($dir)) {
3718 if ($file=="." || $file=="..") {
3719 continue;
3721 $status = copy_file ("$from_file/$file","$to_file/$file");
3723 closedir($dir);
3724 return $status;
3729 * Zip an array of files/dirs to a destination zip file
3730 * Both parameters must be FULL paths to the files/dirs
3731 * @return boolean
3733 function zip_files ($originalfiles, $destination) {
3735 global $CFG;
3737 //Extract everything from destination
3738 $path_parts = pathinfo(cleardoubleslashes($destination));
3739 $destpath = $path_parts["dirname"]; //The path of the zip file
3740 $destfilename = $path_parts["basename"]; //The name of the zip file
3741 $extension = $path_parts["extension"]; //The extension of the file
3743 //If no file, error
3744 if (empty($destfilename)) {
3745 return false;
3748 //If no extension, add it
3749 if (empty($extension)) {
3750 $extension = 'zip';
3751 $destfilename = $destfilename.'.'.$extension;
3754 //Check destination path exists
3755 if (!is_dir($destpath)) {
3756 return false;
3759 //Check destination path is writable. TODO!!
3761 //Clean destination filename
3762 $destfilename = clean_filename($destfilename);
3764 //Now check and prepare every file
3765 $files = array();
3766 $origpath = NULL;
3768 foreach ($originalfiles as $file) { //Iterate over each file
3769 //Check for every file
3770 $tempfile = cleardoubleslashes($file); // no doubleslashes!
3771 //Calculate the base path for all files if it isn't set
3772 if ($origpath === NULL) {
3773 $origpath = rtrim(cleardoubleslashes(dirname($tempfile)), "/");
3775 //See if the file is readable
3776 if (!is_readable($tempfile)) { //Is readable
3777 continue;
3779 //See if the file/dir is in the same directory than the rest
3780 if (rtrim(cleardoubleslashes(dirname($tempfile)), "/") != $origpath) {
3781 continue;
3783 //Add the file to the array
3784 $files[] = $tempfile;
3787 //Everything is ready:
3788 // -$origpath is the path where ALL the files to be compressed reside (dir).
3789 // -$destpath is the destination path where the zip file will go (dir).
3790 // -$files is an array of files/dirs to compress (fullpath)
3791 // -$destfilename is the name of the zip file (without path)
3793 //print_object($files); //Debug
3795 if (empty($CFG->zip)) { // Use built-in php-based zip function
3797 include_once("$CFG->libdir/pclzip/pclzip.lib.php");
3798 $archive = new PclZip(cleardoubleslashes("$destpath/$destfilename"));
3799 if (($list = $archive->create($files, PCLZIP_OPT_REMOVE_PATH,$origpath) == 0)) {
3800 notice($archive->errorInfo(true));
3801 return false;
3804 } else { // Use external zip program
3806 $filestozip = "";
3807 foreach ($files as $filetozip) {
3808 $filestozip .= escapeshellarg(basename($filetozip));
3809 $filestozip .= " ";
3811 //Construct the command
3812 $separator = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? ' &' : ' ;';
3813 $command = 'cd '.escapeshellarg($origpath).$separator.
3814 escapeshellarg($CFG->zip).' -r '.
3815 escapeshellarg(cleardoubleslashes("$destpath/$destfilename")).' '.$filestozip;
3816 //All converted to backslashes in WIN
3817 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
3818 $command = str_replace('/','\\',$command);
3820 Exec($command);
3822 return true;
3826 * Unzip one zip file to a destination dir
3827 * Both parameters must be FULL paths
3828 * If destination isn't specified, it will be the
3829 * SAME directory where the zip file resides.
3830 * @return boolean
3832 function unzip_file ($zipfile, $destination = '', $showstatus = true) {
3834 global $CFG;
3836 //Extract everything from zipfile
3837 $path_parts = pathinfo(cleardoubleslashes($zipfile));
3838 $zippath = $path_parts["dirname"]; //The path of the zip file
3839 $zipfilename = $path_parts["basename"]; //The name of the zip file
3840 $extension = $path_parts["extension"]; //The extension of the file
3842 //If no file, error
3843 if (empty($zipfilename)) {
3844 return false;
3847 //If no extension, error
3848 if (empty($extension)) {
3849 return false;
3852 //Clear $zipfile
3853 $zipfile = cleardoubleslashes($zipfile);
3855 //Check zipfile exists
3856 if (!file_exists($zipfile)) {
3857 return false;
3860 //If no destination, passed let's go with the same directory
3861 if (empty($destination)) {
3862 $destination = $zippath;
3865 //Clear $destination
3866 $destpath = rtrim(cleardoubleslashes($destination), "/");
3868 //Check destination path exists
3869 if (!is_dir($destpath)) {
3870 return false;
3873 //Check destination path is writable. TODO!!
3875 //Everything is ready:
3876 // -$zippath is the path where the zip file resides (dir)
3877 // -$zipfilename is the name of the zip file (without path)
3878 // -$destpath is the destination path where the zip file will uncompressed (dir)
3880 if (empty($CFG->unzip)) { // Use built-in php-based unzip function
3882 include_once("$CFG->libdir/pclzip/pclzip.lib.php");
3883 $archive = new PclZip(cleardoubleslashes("$zippath/$zipfilename"));
3884 if (!$list = $archive->extract(PCLZIP_OPT_PATH, $destpath,
3885 PCLZIP_CB_PRE_EXTRACT, 'unzip_cleanfilename')) {
3886 notice($archive->errorInfo(true));
3887 return false;
3890 } else { // Use external unzip program
3892 $separator = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? ' &' : ' ;';
3893 $redirection = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? '' : ' 2>&1';
3895 $command = 'cd '.escapeshellarg($zippath).$separator.
3896 escapeshellarg($CFG->unzip).' -o '.
3897 escapeshellarg(cleardoubleslashes("$zippath/$zipfilename")).' -d '.
3898 escapeshellarg($destpath).$redirection;
3899 //All converted to backslashes in WIN
3900 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
3901 $command = str_replace('/','\\',$command);
3903 Exec($command,$list);
3906 //Display some info about the unzip execution
3907 if ($showstatus) {
3908 unzip_show_status($list,$destpath);
3911 return true;
3915 * This function is used as callback in unzip_file() function
3916 * to clean illegal characters for given platform and to prevent directory traversal.
3917 * Produces the same result as info-zip unzip.
3919 function unzip_cleanfilename ($p_event, &$p_header) {
3920 $p_header['filename'] = ereg_replace('[[:cntrl:]]', '', $p_header['filename']); //strip control chars first!
3921 $p_header['filename'] = ereg_replace('\.\.+', '', $p_header['filename']); //directory traversal protection
3922 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
3923 $p_header['filename'] = ereg_replace('[:*"?<>|]', '_', $p_header['filename']); //replace illegal chars
3924 $p_header['filename'] = ereg_replace('^([a-zA-Z])_', '\1:', $p_header['filename']); //repair drive letter
3925 } else {
3926 //Add filtering for other systems here
3927 // BSD: none (tested)
3928 // Linux: ??
3929 // MacosX: ??
3931 $p_header['filename'] = cleardoubleslashes($p_header['filename']); //normalize the slashes/backslashes
3932 return 1;
3937 * This function shows the results of the unzip execution
3938 * depending of the value of the $CFG->zip, results will be
3939 * text or an array of files.
3941 function unzip_show_status ($list,$removepath) {
3943 global $CFG;
3945 if (empty($CFG->unzip)) { // Use built-in php-based zip function
3946 $strname = get_string("name");
3947 $strsize = get_string("size");
3948 $strmodified = get_string("modified");
3949 $strstatus = get_string("status");
3950 echo "<table cellpadding=\"4\" cellspacing=\"2\" border=\"0\" width=\"640\">";
3951 echo "<tr><th class=\"header\" align=\"left\">$strname</th>";
3952 echo "<th class=\"header\" align=\"right\">$strsize</th>";
3953 echo "<th class=\"header\" align=\"right\">$strmodified</th>";
3954 echo "<th class=\"header\" align=\"right\">$strstatus</th></tr>";
3955 foreach ($list as $item) {
3956 echo "<tr>";
3957 $item['filename'] = str_replace(cleardoubleslashes($removepath).'/', "", $item['filename']);
3958 print_cell("left", $item['filename']);
3959 if (! $item['folder']) {
3960 print_cell("right", display_size($item['size']));
3961 } else {
3962 echo "<td>&nbsp;</td>";
3964 $filedate = userdate($item['mtime'], get_string("strftimedatetime"));
3965 print_cell("right", $filedate);
3966 print_cell("right", $item['status']);
3967 echo "</tr>";
3969 echo "</table>";
3971 } else { // Use external zip program
3972 print_simple_box_start("center");
3973 echo "<pre>";
3974 foreach ($list as $item) {
3975 echo str_replace(cleardoubleslashes($removepath.'/'), '', $item).'<br />';
3977 echo "</pre>";
3978 print_simple_box_end();
3984 * @return boolean
3986 function isadmin($userid=0) {
3987 global $USER;
3989 static $admins, $nonadmins;
3991 if (!isset($admins)) {
3992 $admins = array();
3993 $nonadmins = array();
3996 if (empty($userid)) {
3997 if (empty($USER)) { // maybe not logged in
3998 return false;
3999 } else {
4000 $userid = (int) $USER->ident;
4004 if (in_array($userid, $admins)) {
4005 return true;
4006 } else if (in_array($userid, $nonadmins)) {
4007 return false;
4008 } else if (user_flag_get('admin', $userid)) {
4009 $admins[] = $userid;
4010 return true;
4011 } else {
4012 $nonadmins[] = $userid;
4013 return false;
4020 function get_admins() {
4021 global $CFG;
4022 return get_records_sql('SELECT u.* FROM '.$CFG->prefix.'users u
4023 JOIN '.$CFG->prefix.'user_flags uf ON u.ident = uf.user_id
4024 WHERE flag = ?',array('admin'));
4028 function get_admin() {
4029 global $CFG;
4030 return get_record_sql('SELECT u.* FROM '.$CFG->prefix.'users u
4031 JOIN '.$CFG->prefix.'user_flags uf ON u.ident = uf.user_id
4032 WHERE flag = ? ORDER BY ident',array('admin'),true);
4038 * cli_die($str) - a perlish die()
4040 * this function call will exit with a warning and an exit code
4041 * that clearly indicates that something went wrong.
4043 * We shouldn't need this, but due to PHP's web-centric heritage,
4044 * die()/exit() cant print a warnign _and_ set a non-success exit
4045 * code. Silly thing -- disregarding POSIX and friends doesn't get
4046 * you very far ;-D
4048 function cli_die ($str, $code) {
4049 trigger_error($str);
4050 exit(1);
4056 * Take a comma-separated string of keywords and create the relevant tag entries
4057 * in the database. Returns a cleaned comma-separated keyword string.
4059 * @param string $string user-input tag string
4060 * @param string $tagtype type of tagged object
4061 * @param int $ref id of tagged object
4062 * @param string $access access control value
4063 * @param int $owner tag owner
4065 * @return string cleaned comma-separated keyword string
4067 function insert_tags_from_string ($string, $tagtype, $ref, $access, $owner) {
4069 $ref = (int) $ref;
4070 $owner = (int) $owner;
4071 $tagtype = trim($tagtype);
4072 $access = trim($access);
4073 $string = trim($string);
4074 $keywords = "";
4076 $string = str_replace("\n", "", $string);
4077 $string = str_replace("\r", "", $string);
4078 if ($string) {
4079 $keyword_list = explode(",", $string);
4080 $keyword_list = array_unique($keyword_list);
4081 sort($keyword_list);
4082 if (sizeof($keyword_list) > 0) {
4083 foreach($keyword_list as $key => $list_item) {
4084 $list_item = trim($list_item);
4085 if ($list_item) {
4086 if ($key > 0) {
4087 $keywords .= ", ";
4089 $keywords .= $list_item;
4090 $t = new StdClass;
4091 $t->tagtype = $tagtype;
4092 $t->access = $access;
4093 $t->tag = $list_item;
4094 $t->ref = $ref;
4095 $t->owner = $owner;
4096 insert_record('tags', $t);
4102 return($keywords);
4107 * return the "this is restricted" text for a given access value
4108 * functionised to reduce code duplication
4110 * @param string $access access control value
4111 * @return string description for output
4113 function get_access_description ($accessvalue) {
4115 if ($accessvalue != "PUBLIC") {
4116 if ($accessvalue == "LOGGED_IN") {
4117 $title = "[" . __gettext("Logged in users") . "] ";
4118 } else if (substr_count($accessvalue, "user") > 0) {
4119 $title = "[" . __gettext("Private") . "] ";
4120 } else {
4121 $title = "[" . __gettext("Restricted") . "] ";
4122 if (substr_count($accessvalue, "community") > 0) {
4123 $name = user_name(str_replace("community", '', $accessvalue));
4124 $title = '<span title="' . __gettext('Restricted to Community: ') . $name . '">' . $title . '</span>';
4125 } else if (substr_count($accessvalue, "group") > 0) {
4126 $title = '<span title="' . __gettext('Restricted to Group') . '">' . $title . '</span>';
4129 } else {
4130 $title = "";
4133 return $title;
4137 * Activate URLs - turns any URLs found into links
4138 * @param string $str
4139 * @return string
4141 function activate_urls ($str) {
4143 // Function for URL autodiscovery
4145 $search = array();
4146 $replace = array();
4148 // lift all links, images and image maps
4149 $url_tags = array (
4150 "'<a[^>]*>.*?</a>'si",
4151 "'<map[^>]*>.*?</map>'si",
4152 "'<script[^>]*>.*?</script>'si",
4153 "'<style[^>]*>.*?</style>'si",
4154 "'<[^>]+>'si"
4157 foreach($url_tags as $url_tag)
4159 preg_match_all($url_tag, $str, $matches, PREG_SET_ORDER);
4160 foreach($matches as $match)
4162 $key = "<" . md5($match[0]) . ">";
4163 $search[] = $key;
4164 $replace[] = $match[0];
4168 $str = str_replace($replace, $search, $str);
4170 // indicate where urls end if they have these trailing special chars
4171 $sentinals = array("/&(quot|#34);/i", // Replace html entities
4172 "/&(lt|#60);/i",
4173 "/&(gt|#62);/i",
4174 "/&(nbsp|#160);/i",
4175 "/&(iexcl|#161);/i",
4176 "/&(cent|#162);/i",
4177 "/&(pound|#163);/i",
4178 "/&(copy|#169);/i");
4180 $str = preg_replace($sentinals, "<marker>\\0", $str);
4182 // URL into links
4183 $str =
4184 preg_replace( "|\w{3,10}://[\w\.\-_]+(:\d+)?[^\s\"\'<>\(\)\{\}]*|",
4185 "<a href=\"\\0\">[".__gettext("Click to view link") . "]</a>", $str );
4187 $str = str_replace("<marker>", '', $str);
4188 return str_replace($search, $replace, $str);
4193 * Returns the ID of the page owner, as reported by all plugins. Default is -1.
4194 * @return integer
4196 function page_owner() {
4198 static $owner, $called;
4199 if (!$called) {
4201 $owner = optional_param('owner',-1,PARAM_INT);
4202 if ($allmods = get_list_of_plugins('mod') ) {
4203 foreach ($allmods as $mod) {
4204 $mod_page_owner = $mod . '_page_owner';
4205 if (function_exists($mod_page_owner)) {
4206 if ($value = $mod_page_owner()) {
4208 $owner = $value;
4214 $called = true;
4217 return $owner;
4221 * Returns whether or not the current logged in user has permission to edit
4222 * a resource owned by a given account.
4223 * @param string $objecttype The type of object
4224 * @param int $owner The owning account's ident
4225 * @return boolean $permissions True or false
4228 function permissions_check($objecttype, $owner) {
4230 static $permissions_check;
4232 if (isset($permissions_check[$objecttype][$owner])) {
4233 return $permissions_check[$objecttype][$owner];
4236 if ($allmods = get_list_of_plugins('mod')) {
4237 foreach ($allmods as $mod) {
4238 $mod_permissions_check = $mod . "_permissions_check";
4239 if (function_exists($mod_permissions_check)) {
4240 if ($value = $mod_permissions_check($objecttype, $owner)) {
4241 $permissions_check[$objecttype][$owner] = $value;
4242 return $value;
4248 return false;
4253 * Returns size in human readable format
4254 * @param string $size The size in bytes
4255 * @param string $format The printf-like output format
4256 * @return string
4259 function size_readable($size, $format=null) {
4260 $size = (int)$size;
4261 $format = empty($format) ? '%.2f %s' : (string)$format;
4263 $sizes = array(
4264 'Gb' => 1073741824,
4265 'Mb' => 1048576,
4266 'Kb' => 1024,
4267 'bytes' => 1,
4270 $result = '';
4272 foreach ($sizes as $unit => $bytes) {
4273 if ($size > $bytes) {
4274 $result = sprintf($format, $size/$bytes, $unit);
4275 break;
4279 return $result;