Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/moodlelib.php
Go to the documentation of this file.
00001 <?php
00002 
00003 // This file is part of Moodle - http://moodle.org/
00004 //
00005 // Moodle is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // Moodle is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00017 
00032 defined('MOODLE_INTERNAL') || die();
00033 
00035 
00037 
00040 define('YEARSECS', 31536000);
00041 
00045 define('WEEKSECS', 604800);
00046 
00050 define('DAYSECS', 86400);
00051 
00055 define('HOURSECS', 3600);
00056 
00060 define('MINSECS', 60);
00061 
00065 define('DAYMINS', 1440);
00066 
00070 define('HOURMINS', 60);
00071 
00074 
00075 
00076 
00080 define('PARAM_ALPHA',    'alpha');
00081 
00086 define('PARAM_ALPHAEXT', 'alphaext');
00087 
00091 define('PARAM_ALPHANUM', 'alphanum');
00092 
00096 define('PARAM_ALPHANUMEXT', 'alphanumext');
00097 
00101 define('PARAM_AUTH',  'auth');
00102 
00106 define('PARAM_BASE64',   'base64');
00107 
00111 define('PARAM_BOOL',     'bool');
00112 
00117 define('PARAM_CAPABILITY',   'capability');
00118 
00122 define('PARAM_CLEANHTML', 'cleanhtml');
00123 
00127 define('PARAM_EMAIL',   'email');
00128 
00132 define('PARAM_FILE',   'file');
00133 
00137 define('PARAM_FLOAT',  'float');
00138 
00142 define('PARAM_HOST',     'host');
00143 
00147 define('PARAM_INT',      'int');
00148 
00152 define('PARAM_LANG',  'lang');
00153 
00157 define('PARAM_LOCALURL', 'localurl');
00158 
00162 define('PARAM_NOTAGS',   'notags');
00163 
00168 define('PARAM_PATH',     'path');
00169 
00173 define('PARAM_PEM',      'pem');
00174 
00178 define('PARAM_PERMISSION',   'permission');
00179 
00183 define('PARAM_RAW', 'raw');
00184 
00188 define('PARAM_RAW_TRIMMED', 'raw_trimmed');
00189 
00193 define('PARAM_SAFEDIR',  'safedir');
00194 
00198 define('PARAM_SAFEPATH',  'safepath');
00199 
00203 define('PARAM_SEQUENCE',  'sequence');
00204 
00208 define('PARAM_TAG',   'tag');
00209 
00213 define('PARAM_TAGLIST',   'taglist');
00214 
00218 define('PARAM_TEXT',  'text');
00219 
00223 define('PARAM_THEME',  'theme');
00224 
00228 define('PARAM_URL',      'url');
00229 
00233 define('PARAM_USERNAME',    'username');
00234 
00238 define('PARAM_STRINGID',    'stringid');
00239 
00241 
00246 define('PARAM_CLEAN',    'clean');
00247 
00251 define('PARAM_INTEGER',  'int');
00252 
00256 define('PARAM_NUMBER',  'float');
00257 
00262 define('PARAM_ACTION',   'alphanumext');
00263 
00268 define('PARAM_FORMAT',   'alphanumext');
00269 
00273 define('PARAM_MULTILANG',  'text');
00274 
00280 define('PARAM_TIMEZONE', 'timezone');
00281 
00285 define('PARAM_CLEANFILE', 'file');
00286 
00293 define('PARAM_COMPONENT', 'component');
00294 
00300 define('PARAM_AREA', 'area');
00301 
00307 define('PARAM_PLUGIN', 'plugin');
00308 
00309 
00311 
00315 define('VALUE_REQUIRED', 1);
00316 
00320 define('VALUE_OPTIONAL', 2);
00321 
00325 define('VALUE_DEFAULT', 0);
00326 
00330 define('NULL_NOT_ALLOWED', false);
00331 
00335 define('NULL_ALLOWED', true);
00336 
00338 
00341 define('PAGE_COURSE_VIEW', 'course-view');
00342 
00344 define('GETREMOTEADDR_SKIP_HTTP_CLIENT_IP', '1');
00346 define('GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR', '2');
00347 
00349 define ('BLOG_USER_LEVEL', 1);
00350 define ('BLOG_GROUP_LEVEL', 2);
00351 define ('BLOG_COURSE_LEVEL', 3);
00352 define ('BLOG_SITE_LEVEL', 4);
00353 define ('BLOG_GLOBAL_LEVEL', 5);
00354 
00355 
00357 
00364 define('TAG_MAX_LENGTH', 50);
00365 
00367 define ('PASSWORD_LOWER', 'abcdefghijklmnopqrstuvwxyz');
00368 define ('PASSWORD_UPPER', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
00369 define ('PASSWORD_DIGITS', '0123456789');
00370 define ('PASSWORD_NONALPHANUM', '.,;:!?_-+/*@#&$');
00371 
00373 // Used for plugin_supports() to report features that are, or are not, supported by a module.
00374 
00376 define('FEATURE_GRADE_HAS_GRADE', 'grade_has_grade');
00378 define('FEATURE_GRADE_OUTCOMES', 'outcomes');
00380 define('FEATURE_ADVANCED_GRADING', 'grade_advanced_grading');
00381 
00383 define('FEATURE_COMPLETION_TRACKS_VIEWS', 'completion_tracks_views');
00385 define('FEATURE_COMPLETION_HAS_RULES', 'completion_has_rules');
00386 
00388 define('FEATURE_NO_VIEW_LINK', 'viewlink');
00390 define('FEATURE_IDNUMBER', 'idnumber');
00392 define('FEATURE_GROUPS', 'groups');
00394 define('FEATURE_GROUPINGS', 'groupings');
00396 define('FEATURE_GROUPMEMBERSONLY', 'groupmembersonly');
00397 
00399 define('FEATURE_MOD_ARCHETYPE', 'mod_archetype');
00401 define('FEATURE_MOD_INTRO', 'mod_intro');
00403 define('FEATURE_MODEDIT_DEFAULT_COMPLETION', 'modedit_default_completion');
00404 
00405 define('FEATURE_COMMENT', 'comment');
00406 
00407 define('FEATURE_RATE', 'rate');
00409 define('FEATURE_BACKUP_MOODLE2', 'backup_moodle2');
00410 
00412 define('FEATURE_SHOW_DESCRIPTION', 'showdescription');
00413 
00415 define('MOD_ARCHETYPE_OTHER', 0);
00417 define('MOD_ARCHETYPE_RESOURCE', 1);
00419 define('MOD_ARCHETYPE_ASSIGNMENT', 2);
00420 
00428 define('EXTERNAL_TOKEN_PERMANENT', 0);
00429 
00436 define('EXTERNAL_TOKEN_EMBEDDED', 1);
00437 
00441 define('HOMEPAGE_SITE', 0);
00445 define('HOMEPAGE_MY', 1);
00449 define('HOMEPAGE_USER', 2);
00450 
00454 define('HUB_HUBDIRECTORYURL', "http://hubdirectory.moodle.org");
00455 
00456 
00460 define('HUB_MOODLEORGHUBURL', "http://hub.moodle.org");
00461 
00465 define('MOODLE_OFFICIAL_MOBILE_SERVICE', 'moodle_mobile_app');
00466 
00468 
00485 function required_param($parname, $type) {
00486     if (func_num_args() != 2 or empty($parname) or empty($type)) {
00487         throw new coding_exception('required_param() requires $parname and $type to be specified (parameter: '.$parname.')');
00488     }
00489     if (isset($_POST[$parname])) {       // POST has precedence
00490         $param = $_POST[$parname];
00491     } else if (isset($_GET[$parname])) {
00492         $param = $_GET[$parname];
00493     } else {
00494         print_error('missingparam', '', '', $parname);
00495     }
00496 
00497     if (is_array($param)) {
00498         debugging('Invalid array parameter detected in required_param(): '.$parname);
00499         // TODO: switch to fatal error in Moodle 2.3
00500         //print_error('missingparam', '', '', $parname);
00501         return required_param_array($parname, $type);
00502     }
00503 
00504     return clean_param($param, $type);
00505 }
00506 
00523 function required_param_array($parname, $type) {
00524     if (func_num_args() != 2 or empty($parname) or empty($type)) {
00525         throw new coding_exception('required_param_array() requires $parname and $type to be specified (parameter: '.$parname.')');
00526     }
00527     if (isset($_POST[$parname])) {       // POST has precedence
00528         $param = $_POST[$parname];
00529     } else if (isset($_GET[$parname])) {
00530         $param = $_GET[$parname];
00531     } else {
00532         print_error('missingparam', '', '', $parname);
00533     }
00534     if (!is_array($param)) {
00535         print_error('missingparam', '', '', $parname);
00536     }
00537 
00538     $result = array();
00539     foreach($param as $key=>$value) {
00540         if (!preg_match('/^[a-z0-9_-]+$/i', $key)) {
00541             debugging('Invalid key name in required_param_array() detected: '.$key.', parameter: '.$parname);
00542             continue;
00543         }
00544         $result[$key] = clean_param($value, $type);
00545     }
00546 
00547     return $result;
00548 }
00549 
00566 function optional_param($parname, $default, $type) {
00567     if (func_num_args() != 3 or empty($parname) or empty($type)) {
00568         throw new coding_exception('optional_param() requires $parname, $default and $type to be specified (parameter: '.$parname.')');
00569     }
00570     if (!isset($default)) {
00571         $default = null;
00572     }
00573 
00574     if (isset($_POST[$parname])) {       // POST has precedence
00575         $param = $_POST[$parname];
00576     } else if (isset($_GET[$parname])) {
00577         $param = $_GET[$parname];
00578     } else {
00579         return $default;
00580     }
00581 
00582     if (is_array($param)) {
00583         debugging('Invalid array parameter detected in required_param(): '.$parname);
00584         // TODO: switch to $default in Moodle 2.3
00585         //return $default;
00586         return optional_param_array($parname, $default, $type);
00587     }
00588 
00589     return clean_param($param, $type);
00590 }
00591 
00608 function optional_param_array($parname, $default, $type) {
00609     if (func_num_args() != 3 or empty($parname) or empty($type)) {
00610         throw new coding_exception('optional_param_array() requires $parname, $default and $type to be specified (parameter: '.$parname.')');
00611     }
00612 
00613     if (isset($_POST[$parname])) {       // POST has precedence
00614         $param = $_POST[$parname];
00615     } else if (isset($_GET[$parname])) {
00616         $param = $_GET[$parname];
00617     } else {
00618         return $default;
00619     }
00620     if (!is_array($param)) {
00621         debugging('optional_param_array() expects array parameters only: '.$parname);
00622         return $default;
00623     }
00624 
00625     $result = array();
00626     foreach($param as $key=>$value) {
00627         if (!preg_match('/^[a-z0-9_-]+$/i', $key)) {
00628             debugging('Invalid key name in optional_param_array() detected: '.$key.', parameter: '.$parname);
00629             continue;
00630         }
00631         $result[$key] = clean_param($value, $type);
00632     }
00633 
00634     return $result;
00635 }
00636 
00650 function validate_param($param, $type, $allownull=NULL_NOT_ALLOWED, $debuginfo='') {
00651     if (is_null($param)) {
00652         if ($allownull == NULL_ALLOWED) {
00653             return null;
00654         } else {
00655             throw new invalid_parameter_exception($debuginfo);
00656         }
00657     }
00658     if (is_array($param) or is_object($param)) {
00659         throw new invalid_parameter_exception($debuginfo);
00660     }
00661 
00662     $cleaned = clean_param($param, $type);
00663     if ((string)$param !== (string)$cleaned) {
00664         // conversion to string is usually lossless
00665         throw new invalid_parameter_exception($debuginfo);
00666     }
00667 
00668     return $cleaned;
00669 }
00670 
00683 function clean_param_array(array $param = null, $type, $recursive = false) {
00684     $param = (array)$param; // convert null to empty array
00685     foreach ($param as $key => $value) {
00686         if (is_array($value)) {
00687             if ($recursive) {
00688                 $param[$key] = clean_param_array($value, $type, true);
00689             } else {
00690                 throw new coding_exception('clean_param_array() can not process multidimensional arrays when $recursive is false.');
00691             }
00692         } else {
00693             $param[$key] = clean_param($value, $type);
00694         }
00695     }
00696     return $param;
00697 }
00698 
00712 function clean_param($param, $type) {
00713 
00714     global $CFG;
00715 
00716     if (is_array($param)) {
00717         throw new coding_exception('clean_param() can not process arrays, please use clean_param_array() instead.');
00718     } else if (is_object($param)) {
00719         if (method_exists($param, '__toString')) {
00720             $param = $param->__toString();
00721         } else {
00722             throw new coding_exception('clean_param() can not process objects, please use clean_param_array() instead.');
00723         }
00724     }
00725 
00726     switch ($type) {
00727         case PARAM_RAW:          // no cleaning at all
00728             $param = fix_utf8($param);
00729             return $param;
00730 
00731         case PARAM_RAW_TRIMMED:         // no cleaning, but strip leading and trailing whitespace.
00732             $param = fix_utf8($param);
00733             return trim($param);
00734 
00735         case PARAM_CLEAN:        // General HTML cleaning, try to use more specific type if possible
00736             // this is deprecated!, please use more specific type instead
00737             if (is_numeric($param)) {
00738                 return $param;
00739             }
00740             $param = fix_utf8($param);
00741             return clean_text($param);     // Sweep for scripts, etc
00742 
00743         case PARAM_CLEANHTML:    // clean html fragment
00744             $param = fix_utf8($param);
00745             $param = clean_text($param, FORMAT_HTML);     // Sweep for scripts, etc
00746             return trim($param);
00747 
00748         case PARAM_INT:
00749             return (int)$param;  // Convert to integer
00750 
00751         case PARAM_FLOAT:
00752         case PARAM_NUMBER:
00753             return (float)$param;  // Convert to float
00754 
00755         case PARAM_ALPHA:        // Remove everything not a-z
00756             return preg_replace('/[^a-zA-Z]/i', '', $param);
00757 
00758         case PARAM_ALPHAEXT:     // Remove everything not a-zA-Z_- (originally allowed "/" too)
00759             return preg_replace('/[^a-zA-Z_-]/i', '', $param);
00760 
00761         case PARAM_ALPHANUM:     // Remove everything not a-zA-Z0-9
00762             return preg_replace('/[^A-Za-z0-9]/i', '', $param);
00763 
00764         case PARAM_ALPHANUMEXT:     // Remove everything not a-zA-Z0-9_-
00765             return preg_replace('/[^A-Za-z0-9_-]/i', '', $param);
00766 
00767         case PARAM_SEQUENCE:     // Remove everything not 0-9,
00768             return preg_replace('/[^0-9,]/i', '', $param);
00769 
00770         case PARAM_BOOL:         // Convert to 1 or 0
00771             $tempstr = strtolower($param);
00772             if ($tempstr === 'on' or $tempstr === 'yes' or $tempstr === 'true') {
00773                 $param = 1;
00774             } else if ($tempstr === 'off' or $tempstr === 'no'  or $tempstr === 'false') {
00775                 $param = 0;
00776             } else {
00777                 $param = empty($param) ? 0 : 1;
00778             }
00779             return $param;
00780 
00781         case PARAM_NOTAGS:       // Strip all tags
00782             $param = fix_utf8($param);
00783             return strip_tags($param);
00784 
00785         case PARAM_TEXT:    // leave only tags needed for multilang
00786             $param = fix_utf8($param);
00787             // if the multilang syntax is not correct we strip all tags
00788             // because it would break xhtml strict which is required for accessibility standards
00789             // please note this cleaning does not strip unbalanced '>' for BC compatibility reasons
00790             do {
00791                 if (strpos($param, '</lang>') !== false) {
00792                     // old and future mutilang syntax
00793                     $param = strip_tags($param, '<lang>');
00794                     if (!preg_match_all('/<.*>/suU', $param, $matches)) {
00795                         break;
00796                     }
00797                     $open = false;
00798                     foreach ($matches[0] as $match) {
00799                         if ($match === '</lang>') {
00800                             if ($open) {
00801                                 $open = false;
00802                                 continue;
00803                             } else {
00804                                 break 2;
00805                             }
00806                         }
00807                         if (!preg_match('/^<lang lang="[a-zA-Z0-9_-]+"\s*>$/u', $match)) {
00808                             break 2;
00809                         } else {
00810                             $open = true;
00811                         }
00812                     }
00813                     if ($open) {
00814                         break;
00815                     }
00816                     return $param;
00817 
00818                 } else if (strpos($param, '</span>') !== false) {
00819                     // current problematic multilang syntax
00820                     $param = strip_tags($param, '<span>');
00821                     if (!preg_match_all('/<.*>/suU', $param, $matches)) {
00822                         break;
00823                     }
00824                     $open = false;
00825                     foreach ($matches[0] as $match) {
00826                         if ($match === '</span>') {
00827                             if ($open) {
00828                                 $open = false;
00829                                 continue;
00830                             } else {
00831                                 break 2;
00832                             }
00833                         }
00834                         if (!preg_match('/^<span(\s+lang="[a-zA-Z0-9_-]+"|\s+class="multilang"){2}\s*>$/u', $match)) {
00835                             break 2;
00836                         } else {
00837                             $open = true;
00838                         }
00839                     }
00840                     if ($open) {
00841                         break;
00842                     }
00843                     return $param;
00844                 }
00845             } while (false);
00846             // easy, just strip all tags, if we ever want to fix orphaned '&' we have to do that in format_string()
00847             return strip_tags($param);
00848 
00849         case PARAM_COMPONENT:
00850             // we do not want any guessing here, either the name is correct or not
00851             // please note only normalised component names are accepted
00852             if (!preg_match('/^[a-z]+(_[a-z][a-z0-9_]*)?[a-z0-9]$/', $param)) {
00853                 return '';
00854             }
00855             if (strpos($param, '__') !== false) {
00856                 return '';
00857             }
00858             if (strpos($param, 'mod_') === 0) {
00859                 // module names must not contain underscores because we need to differentiate them from invalid plugin types
00860                 if (substr_count($param, '_') != 1) {
00861                     return '';
00862                 }
00863             }
00864             return $param;
00865 
00866         case PARAM_PLUGIN:
00867         case PARAM_AREA:
00868             // we do not want any guessing here, either the name is correct or not
00869             if (!preg_match('/^[a-z][a-z0-9_]*[a-z0-9]$/', $param)) {
00870                 return '';
00871             }
00872             if (strpos($param, '__') !== false) {
00873                 return '';
00874             }
00875             return $param;
00876 
00877         case PARAM_SAFEDIR:      // Remove everything not a-zA-Z0-9_-
00878             return preg_replace('/[^a-zA-Z0-9_-]/i', '', $param);
00879 
00880         case PARAM_SAFEPATH:     // Remove everything not a-zA-Z0-9/_-
00881             return preg_replace('/[^a-zA-Z0-9\/_-]/i', '', $param);
00882 
00883         case PARAM_FILE:         // Strip all suspicious characters from filename
00884             $param = fix_utf8($param);
00885             $param = preg_replace('~[[:cntrl:]]|[&<>"`\|\':\\\\/]~u', '', $param);
00886             $param = preg_replace('~\.\.+~', '', $param);
00887             if ($param === '.') {
00888                 $param = '';
00889             }
00890             return $param;
00891 
00892         case PARAM_PATH:         // Strip all suspicious characters from file path
00893             $param = fix_utf8($param);
00894             $param = str_replace('\\', '/', $param);
00895             $param = preg_replace('~[[:cntrl:]]|[&<>"`\|\':]~u', '', $param);
00896             $param = preg_replace('~\.\.+~', '', $param);
00897             $param = preg_replace('~//+~', '/', $param);
00898             return preg_replace('~/(\./)+~', '/', $param);
00899 
00900         case PARAM_HOST:         // allow FQDN or IPv4 dotted quad
00901             $param = preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars
00902             // match ipv4 dotted quad
00903             if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){
00904                 // confirm values are ok
00905                 if ( $match[0] > 255
00906                      || $match[1] > 255
00907                      || $match[3] > 255
00908                      || $match[4] > 255 ) {
00909                     // hmmm, what kind of dotted quad is this?
00910                     $param = '';
00911                 }
00912             } elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers
00913                        && !preg_match('/^[\.-]/',  $param) // no leading dots/hyphens
00914                        && !preg_match('/[\.-]$/',  $param) // no trailing dots/hyphens
00915                        ) {
00916                 // all is ok - $param is respected
00917             } else {
00918                 // all is not ok...
00919                 $param='';
00920             }
00921             return $param;
00922 
00923         case PARAM_URL:          // allow safe ftp, http, mailto urls
00924             $param = fix_utf8($param);
00925             include_once($CFG->dirroot . '/lib/validateurlsyntax.php');
00926             if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p?f?q?r?')) {
00927                 // all is ok, param is respected
00928             } else {
00929                 $param =''; // not really ok
00930             }
00931             return $param;
00932 
00933         case PARAM_LOCALURL:     // allow http absolute, root relative and relative URLs within wwwroot
00934             $param = clean_param($param, PARAM_URL);
00935             if (!empty($param)) {
00936                 if (preg_match(':^/:', $param)) {
00937                     // root-relative, ok!
00938                 } elseif (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i',$param)) {
00939                     // absolute, and matches our wwwroot
00940                 } else {
00941                     // relative - let's make sure there are no tricks
00942                     if (validateUrlSyntax('/' . $param, 's-u-P-a-p-f+q?r?')) {
00943                         // looks ok.
00944                     } else {
00945                         $param = '';
00946                     }
00947                 }
00948             }
00949             return $param;
00950 
00951         case PARAM_PEM:
00952             $param = trim($param);
00953             // PEM formatted strings may contain letters/numbers and the symbols
00954             // forward slash: /
00955             // plus sign:     +
00956             // equal sign:    =
00957             // , surrounded by BEGIN and END CERTIFICATE prefix and suffixes
00958             if (preg_match('/^-----BEGIN CERTIFICATE-----([\s\w\/\+=]+)-----END CERTIFICATE-----$/', trim($param), $matches)) {
00959                 list($wholething, $body) = $matches;
00960                 unset($wholething, $matches);
00961                 $b64 = clean_param($body, PARAM_BASE64);
00962                 if (!empty($b64)) {
00963                     return "-----BEGIN CERTIFICATE-----\n$b64\n-----END CERTIFICATE-----\n";
00964                 } else {
00965                     return '';
00966                 }
00967             }
00968             return '';
00969 
00970         case PARAM_BASE64:
00971             if (!empty($param)) {
00972                 // PEM formatted strings may contain letters/numbers and the symbols
00973                 // forward slash: /
00974                 // plus sign:     +
00975                 // equal sign:    =
00976                 if (0 >= preg_match('/^([\s\w\/\+=]+)$/', trim($param))) {
00977                     return '';
00978                 }
00979                 $lines = preg_split('/[\s]+/', $param, -1, PREG_SPLIT_NO_EMPTY);
00980                 // Each line of base64 encoded data must be 64 characters in
00981                 // length, except for the last line which may be less than (or
00982                 // equal to) 64 characters long.
00983                 for ($i=0, $j=count($lines); $i < $j; $i++) {
00984                     if ($i + 1 == $j) {
00985                         if (64 < strlen($lines[$i])) {
00986                             return '';
00987                         }
00988                         continue;
00989                     }
00990 
00991                     if (64 != strlen($lines[$i])) {
00992                         return '';
00993                     }
00994                 }
00995                 return implode("\n",$lines);
00996             } else {
00997                 return '';
00998             }
00999 
01000         case PARAM_TAG:
01001             $param = fix_utf8($param);
01002             // Please note it is not safe to use the tag name directly anywhere,
01003             // it must be processed with s(), urlencode() before embedding anywhere.
01004             // remove some nasties
01005             $param = preg_replace('~[[:cntrl:]]|[<>`]~u', '', $param);
01006             //convert many whitespace chars into one
01007             $param = preg_replace('/\s+/', ' ', $param);
01008             $textlib = textlib_get_instance();
01009             $param = $textlib->substr(trim($param), 0, TAG_MAX_LENGTH);
01010             return $param;
01011 
01012         case PARAM_TAGLIST:
01013             $param = fix_utf8($param);
01014             $tags = explode(',', $param);
01015             $result = array();
01016             foreach ($tags as $tag) {
01017                 $res = clean_param($tag, PARAM_TAG);
01018                 if ($res !== '') {
01019                     $result[] = $res;
01020                 }
01021             }
01022             if ($result) {
01023                 return implode(',', $result);
01024             } else {
01025                 return '';
01026             }
01027 
01028         case PARAM_CAPABILITY:
01029             if (get_capability_info($param)) {
01030                 return $param;
01031             } else {
01032                 return '';
01033             }
01034 
01035         case PARAM_PERMISSION:
01036             $param = (int)$param;
01037             if (in_array($param, array(CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT))) {
01038                 return $param;
01039             } else {
01040                 return CAP_INHERIT;
01041             }
01042 
01043         case PARAM_AUTH:
01044             $param = clean_param($param, PARAM_PLUGIN);
01045             if (empty($param)) {
01046                 return '';
01047             } else if (exists_auth_plugin($param)) {
01048                 return $param;
01049             } else {
01050                 return '';
01051             }
01052 
01053         case PARAM_LANG:
01054             $param = clean_param($param, PARAM_SAFEDIR);
01055             if (get_string_manager()->translation_exists($param)) {
01056                 return $param;
01057             } else {
01058                 return ''; // Specified language is not installed or param malformed
01059             }
01060 
01061         case PARAM_THEME:
01062             $param = clean_param($param, PARAM_PLUGIN);
01063             if (empty($param)) {
01064                 return '';
01065             } else if (file_exists("$CFG->dirroot/theme/$param/config.php")) {
01066                 return $param;
01067             } else if (!empty($CFG->themedir) and file_exists("$CFG->themedir/$param/config.php")) {
01068                 return $param;
01069             } else {
01070                 return '';  // Specified theme is not installed
01071             }
01072 
01073         case PARAM_USERNAME:
01074             $param = fix_utf8($param);
01075             $param = str_replace(" " , "", $param);
01076             $param = moodle_strtolower($param);  // Convert uppercase to lowercase MDL-16919
01077             if (empty($CFG->extendedusernamechars)) {
01078                 // regular expression, eliminate all chars EXCEPT:
01079                 // alphanum, dash (-), underscore (_), at sign (@) and period (.) characters.
01080                 $param = preg_replace('/[^-\.@_a-z0-9]/', '', $param);
01081             }
01082             return $param;
01083 
01084         case PARAM_EMAIL:
01085             $param = fix_utf8($param);
01086             if (validate_email($param)) {
01087                 return $param;
01088             } else {
01089                 return '';
01090             }
01091 
01092         case PARAM_STRINGID:
01093             if (preg_match('|^[a-zA-Z][a-zA-Z0-9\.:/_-]*$|', $param)) {
01094                 return $param;
01095             } else {
01096                 return '';
01097             }
01098 
01099         case PARAM_TIMEZONE:    //can be int, float(with .5 or .0) or string seperated by '/' and can have '-_'
01100             $param = fix_utf8($param);
01101             $timezonepattern = '/^(([+-]?(0?[0-9](\.[5|0])?|1[0-3]|1[0-2]\.5))|(99)|[[:alnum:]]+(\/?[[:alpha:]_-])+)$/';
01102             if (preg_match($timezonepattern, $param)) {
01103                 return $param;
01104             } else {
01105                 return '';
01106             }
01107 
01108         default:                 // throw error, switched parameters in optional_param or another serious problem
01109             print_error("unknownparamtype", '', '', $type);
01110     }
01111 }
01112 
01121 function fix_utf8($value) {
01122     if (is_null($value) or $value === '') {
01123         return $value;
01124 
01125     } else if (is_string($value)) {
01126         if ((string)(int)$value === $value) {
01127             // shortcut
01128             return $value;
01129         }
01130         return iconv('UTF-8', 'UTF-8//IGNORE', $value);
01131 
01132     } else if (is_array($value)) {
01133         foreach ($value as $k=>$v) {
01134             $value[$k] = fix_utf8($v);
01135         }
01136         return $value;
01137 
01138     } else if (is_object($value)) {
01139         $value = clone($value); // do not modify original
01140         foreach ($value as $k=>$v) {
01141             $value->$k = fix_utf8($v);
01142         }
01143         return $value;
01144 
01145     } else {
01146         // this is some other type, no utf-8 here
01147         return $value;
01148     }
01149 }
01150 
01157 function is_number($value) {
01158     if (is_int($value)) {
01159         return true;
01160     } else if (is_string($value)) {
01161         return ((string)(int)$value) === $value;
01162     } else {
01163         return false;
01164     }
01165 }
01166 
01172 function get_host_from_url($url) {
01173     preg_match('|^[a-z]+://([a-zA-Z0-9-.]+)|i', $url, $matches);
01174     if ($matches) {
01175         return $matches[1];
01176     }
01177     return null;
01178 }
01179 
01191 function html_is_blank($string) {
01192     return trim(strip_tags($string, '<img><object><applet><input><select><textarea><hr>')) == '';
01193 }
01194 
01213 function set_config($name, $value, $plugin=NULL) {
01214     global $CFG, $DB;
01215 
01216     if (empty($plugin)) {
01217         if (!array_key_exists($name, $CFG->config_php_settings)) {
01218             // So it's defined for this invocation at least
01219             if (is_null($value)) {
01220                 unset($CFG->$name);
01221             } else {
01222                 $CFG->$name = (string)$value; // settings from db are always strings
01223             }
01224         }
01225 
01226         if ($DB->get_field('config', 'name', array('name'=>$name))) {
01227             if ($value === null) {
01228                 $DB->delete_records('config', array('name'=>$name));
01229             } else {
01230                 $DB->set_field('config', 'value', $value, array('name'=>$name));
01231             }
01232         } else {
01233             if ($value !== null) {
01234                 $config = new stdClass();
01235                 $config->name  = $name;
01236                 $config->value = $value;
01237                 $DB->insert_record('config', $config, false);
01238             }
01239         }
01240 
01241     } else { // plugin scope
01242         if ($id = $DB->get_field('config_plugins', 'id', array('name'=>$name, 'plugin'=>$plugin))) {
01243             if ($value===null) {
01244                 $DB->delete_records('config_plugins', array('name'=>$name, 'plugin'=>$plugin));
01245             } else {
01246                 $DB->set_field('config_plugins', 'value', $value, array('id'=>$id));
01247             }
01248         } else {
01249             if ($value !== null) {
01250                 $config = new stdClass();
01251                 $config->plugin = $plugin;
01252                 $config->name   = $name;
01253                 $config->value  = $value;
01254                 $DB->insert_record('config_plugins', $config, false);
01255             }
01256         }
01257     }
01258 
01259     return true;
01260 }
01261 
01276 function get_config($plugin, $name = NULL) {
01277     global $CFG, $DB;
01278 
01279     // normalise component name
01280     if ($plugin === 'moodle' or $plugin === 'core') {
01281         $plugin = NULL;
01282     }
01283 
01284     if (!empty($name)) { // the user is asking for a specific value
01285         if (!empty($plugin)) {
01286             if (isset($CFG->forced_plugin_settings[$plugin]) and array_key_exists($name, $CFG->forced_plugin_settings[$plugin])) {
01287                 // setting forced in config file
01288                 return $CFG->forced_plugin_settings[$plugin][$name];
01289             } else {
01290                 return $DB->get_field('config_plugins', 'value', array('plugin'=>$plugin, 'name'=>$name));
01291             }
01292         } else {
01293             if (array_key_exists($name, $CFG->config_php_settings)) {
01294                 // setting force in config file
01295                 return $CFG->config_php_settings[$name];
01296             } else {
01297                 return $DB->get_field('config', 'value', array('name'=>$name));
01298             }
01299         }
01300     }
01301 
01302     // the user is after a recordset
01303     if ($plugin) {
01304         $localcfg = $DB->get_records_menu('config_plugins', array('plugin'=>$plugin), '', 'name,value');
01305         if (isset($CFG->forced_plugin_settings[$plugin])) {
01306             foreach($CFG->forced_plugin_settings[$plugin] as $n=>$v) {
01307                 if (is_null($v) or is_array($v) or is_object($v)) {
01308                     // we do not want any extra mess here, just real settings that could be saved in db
01309                     unset($localcfg[$n]);
01310                 } else {
01311                     //convert to string as if it went through the DB
01312                     $localcfg[$n] = (string)$v;
01313                 }
01314             }
01315         }
01316         if ($localcfg) {
01317             return (object)$localcfg;
01318         } else {
01319             return new stdClass();
01320         }
01321 
01322     } else {
01323         // this part is not really used any more, but anyway...
01324         $localcfg = $DB->get_records_menu('config', array(), '', 'name,value');
01325         foreach($CFG->config_php_settings as $n=>$v) {
01326             if (is_null($v) or is_array($v) or is_object($v)) {
01327                 // we do not want any extra mess here, just real settings that could be saved in db
01328                 unset($localcfg[$n]);
01329             } else {
01330                 //convert to string as if it went through the DB
01331                 $localcfg[$n] = (string)$v;
01332             }
01333         }
01334         return (object)$localcfg;
01335     }
01336 }
01337 
01346 function unset_config($name, $plugin=NULL) {
01347     global $CFG, $DB;
01348 
01349     if (empty($plugin)) {
01350         unset($CFG->$name);
01351         $DB->delete_records('config', array('name'=>$name));
01352     } else {
01353         $DB->delete_records('config_plugins', array('name'=>$name, 'plugin'=>$plugin));
01354     }
01355 
01356     return true;
01357 }
01358 
01365 function unset_all_config_for_plugin($plugin) {
01366     global $DB;
01367     $DB->delete_records('config_plugins', array('plugin' => $plugin));
01368     $like = $DB->sql_like('name', '?', true, true, false, '|');
01369     $params = array($DB->sql_like_escape($plugin.'_', '|') . '%');
01370     $DB->delete_records_select('config', $like, $params);
01371     return true;
01372 }
01373 
01384 function get_users_from_config($value, $capability, $includeadmins = true) {
01385     global $CFG, $DB;
01386 
01387     if (empty($value) or $value === '$@NONE@$') {
01388         return array();
01389     }
01390 
01391     // we have to make sure that users still have the necessary capability,
01392     // it should be faster to fetch them all first and then test if they are present
01393     // instead of validating them one-by-one
01394     $users = get_users_by_capability(get_context_instance(CONTEXT_SYSTEM), $capability);
01395     if ($includeadmins) {
01396         $admins = get_admins();
01397         foreach ($admins as $admin) {
01398             $users[$admin->id] = $admin;
01399         }
01400     }
01401 
01402     if ($value === '$@ALL@$') {
01403         return $users;
01404     }
01405 
01406     $result = array(); // result in correct order
01407     $allowed = explode(',', $value);
01408     foreach ($allowed as $uid) {
01409         if (isset($users[$uid])) {
01410             $user = $users[$uid];
01411             $result[$user->id] = $user;
01412         }
01413     }
01414 
01415     return $result;
01416 }
01417 
01418 
01423 function purge_all_caches() {
01424     global $CFG;
01425 
01426     reset_text_filters_cache();
01427     js_reset_all_caches();
01428     theme_reset_all_caches();
01429     get_string_manager()->reset_caches();
01430 
01431     // purge all other caches: rss, simplepie, etc.
01432     remove_dir($CFG->cachedir.'', true);
01433 
01434     // make sure cache dir is writable, throws exception if not
01435     make_cache_directory('');
01436 
01437     // hack: this script may get called after the purifier was initialised,
01438     // but we do not want to verify repeatedly this exists in each call
01439     make_cache_directory('htmlpurifier');
01440 }
01441 
01449 function get_cache_flags($type, $changedsince=NULL) {
01450     global $DB;
01451 
01452     $params = array('type'=>$type, 'expiry'=>time());
01453     $sqlwhere = "flagtype = :type AND expiry >= :expiry";
01454     if ($changedsince !== NULL) {
01455         $params['changedsince'] = $changedsince;
01456         $sqlwhere .= " AND timemodified > :changedsince";
01457     }
01458     $cf = array();
01459 
01460     if ($flags = $DB->get_records_select('cache_flags', $sqlwhere, $params, '', 'name,value')) {
01461         foreach ($flags as $flag) {
01462             $cf[$flag->name] = $flag->value;
01463         }
01464     }
01465     return $cf;
01466 }
01467 
01476 function get_cache_flag($type, $name, $changedsince=NULL) {
01477     global $DB;
01478 
01479     $params = array('type'=>$type, 'name'=>$name, 'expiry'=>time());
01480 
01481     $sqlwhere = "flagtype = :type AND name = :name AND expiry >= :expiry";
01482     if ($changedsince !== NULL) {
01483         $params['changedsince'] = $changedsince;
01484         $sqlwhere .= " AND timemodified > :changedsince";
01485     }
01486 
01487     return $DB->get_field_select('cache_flags', 'value', $sqlwhere, $params);
01488 }
01489 
01499 function set_cache_flag($type, $name, $value, $expiry=NULL) {
01500     global $DB;
01501 
01502     $timemodified = time();
01503     if ($expiry===NULL || $expiry < $timemodified) {
01504         $expiry = $timemodified + 24 * 60 * 60;
01505     } else {
01506         $expiry = (int)$expiry;
01507     }
01508 
01509     if ($value === NULL) {
01510         unset_cache_flag($type,$name);
01511         return true;
01512     }
01513 
01514     if ($f = $DB->get_record('cache_flags', array('name'=>$name, 'flagtype'=>$type), '*', IGNORE_MULTIPLE)) { // this is a potential problem in DEBUG_DEVELOPER
01515         if ($f->value == $value and $f->expiry == $expiry and $f->timemodified == $timemodified) {
01516             return true; //no need to update; helps rcache too
01517         }
01518         $f->value        = $value;
01519         $f->expiry       = $expiry;
01520         $f->timemodified = $timemodified;
01521         $DB->update_record('cache_flags', $f);
01522     } else {
01523         $f = new stdClass();
01524         $f->flagtype     = $type;
01525         $f->name         = $name;
01526         $f->value        = $value;
01527         $f->expiry       = $expiry;
01528         $f->timemodified = $timemodified;
01529         $DB->insert_record('cache_flags', $f);
01530     }
01531     return true;
01532 }
01533 
01542 function unset_cache_flag($type, $name) {
01543     global $DB;
01544     $DB->delete_records('cache_flags', array('name'=>$name, 'flagtype'=>$type));
01545     return true;
01546 }
01547 
01553 function gc_cache_flags() {
01554     global $DB;
01555     $DB->delete_records_select('cache_flags', 'expiry < ?', array(time()));
01556     return true;
01557 }
01558 
01560 
01571 function check_user_preferences_loaded(stdClass $user, $cachelifetime = 120) {
01572     global $DB;
01573     static $loadedusers = array(); // Static cache, we need to check on each page load, not only every 2 minutes.
01574 
01575     if (!isset($user->id)) {
01576         throw new coding_exception('Invalid $user parameter in check_user_preferences_loaded() call, missing id field');
01577     }
01578 
01579     if (empty($user->id) or isguestuser($user->id)) {
01580         // No permanent storage for not-logged-in users and guest
01581         if (!isset($user->preference)) {
01582             $user->preference = array();
01583         }
01584         return;
01585     }
01586 
01587     $timenow = time();
01588 
01589     if (isset($loadedusers[$user->id]) and isset($user->preference) and isset($user->preference['_lastloaded'])) {
01590         // Already loaded at least once on this page. Are we up to date?
01591         if ($user->preference['_lastloaded'] + $cachelifetime > $timenow) {
01592             // no need to reload - we are on the same page and we loaded prefs just a moment ago
01593             return;
01594 
01595         } else if (!get_cache_flag('userpreferenceschanged', $user->id, $user->preference['_lastloaded'])) {
01596             // no change since the lastcheck on this page
01597             $user->preference['_lastloaded'] = $timenow;
01598             return;
01599         }
01600     }
01601 
01602     // OK, so we have to reload all preferences
01603     $loadedusers[$user->id] = true;
01604     $user->preference = $DB->get_records_menu('user_preferences', array('userid'=>$user->id), '', 'name,value'); // All values
01605     $user->preference['_lastloaded'] = $timenow;
01606 }
01607 
01617 function mark_user_preferences_changed($userid) {
01618     global $CFG;
01619 
01620     if (empty($userid) or isguestuser($userid)) {
01621         // no cache flags for guest and not-logged-in users
01622         return;
01623     }
01624 
01625     set_cache_flag('userpreferenceschanged', $userid, 1, time() + $CFG->sessiontimeout);
01626 }
01627 
01639 function set_user_preference($name, $value, $user = null) {
01640     global $USER, $DB;
01641 
01642     if (empty($name) or is_numeric($name) or $name === '_lastloaded') {
01643         throw new coding_exception('Invalid preference name in set_user_preference() call');
01644     }
01645 
01646     if (is_null($value)) {
01647         // null means delete current
01648         return unset_user_preference($name, $user);
01649     } else if (is_object($value)) {
01650         throw new coding_exception('Invalid value in set_user_preference() call, objects are not allowed');
01651     } else if (is_array($value)) {
01652         throw new coding_exception('Invalid value in set_user_preference() call, arrays are not allowed');
01653     }
01654     $value = (string)$value;
01655     if (textlib::strlen($value) > 1333) { //value column maximum length is 1333 characters
01656         throw new coding_exception('Invalid value in set_user_preference() call, value is is too long for the value column');
01657     }
01658 
01659     if (is_null($user)) {
01660         $user = $USER;
01661     } else if (isset($user->id)) {
01662         // $user is valid object
01663     } else if (is_numeric($user)) {
01664         $user = (object)array('id'=>(int)$user);
01665     } else {
01666         throw new coding_exception('Invalid $user parameter in set_user_preference() call');
01667     }
01668 
01669     check_user_preferences_loaded($user);
01670 
01671     if (empty($user->id) or isguestuser($user->id)) {
01672         // no permanent storage for not-logged-in users and guest
01673         $user->preference[$name] = $value;
01674         return true;
01675     }
01676 
01677     if ($preference = $DB->get_record('user_preferences', array('userid'=>$user->id, 'name'=>$name))) {
01678         if ($preference->value === $value and isset($user->preference[$name]) and $user->preference[$name] === $value) {
01679             // preference already set to this value
01680             return true;
01681         }
01682         $DB->set_field('user_preferences', 'value', $value, array('id'=>$preference->id));
01683 
01684     } else {
01685         $preference = new stdClass();
01686         $preference->userid = $user->id;
01687         $preference->name   = $name;
01688         $preference->value  = $value;
01689         $DB->insert_record('user_preferences', $preference);
01690     }
01691 
01692     // update value in cache
01693     $user->preference[$name] = $value;
01694 
01695     // set reload flag for other sessions
01696     mark_user_preferences_changed($user->id);
01697 
01698     return true;
01699 }
01700 
01710 function set_user_preferences(array $prefarray, $user = null) {
01711     foreach ($prefarray as $name => $value) {
01712         set_user_preference($name, $value, $user);
01713     }
01714     return true;
01715 }
01716 
01726 function unset_user_preference($name, $user = null) {
01727     global $USER, $DB;
01728 
01729     if (empty($name) or is_numeric($name) or $name === '_lastloaded') {
01730         throw new coding_exception('Invalid preference name in unset_user_preference() call');
01731     }
01732 
01733     if (is_null($user)) {
01734         $user = $USER;
01735     } else if (isset($user->id)) {
01736         // $user is valid object
01737     } else if (is_numeric($user)) {
01738         $user = (object)array('id'=>(int)$user);
01739     } else {
01740         throw new coding_exception('Invalid $user parameter in unset_user_preference() call');
01741     }
01742 
01743     check_user_preferences_loaded($user);
01744 
01745     if (empty($user->id) or isguestuser($user->id)) {
01746         // no permanent storage for not-logged-in user and guest
01747         unset($user->preference[$name]);
01748         return true;
01749     }
01750 
01751     // delete from DB
01752     $DB->delete_records('user_preferences', array('userid'=>$user->id, 'name'=>$name));
01753 
01754     // delete the preference from cache
01755     unset($user->preference[$name]);
01756 
01757     // set reload flag for other sessions
01758     mark_user_preferences_changed($user->id);
01759 
01760     return true;
01761 }
01762 
01781 function get_user_preferences($name = null, $default = null, $user = null) {
01782     global $USER;
01783 
01784     if (is_null($name)) {
01785         // all prefs
01786     } else if (is_numeric($name) or $name === '_lastloaded') {
01787         throw new coding_exception('Invalid preference name in get_user_preferences() call');
01788     }
01789 
01790     if (is_null($user)) {
01791         $user = $USER;
01792     } else if (isset($user->id)) {
01793         // $user is valid object
01794     } else if (is_numeric($user)) {
01795         $user = (object)array('id'=>(int)$user);
01796     } else {
01797         throw new coding_exception('Invalid $user parameter in get_user_preferences() call');
01798     }
01799 
01800     check_user_preferences_loaded($user);
01801 
01802     if (empty($name)) {
01803         return $user->preference; // All values
01804     } else if (isset($user->preference[$name])) {
01805         return $user->preference[$name]; // The single string value
01806     } else {
01807         return $default; // Default value (null if not specified)
01808     }
01809 }
01810 
01812 
01828 function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) {
01829 
01830     //save input timezone, required for dst offset check.
01831     $passedtimezone = $timezone;
01832 
01833     $timezone = get_user_timezone_offset($timezone);
01834 
01835     if (abs($timezone) > 13) {  //server time
01836         $time = mktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year);
01837     } else {
01838         $time = gmmktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year);
01839         $time = usertime($time, $timezone);
01840 
01841         //Apply dst for string timezones or if 99 then try dst offset with user's default timezone
01842         if ($applydst && ((99 == $passedtimezone) || !is_numeric($passedtimezone))) {
01843             $time -= dst_offset_on($time, $passedtimezone);
01844         }
01845     }
01846 
01847     return $time;
01848 
01849 }
01850 
01865  function format_time($totalsecs, $str=NULL) {
01866 
01867     $totalsecs = abs($totalsecs);
01868 
01869     if (!$str) {  // Create the str structure the slow way
01870         $str = new stdClass();
01871         $str->day   = get_string('day');
01872         $str->days  = get_string('days');
01873         $str->hour  = get_string('hour');
01874         $str->hours = get_string('hours');
01875         $str->min   = get_string('min');
01876         $str->mins  = get_string('mins');
01877         $str->sec   = get_string('sec');
01878         $str->secs  = get_string('secs');
01879         $str->year  = get_string('year');
01880         $str->years = get_string('years');
01881     }
01882 
01883 
01884     $years     = floor($totalsecs/YEARSECS);
01885     $remainder = $totalsecs - ($years*YEARSECS);
01886     $days      = floor($remainder/DAYSECS);
01887     $remainder = $totalsecs - ($days*DAYSECS);
01888     $hours     = floor($remainder/HOURSECS);
01889     $remainder = $remainder - ($hours*HOURSECS);
01890     $mins      = floor($remainder/MINSECS);
01891     $secs      = $remainder - ($mins*MINSECS);
01892 
01893     $ss = ($secs == 1)  ? $str->sec  : $str->secs;
01894     $sm = ($mins == 1)  ? $str->min  : $str->mins;
01895     $sh = ($hours == 1) ? $str->hour : $str->hours;
01896     $sd = ($days == 1)  ? $str->day  : $str->days;
01897     $sy = ($years == 1)  ? $str->year  : $str->years;
01898 
01899     $oyears = '';
01900     $odays = '';
01901     $ohours = '';
01902     $omins = '';
01903     $osecs = '';
01904 
01905     if ($years)  $oyears  = $years .' '. $sy;
01906     if ($days)  $odays  = $days .' '. $sd;
01907     if ($hours) $ohours = $hours .' '. $sh;
01908     if ($mins)  $omins  = $mins .' '. $sm;
01909     if ($secs)  $osecs  = $secs .' '. $ss;
01910 
01911     if ($years) return trim($oyears .' '. $odays);
01912     if ($days)  return trim($odays .' '. $ohours);
01913     if ($hours) return trim($ohours .' '. $omins);
01914     if ($mins)  return trim($omins .' '. $osecs);
01915     if ($secs)  return $osecs;
01916     return get_string('now');
01917 }
01918 
01941 function userdate($date, $format = '', $timezone = 99, $fixday = true) {
01942 
01943     global $CFG;
01944 
01945     if (empty($format)) {
01946         $format = get_string('strftimedaydatetime', 'langconfig');
01947     }
01948 
01949     if (!empty($CFG->nofixday)) {  // Config.php can force %d not to be fixed.
01950         $fixday = false;
01951     } else if ($fixday) {
01952         $formatnoday = str_replace('%d', 'DD', $format);
01953         $fixday = ($formatnoday != $format);
01954     }
01955 
01956     //add daylight saving offset for string timezones only, as we can't get dst for
01957     //float values. if timezone is 99 (user default timezone), then try update dst.
01958     if ((99 == $timezone) || !is_numeric($timezone)) {
01959         $date += dst_offset_on($date, $timezone);
01960     }
01961 
01962     $timezone = get_user_timezone_offset($timezone);
01963 
01964     if (abs($timezone) > 13) {   
01965         if ($fixday) {
01966             $datestring = strftime($formatnoday, $date);
01967             $daystring  = ltrim(str_replace(array(' 0', ' '), '', strftime(' %d', $date)));
01968             $datestring = str_replace('DD', $daystring, $datestring);
01969         } else {
01970             $datestring = strftime($format, $date);
01971         }
01972     } else {
01973         $date += (int)($timezone * 3600);
01974         if ($fixday) {
01975             $datestring = gmstrftime($formatnoday, $date);
01976             $daystring  = ltrim(str_replace(array(' 0', ' '), '', gmstrftime(' %d', $date)));
01977             $datestring = str_replace('DD', $daystring, $datestring);
01978         } else {
01979             $datestring = gmstrftime($format, $date);
01980         }
01981     }
01982 
01985 
01986    if ($CFG->ostype == 'WINDOWS') {
01987        if ($localewincharset = get_string('localewincharset', 'langconfig')) {
01988            $textlib = textlib_get_instance();
01989            $datestring = $textlib->convert($datestring, $localewincharset, 'utf-8');
01990        }
01991    }
01992 
01993     return $datestring;
01994 }
01995 
02007 function usergetdate($time, $timezone=99) {
02008 
02009     //save input timezone, required for dst offset check.
02010     $passedtimezone = $timezone;
02011 
02012     $timezone = get_user_timezone_offset($timezone);
02013 
02014     if (abs($timezone) > 13) {    // Server time
02015         return getdate($time);
02016     }
02017 
02018     //add daylight saving offset for string timezones only, as we can't get dst for
02019     //float values. if timezone is 99 (user default timezone), then try update dst.
02020     if ($passedtimezone == 99 || !is_numeric($passedtimezone)) {
02021         $time += dst_offset_on($time, $passedtimezone);
02022     }
02023 
02024     $time += intval((float)$timezone * HOURSECS);
02025 
02026     $datestring = gmstrftime('%B_%A_%j_%Y_%m_%w_%d_%H_%M_%S', $time);
02027 
02028     //be careful to ensure the returned array matches that produced by getdate() above
02029     list(
02030         $getdate['month'],
02031         $getdate['weekday'],
02032         $getdate['yday'],
02033         $getdate['year'],
02034         $getdate['mon'],
02035         $getdate['wday'],
02036         $getdate['mday'],
02037         $getdate['hours'],
02038         $getdate['minutes'],
02039         $getdate['seconds']
02040     ) = explode('_', $datestring);
02041 
02042     return $getdate;
02043 }
02044 
02054 function usertime($date, $timezone=99) {
02055 
02056     $timezone = get_user_timezone_offset($timezone);
02057 
02058     if (abs($timezone) > 13) {
02059         return $date;
02060     }
02061     return $date - (int)($timezone * HOURSECS);
02062 }
02063 
02072 function usergetmidnight($date, $timezone=99) {
02073 
02074     $userdate = usergetdate($date, $timezone);
02075 
02076     // Time of midnight of this user's day, in GMT
02077     return make_timestamp($userdate['year'], $userdate['mon'], $userdate['mday'], 0, 0, 0, $timezone);
02078 
02079 }
02080 
02087 function usertimezone($timezone=99) {
02088 
02089     $tz = get_user_timezone($timezone);
02090 
02091     if (!is_float($tz)) {
02092         return $tz;
02093     }
02094 
02095     if(abs($tz) > 13) { // Server time
02096         return get_string('serverlocaltime');
02097     }
02098 
02099     if($tz == intval($tz)) {
02100         // Don't show .0 for whole hours
02101         $tz = intval($tz);
02102     }
02103 
02104     if($tz == 0) {
02105         return 'UTC';
02106     }
02107     else if($tz > 0) {
02108         return 'UTC+'.$tz;
02109     }
02110     else {
02111         return 'UTC'.$tz;
02112     }
02113 
02114 }
02115 
02125 function get_user_timezone_offset($tz = 99) {
02126 
02127     global $USER, $CFG;
02128 
02129     $tz = get_user_timezone($tz);
02130 
02131     if (is_float($tz)) {
02132         return $tz;
02133     } else {
02134         $tzrecord = get_timezone_record($tz);
02135         if (empty($tzrecord)) {
02136             return 99.0;
02137         }
02138         return (float)$tzrecord->gmtoff / HOURMINS;
02139     }
02140 }
02141 
02149 function get_timezone_offset($tz) {
02150     global $CFG;
02151 
02152     if ($tz == 99) {
02153         return false;
02154     }
02155 
02156     if (is_numeric($tz)) {
02157         return intval($tz * 60*60);
02158     }
02159 
02160     if (!$tzrecord = get_timezone_record($tz)) {
02161         return false;
02162     }
02163     return intval($tzrecord->gmtoff * 60);
02164 }
02165 
02177 function get_user_timezone($tz = 99) {
02178     global $USER, $CFG;
02179 
02180     $timezones = array(
02181         $tz,
02182         isset($CFG->forcetimezone) ? $CFG->forcetimezone : 99,
02183         isset($USER->timezone) ? $USER->timezone : 99,
02184         isset($CFG->timezone) ? $CFG->timezone : 99,
02185         );
02186 
02187     $tz = 99;
02188 
02189     while(($tz == '' || $tz == 99 || $tz == NULL) && $next = each($timezones)) {
02190         $tz = $next['value'];
02191     }
02192 
02193     return is_numeric($tz) ? (float) $tz : $tz;
02194 }
02195 
02204 function get_timezone_record($timezonename) {
02205     global $CFG, $DB;
02206     static $cache = NULL;
02207 
02208     if ($cache === NULL) {
02209         $cache = array();
02210     }
02211 
02212     if (isset($cache[$timezonename])) {
02213         return $cache[$timezonename];
02214     }
02215 
02216     return $cache[$timezonename] = $DB->get_record_sql('SELECT * FROM {timezone}
02217                                                         WHERE name = ? ORDER BY year DESC', array($timezonename), IGNORE_MULTIPLE);
02218 }
02219 
02231 function calculate_user_dst_table($from_year = NULL, $to_year = NULL, $strtimezone = NULL) {
02232     global $CFG, $SESSION, $DB;
02233 
02234     $usertz = get_user_timezone($strtimezone);
02235 
02236     if (is_float($usertz)) {
02237         // Trivial timezone, no DST
02238         return false;
02239     }
02240 
02241     if (!empty($SESSION->dst_offsettz) && $SESSION->dst_offsettz != $usertz) {
02242         // We have precalculated values, but the user's effective TZ has changed in the meantime, so reset
02243         unset($SESSION->dst_offsets);
02244         unset($SESSION->dst_range);
02245     }
02246 
02247     if (!empty($SESSION->dst_offsets) && empty($from_year) && empty($to_year)) {
02248         // Repeat calls which do not request specific year ranges stop here, we have already calculated the table
02249         // This will be the return path most of the time, pretty light computationally
02250         return true;
02251     }
02252 
02253     // Reaching here means we either need to extend our table or create it from scratch
02254 
02255     // Remember which TZ we calculated these changes for
02256     $SESSION->dst_offsettz = $usertz;
02257 
02258     if(empty($SESSION->dst_offsets)) {
02259         // If we 're creating from scratch, put the two guard elements in there
02260         $SESSION->dst_offsets = array(1 => NULL, 0 => NULL);
02261     }
02262     if(empty($SESSION->dst_range)) {
02263         // If creating from scratch
02264         $from = max((empty($from_year) ? intval(date('Y')) - 3 : $from_year), 1971);
02265         $to   = min((empty($to_year)   ? intval(date('Y')) + 3 : $to_year),   2035);
02266 
02267         // Fill in the array with the extra years we need to process
02268         $yearstoprocess = array();
02269         for($i = $from; $i <= $to; ++$i) {
02270             $yearstoprocess[] = $i;
02271         }
02272 
02273         // Take note of which years we have processed for future calls
02274         $SESSION->dst_range = array($from, $to);
02275     }
02276     else {
02277         // If needing to extend the table, do the same
02278         $yearstoprocess = array();
02279 
02280         $from = max((empty($from_year) ? $SESSION->dst_range[0] : $from_year), 1971);
02281         $to   = min((empty($to_year)   ? $SESSION->dst_range[1] : $to_year),   2035);
02282 
02283         if($from < $SESSION->dst_range[0]) {
02284             // Take note of which years we need to process and then note that we have processed them for future calls
02285             for($i = $from; $i < $SESSION->dst_range[0]; ++$i) {
02286                 $yearstoprocess[] = $i;
02287             }
02288             $SESSION->dst_range[0] = $from;
02289         }
02290         if($to > $SESSION->dst_range[1]) {
02291             // Take note of which years we need to process and then note that we have processed them for future calls
02292             for($i = $SESSION->dst_range[1] + 1; $i <= $to; ++$i) {
02293                 $yearstoprocess[] = $i;
02294             }
02295             $SESSION->dst_range[1] = $to;
02296         }
02297     }
02298 
02299     if(empty($yearstoprocess)) {
02300         // This means that there was a call requesting a SMALLER range than we have already calculated
02301         return true;
02302     }
02303 
02304     // From now on, we know that the array has at least the two guard elements, and $yearstoprocess has the years we need
02305     // Also, the array is sorted in descending timestamp order!
02306 
02307     // Get DB data
02308 
02309     static $presets_cache = array();
02310     if (!isset($presets_cache[$usertz])) {
02311         $presets_cache[$usertz] = $DB->get_records('timezone', array('name'=>$usertz), 'year DESC', 'year, gmtoff, dstoff, dst_month, dst_startday, dst_weekday, dst_skipweeks, dst_time, std_month, std_startday, std_weekday, std_skipweeks, std_time');
02312     }
02313     if(empty($presets_cache[$usertz])) {
02314         return false;
02315     }
02316 
02317     // Remove ending guard (first element of the array)
02318     reset($SESSION->dst_offsets);
02319     unset($SESSION->dst_offsets[key($SESSION->dst_offsets)]);
02320 
02321     // Add all required change timestamps
02322     foreach($yearstoprocess as $y) {
02323         // Find the record which is in effect for the year $y
02324         foreach($presets_cache[$usertz] as $year => $preset) {
02325             if($year <= $y) {
02326                 break;
02327             }
02328         }
02329 
02330         $changes = dst_changes_for_year($y, $preset);
02331 
02332         if($changes === NULL) {
02333             continue;
02334         }
02335         if($changes['dst'] != 0) {
02336             $SESSION->dst_offsets[$changes['dst']] = $preset->dstoff * MINSECS;
02337         }
02338         if($changes['std'] != 0) {
02339             $SESSION->dst_offsets[$changes['std']] = 0;
02340         }
02341     }
02342 
02343     // Put in a guard element at the top
02344     $maxtimestamp = max(array_keys($SESSION->dst_offsets));
02345     $SESSION->dst_offsets[($maxtimestamp + DAYSECS)] = NULL; // DAYSECS is arbitrary, any "small" number will do
02346 
02347     // Sort again
02348     krsort($SESSION->dst_offsets);
02349 
02350     return true;
02351 }
02352 
02362 function dst_changes_for_year($year, $timezone) {
02363 
02364     if($timezone->dst_startday == 0 && $timezone->dst_weekday == 0 && $timezone->std_startday == 0 && $timezone->std_weekday == 0) {
02365         return NULL;
02366     }
02367 
02368     $monthdaydst = find_day_in_month($timezone->dst_startday, $timezone->dst_weekday, $timezone->dst_month, $year);
02369     $monthdaystd = find_day_in_month($timezone->std_startday, $timezone->std_weekday, $timezone->std_month, $year);
02370 
02371     list($dst_hour, $dst_min) = explode(':', $timezone->dst_time);
02372     list($std_hour, $std_min) = explode(':', $timezone->std_time);
02373 
02374     $timedst = make_timestamp($year, $timezone->dst_month, $monthdaydst, 0, 0, 0, 99, false);
02375     $timestd = make_timestamp($year, $timezone->std_month, $monthdaystd, 0, 0, 0, 99, false);
02376 
02377     // Instead of putting hour and minute in make_timestamp(), we add them afterwards.
02378     // This has the advantage of being able to have negative values for hour, i.e. for timezones
02379     // where GMT time would be in the PREVIOUS day than the local one on which DST changes.
02380 
02381     $timedst += $dst_hour * HOURSECS + $dst_min * MINSECS;
02382     $timestd += $std_hour * HOURSECS + $std_min * MINSECS;
02383 
02384     return array('dst' => $timedst, 0 => $timedst, 'std' => $timestd, 1 => $timestd);
02385 }
02386 
02397 function dst_offset_on($time, $strtimezone = NULL) {
02398     global $SESSION;
02399 
02400     if(!calculate_user_dst_table(NULL, NULL, $strtimezone) || empty($SESSION->dst_offsets)) {
02401         return 0;
02402     }
02403 
02404     reset($SESSION->dst_offsets);
02405     while(list($from, $offset) = each($SESSION->dst_offsets)) {
02406         if($from <= $time) {
02407             break;
02408         }
02409     }
02410 
02411     // This is the normal return path
02412     if($offset !== NULL) {
02413         return $offset;
02414     }
02415 
02416     // Reaching this point means we haven't calculated far enough, do it now:
02417     // Calculate extra DST changes if needed and recurse. The recursion always
02418     // moves toward the stopping condition, so will always end.
02419 
02420     if($from == 0) {
02421         // We need a year smaller than $SESSION->dst_range[0]
02422         if($SESSION->dst_range[0] == 1971) {
02423             return 0;
02424         }
02425         calculate_user_dst_table($SESSION->dst_range[0] - 5, NULL, $strtimezone);
02426         return dst_offset_on($time, $strtimezone);
02427     }
02428     else {
02429         // We need a year larger than $SESSION->dst_range[1]
02430         if($SESSION->dst_range[1] == 2035) {
02431             return 0;
02432         }
02433         calculate_user_dst_table(NULL, $SESSION->dst_range[1] + 5, $strtimezone);
02434         return dst_offset_on($time, $strtimezone);
02435     }
02436 }
02437 
02448 function find_day_in_month($startday, $weekday, $month, $year) {
02449 
02450     $daysinmonth = days_in_month($month, $year);
02451 
02452     if($weekday == -1) {
02453         // Don't care about weekday, so return:
02454         //    abs($startday) if $startday != -1
02455         //    $daysinmonth otherwise
02456         return ($startday == -1) ? $daysinmonth : abs($startday);
02457     }
02458 
02459     // From now on we 're looking for a specific weekday
02460 
02461     // Give "end of month" its actual value, since we know it
02462     if($startday == -1) {
02463         $startday = -1 * $daysinmonth;
02464     }
02465 
02466     // Starting from day $startday, the sign is the direction
02467 
02468     if($startday < 1) {
02469 
02470         $startday = abs($startday);
02471         $lastmonthweekday  = strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year));
02472 
02473         // This is the last such weekday of the month
02474         $lastinmonth = $daysinmonth + $weekday - $lastmonthweekday;
02475         if($lastinmonth > $daysinmonth) {
02476             $lastinmonth -= 7;
02477         }
02478 
02479         // Find the first such weekday <= $startday
02480         while($lastinmonth > $startday) {
02481             $lastinmonth -= 7;
02482         }
02483 
02484         return $lastinmonth;
02485 
02486     }
02487     else {
02488 
02489         $indexweekday = strftime('%w', mktime(12, 0, 0, $month, $startday, $year));
02490 
02491         $diff = $weekday - $indexweekday;
02492         if($diff < 0) {
02493             $diff += 7;
02494         }
02495 
02496         // This is the first such weekday of the month equal to or after $startday
02497         $firstfromindex = $startday + $diff;
02498 
02499         return $firstfromindex;
02500 
02501     }
02502 }
02503 
02511 function days_in_month($month, $year) {
02512    return intval(date('t', mktime(12, 0, 0, $month, 1, $year)));
02513 }
02514 
02523 function dayofweek($day, $month, $year) {
02524     // I wonder if this is any different from
02525     // strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0));
02526     return intval(date('w', mktime(12, 0, 0, $month, $day, $year)));
02527 }
02528 
02530 
02536 function get_login_url() {
02537     global $CFG;
02538 
02539     $url = "$CFG->wwwroot/login/index.php";
02540 
02541     if (!empty($CFG->loginhttps)) {
02542         $url = str_replace('http:', 'https:', $url);
02543     }
02544 
02545     return $url;
02546 }
02547 
02575 function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = false) {
02576     global $CFG, $SESSION, $USER, $FULLME, $PAGE, $SITE, $DB, $OUTPUT;
02577 
02578     // setup global $COURSE, themes, language and locale
02579     if (!empty($courseorid)) {
02580         if (is_object($courseorid)) {
02581             $course = $courseorid;
02582         } else if ($courseorid == SITEID) {
02583             $course = clone($SITE);
02584         } else {
02585             $course = $DB->get_record('course', array('id' => $courseorid), '*', MUST_EXIST);
02586         }
02587         if ($cm) {
02588             if ($cm->course != $course->id) {
02589                 throw new coding_exception('course and cm parameters in require_login() call do not match!!');
02590             }
02591             // make sure we have a $cm from get_fast_modinfo as this contains activity access details
02592             if (!($cm instanceof cm_info)) {
02593                 // note: nearly all pages call get_fast_modinfo anyway and it does not make any
02594                 // db queries so this is not really a performance concern, however it is obviously
02595                 // better if you use get_fast_modinfo to get the cm before calling this.
02596                 $modinfo = get_fast_modinfo($course);
02597                 $cm = $modinfo->get_cm($cm->id);
02598             }
02599             $PAGE->set_cm($cm, $course); // set's up global $COURSE
02600             $PAGE->set_pagelayout('incourse');
02601         } else {
02602             $PAGE->set_course($course); // set's up global $COURSE
02603         }
02604     } else {
02605         // do not touch global $COURSE via $PAGE->set_course(),
02606         // the reasons is we need to be able to call require_login() at any time!!
02607         $course = $SITE;
02608         if ($cm) {
02609             throw new coding_exception('cm parameter in require_login() requires valid course parameter!');
02610         }
02611     }
02612 
02613     // If the user is not even logged in yet then make sure they are
02614     if (!isloggedin()) {
02615         if ($autologinguest and !empty($CFG->guestloginbutton) and !empty($CFG->autologinguests)) {
02616             if (!$guest = get_complete_user_data('id', $CFG->siteguest)) {
02617                 // misconfigured site guest, just redirect to login page
02618                 redirect(get_login_url());
02619                 exit; // never reached
02620             }
02621             $lang = isset($SESSION->lang) ? $SESSION->lang : $CFG->lang;
02622             complete_user_login($guest);
02623             $USER->autologinguest = true;
02624             $SESSION->lang = $lang;
02625         } else {
02626             //NOTE: $USER->site check was obsoleted by session test cookie,
02627             //      $USER->confirmed test is in login/index.php
02628             if ($preventredirect) {
02629                 throw new require_login_exception('You are not logged in');
02630             }
02631 
02632             if ($setwantsurltome) {
02633                 // TODO: switch to PAGE->url
02634                 $SESSION->wantsurl = $FULLME;
02635             }
02636             if (!empty($_SERVER['HTTP_REFERER'])) {
02637                 $SESSION->fromurl  = $_SERVER['HTTP_REFERER'];
02638             }
02639             redirect(get_login_url());
02640             exit; // never reached
02641         }
02642     }
02643 
02644     // loginas as redirection if needed
02645     if ($course->id != SITEID and session_is_loggedinas()) {
02646         if ($USER->loginascontext->contextlevel == CONTEXT_COURSE) {
02647             if ($USER->loginascontext->instanceid != $course->id) {
02648                 print_error('loginasonecourse', '', $CFG->wwwroot.'/course/view.php?id='.$USER->loginascontext->instanceid);
02649             }
02650         }
02651     }
02652 
02653     // check whether the user should be changing password (but only if it is REALLY them)
02654     if (get_user_preferences('auth_forcepasswordchange') && !session_is_loggedinas()) {
02655         $userauth = get_auth_plugin($USER->auth);
02656         if ($userauth->can_change_password() and !$preventredirect) {
02657             $SESSION->wantsurl = $FULLME;
02658             if ($changeurl = $userauth->change_password_url()) {
02659                 //use plugin custom url
02660                 redirect($changeurl);
02661             } else {
02662                 //use moodle internal method
02663                 if (empty($CFG->loginhttps)) {
02664                     redirect($CFG->wwwroot .'/login/change_password.php');
02665                 } else {
02666                     $wwwroot = str_replace('http:','https:', $CFG->wwwroot);
02667                     redirect($wwwroot .'/login/change_password.php');
02668                 }
02669             }
02670         } else {
02671             print_error('nopasswordchangeforced', 'auth');
02672         }
02673     }
02674 
02675     // Check that the user account is properly set up
02676     if (user_not_fully_set_up($USER)) {
02677         if ($preventredirect) {
02678             throw new require_login_exception('User not fully set-up');
02679         }
02680         $SESSION->wantsurl = $FULLME;
02681         redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&amp;course='. SITEID);
02682     }
02683 
02684     // Make sure the USER has a sesskey set up. Used for CSRF protection.
02685     sesskey();
02686 
02687     // Do not bother admins with any formalities
02688     if (is_siteadmin()) {
02689         //set accesstime or the user will appear offline which messes up messaging
02690         user_accesstime_log($course->id);
02691         return;
02692     }
02693 
02694     // Check that the user has agreed to a site policy if there is one - do not test in case of admins
02695     if (!$USER->policyagreed and !is_siteadmin()) {
02696         if (!empty($CFG->sitepolicy) and !isguestuser()) {
02697             if ($preventredirect) {
02698                 throw new require_login_exception('Policy not agreed');
02699             }
02700             $SESSION->wantsurl = $FULLME;
02701             redirect($CFG->wwwroot .'/user/policy.php');
02702         } else if (!empty($CFG->sitepolicyguest) and isguestuser()) {
02703             if ($preventredirect) {
02704                 throw new require_login_exception('Policy not agreed');
02705             }
02706             $SESSION->wantsurl = $FULLME;
02707             redirect($CFG->wwwroot .'/user/policy.php');
02708         }
02709     }
02710 
02711     // Fetch the system context, the course context, and prefetch its child contexts
02712     $sysctx = get_context_instance(CONTEXT_SYSTEM);
02713     $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
02714     if ($cm) {
02715         $cmcontext = get_context_instance(CONTEXT_MODULE, $cm->id, MUST_EXIST);
02716     } else {
02717         $cmcontext = null;
02718     }
02719 
02720     // If the site is currently under maintenance, then print a message
02721     if (!empty($CFG->maintenance_enabled) and !has_capability('moodle/site:config', $sysctx)) {
02722         if ($preventredirect) {
02723             throw new require_login_exception('Maintenance in progress');
02724         }
02725 
02726         print_maintenance_message();
02727     }
02728 
02729     // make sure the course itself is not hidden
02730     if ($course->id == SITEID) {
02731         // frontpage can not be hidden
02732     } else {
02733         if (is_role_switched($course->id)) {
02734             // when switching roles ignore the hidden flag - user had to be in course to do the switch
02735         } else {
02736             if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
02737                 // originally there was also test of parent category visibility,
02738                 // BUT is was very slow in complex queries involving "my courses"
02739                 // now it is also possible to simply hide all courses user is not enrolled in :-)
02740                 if ($preventredirect) {
02741                     throw new require_login_exception('Course is hidden');
02742                 }
02743                 // We need to override the navigation URL as the course won't have
02744                 // been added to the navigation and thus the navigation will mess up
02745                 // when trying to find it.
02746                 navigation_node::override_active_url(new moodle_url('/'));
02747                 notice(get_string('coursehidden'), $CFG->wwwroot .'/');
02748             }
02749         }
02750     }
02751 
02752     // is the user enrolled?
02753     if ($course->id == SITEID) {
02754         // everybody is enrolled on the frontpage
02755 
02756     } else {
02757         if (session_is_loggedinas()) {
02758             // Make sure the REAL person can access this course first
02759             $realuser = session_get_realuser();
02760             if (!is_enrolled($coursecontext, $realuser->id, '', true) and !is_viewing($coursecontext, $realuser->id) and !is_siteadmin($realuser->id)) {
02761                 if ($preventredirect) {
02762                     throw new require_login_exception('Invalid course login-as access');
02763                 }
02764                 echo $OUTPUT->header();
02765                 notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/');
02766             }
02767         }
02768 
02769         $access = false;
02770 
02771         if (is_role_switched($course->id)) {
02772             // ok, user had to be inside this course before the switch
02773             $access = true;
02774 
02775         } else if (is_viewing($coursecontext, $USER)) {
02776             // ok, no need to mess with enrol
02777             $access = true;
02778 
02779         } else {
02780             if (isset($USER->enrol['enrolled'][$course->id])) {
02781                 if ($USER->enrol['enrolled'][$course->id] > time()) {
02782                     $access = true;
02783                     if (isset($USER->enrol['tempguest'][$course->id])) {
02784                         unset($USER->enrol['tempguest'][$course->id]);
02785                         remove_temp_course_roles($coursecontext);
02786                     }
02787                 } else {
02788                     //expired
02789                     unset($USER->enrol['enrolled'][$course->id]);
02790                 }
02791             }
02792             if (isset($USER->enrol['tempguest'][$course->id])) {
02793                 if ($USER->enrol['tempguest'][$course->id] == 0) {
02794                     $access = true;
02795                 } else if ($USER->enrol['tempguest'][$course->id] > time()) {
02796                     $access = true;
02797                 } else {
02798                     //expired
02799                     unset($USER->enrol['tempguest'][$course->id]);
02800                     remove_temp_course_roles($coursecontext);
02801                 }
02802             }
02803 
02804             if ($access) {
02805                 // cache ok
02806             } else {
02807                 $until = enrol_get_enrolment_end($coursecontext->instanceid, $USER->id);
02808                 if ($until !== false) {
02809                     // active participants may always access, a timestamp in the future, 0 (always) or false.
02810                     if ($until == 0) {
02811                         $until = ENROL_MAX_TIMESTAMP;
02812                     }
02813                     $USER->enrol['enrolled'][$course->id] = $until;
02814                     $access = true;
02815 
02816                 } else {
02817                     $instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC');
02818                     $enrols = enrol_get_plugins(true);
02819                     // first ask all enabled enrol instances in course if they want to auto enrol user
02820                     foreach($instances as $instance) {
02821                         if (!isset($enrols[$instance->enrol])) {
02822                             continue;
02823                         }
02824                         // Get a duration for the enrolment, a timestamp in the future, 0 (always) or false.
02825                         $until = $enrols[$instance->enrol]->try_autoenrol($instance);
02826                         if ($until !== false) {
02827                             if ($until == 0) {
02828                                 $until = ENROL_MAX_TIMESTAMP;
02829                             }
02830                             $USER->enrol['enrolled'][$course->id] = $until;
02831                             $access = true;
02832                             break;
02833                         }
02834                     }
02835                     // if not enrolled yet try to gain temporary guest access
02836                     if (!$access) {
02837                         foreach($instances as $instance) {
02838                             if (!isset($enrols[$instance->enrol])) {
02839                                 continue;
02840                             }
02841                             // Get a duration for the guest access, a timestamp in the future or false.
02842                             $until = $enrols[$instance->enrol]->try_guestaccess($instance);
02843                             if ($until !== false and $until > time()) {
02844                                 $USER->enrol['tempguest'][$course->id] = $until;
02845                                 $access = true;
02846                                 break;
02847                             }
02848                         }
02849                     }
02850                 }
02851             }
02852         }
02853 
02854         if (!$access) {
02855             if ($preventredirect) {
02856                 throw new require_login_exception('Not enrolled');
02857             }
02858             $SESSION->wantsurl = $FULLME;
02859             redirect($CFG->wwwroot .'/enrol/index.php?id='. $course->id);
02860         }
02861     }
02862 
02863     // Check visibility of activity to current user; includes visible flag, groupmembersonly,
02864     // conditional availability, etc
02865     if ($cm && !$cm->uservisible) {
02866         if ($preventredirect) {
02867             throw new require_login_exception('Activity is hidden');
02868         }
02869         redirect($CFG->wwwroot, get_string('activityiscurrentlyhidden'));
02870     }
02871 
02872     // Finally access granted, update lastaccess times
02873     user_accesstime_log($course->id);
02874 }
02875 
02876 
02882 function require_logout() {
02883     global $USER;
02884 
02885     $params = $USER;
02886 
02887     if (isloggedin()) {
02888         add_to_log(SITEID, "user", "logout", "view.php?id=$USER->id&course=".SITEID, $USER->id, 0, $USER->id);
02889 
02890         $authsequence = get_enabled_auth_plugins(); // auths, in sequence
02891         foreach($authsequence as $authname) {
02892             $authplugin = get_auth_plugin($authname);
02893             $authplugin->prelogout_hook();
02894         }
02895     }
02896 
02897     events_trigger('user_logout', $params);
02898     session_get_instance()->terminate_current();
02899     unset($params);
02900 }
02901 
02920 function require_course_login($courseorid, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = false) {
02921     global $CFG, $PAGE, $SITE;
02922     $issite = (is_object($courseorid) and $courseorid->id == SITEID)
02923           or (!is_object($courseorid) and $courseorid == SITEID);
02924     if ($issite && !empty($cm) && !($cm instanceof cm_info)) {
02925         // note: nearly all pages call get_fast_modinfo anyway and it does not make any
02926         // db queries so this is not really a performance concern, however it is obviously
02927         // better if you use get_fast_modinfo to get the cm before calling this.
02928         if (is_object($courseorid)) {
02929             $course = $courseorid;
02930         } else {
02931             $course = clone($SITE);
02932         }
02933         $modinfo = get_fast_modinfo($course);
02934         $cm = $modinfo->get_cm($cm->id);
02935     }
02936     if (!empty($CFG->forcelogin)) {
02937         // login required for both SITE and courses
02938         require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
02939 
02940     } else if ($issite && !empty($cm) and !$cm->uservisible) {
02941         // always login for hidden activities
02942         require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
02943 
02944     } else if ($issite) {
02945               //login for SITE not required
02946         if ($cm and empty($cm->visible)) {
02947             // hidden activities are not accessible without login
02948             require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
02949         } else if ($cm and !empty($CFG->enablegroupmembersonly) and $cm->groupmembersonly) {
02950             // not-logged-in users do not have any group membership
02951             require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
02952         } else {
02953             // We still need to instatiate PAGE vars properly so that things
02954             // that rely on it like navigation function correctly.
02955             if (!empty($courseorid)) {
02956                 if (is_object($courseorid)) {
02957                     $course = $courseorid;
02958                 } else {
02959                     $course = clone($SITE);
02960                 }
02961                 if ($cm) {
02962                     if ($cm->course != $course->id) {
02963                         throw new coding_exception('course and cm parameters in require_course_login() call do not match!!');
02964                     }
02965                     $PAGE->set_cm($cm, $course);
02966                     $PAGE->set_pagelayout('incourse');
02967                 } else {
02968                     $PAGE->set_course($course);
02969                 }
02970             } else {
02971                 // If $PAGE->course, and hence $PAGE->context, have not already been set
02972                 // up properly, set them up now.
02973                 $PAGE->set_course($PAGE->course);
02974             }
02975             //TODO: verify conditional activities here
02976             user_accesstime_log(SITEID);
02977             return;
02978         }
02979 
02980     } else {
02981         // course login always required
02982         require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
02983     }
02984 }
02985 
02999 function require_user_key_login($script, $instance=null) {
03000     global $USER, $SESSION, $CFG, $DB;
03001 
03002     if (!NO_MOODLE_COOKIES) {
03003         print_error('sessioncookiesdisable');
03004     }
03005 
03007     @session_write_close();
03008 
03009     $keyvalue = required_param('key', PARAM_ALPHANUM);
03010 
03011     if (!$key = $DB->get_record('user_private_key', array('script'=>$script, 'value'=>$keyvalue, 'instance'=>$instance))) {
03012         print_error('invalidkey');
03013     }
03014 
03015     if (!empty($key->validuntil) and $key->validuntil < time()) {
03016         print_error('expiredkey');
03017     }
03018 
03019     if ($key->iprestriction) {
03020         $remoteaddr = getremoteaddr(null);
03021         if (empty($remoteaddr) or !address_in_subnet($remoteaddr, $key->iprestriction)) {
03022             print_error('ipmismatch');
03023         }
03024     }
03025 
03026     if (!$user = $DB->get_record('user', array('id'=>$key->userid))) {
03027         print_error('invaliduserid');
03028     }
03029 
03031     enrol_check_plugins($user);
03032     session_set_user($user);
03033 
03035     if (!defined('USER_KEY_LOGIN')) {
03036         define('USER_KEY_LOGIN', true);
03037     }
03038 
03040     return $key->instance;
03041 }
03042 
03054 function create_user_key($script, $userid, $instance=null, $iprestriction=null, $validuntil=null) {
03055     global $DB;
03056 
03057     $key = new stdClass();
03058     $key->script        = $script;
03059     $key->userid        = $userid;
03060     $key->instance      = $instance;
03061     $key->iprestriction = $iprestriction;
03062     $key->validuntil    = $validuntil;
03063     $key->timecreated   = time();
03064 
03065     $key->value         = md5($userid.'_'.time().random_string(40)); // something long and unique
03066     while ($DB->record_exists('user_private_key', array('value'=>$key->value))) {
03067         // must be unique
03068         $key->value     = md5($userid.'_'.time().random_string(40));
03069     }
03070     $DB->insert_record('user_private_key', $key);
03071     return $key->value;
03072 }
03073 
03082 function delete_user_key($script,$userid) {
03083     global $DB;
03084     $DB->delete_records('user_private_key', array('script'=>$script, 'userid'=>$userid));
03085 }
03086 
03098 function get_user_key($script, $userid, $instance=null, $iprestriction=null, $validuntil=null) {
03099     global $DB;
03100 
03101     if ($key = $DB->get_record('user_private_key', array('script'=>$script, 'userid'=>$userid,
03102                                                          'instance'=>$instance, 'iprestriction'=>$iprestriction,
03103                                                          'validuntil'=>$validuntil))) {
03104         return $key->value;
03105     } else {
03106         return create_user_key($script, $userid, $instance, $iprestriction, $validuntil);
03107     }
03108 }
03109 
03110 
03119 function update_user_login_times() {
03120     global $USER, $DB;
03121 
03122     $user = new stdClass();
03123     $USER->lastlogin = $user->lastlogin = $USER->currentlogin;
03124     $USER->currentlogin = $user->lastaccess = $user->currentlogin = time();
03125 
03126     $user->id = $USER->id;
03127 
03128     $DB->update_record('user', $user);
03129     return true;
03130 }
03131 
03138 function user_not_fully_set_up($user) {
03139     if (isguestuser($user)) {
03140         return false;
03141     }
03142     return (empty($user->firstname) or empty($user->lastname) or empty($user->email) or over_bounce_threshold($user));
03143 }
03144 
03153 function over_bounce_threshold($user) {
03154     global $CFG, $DB;
03155 
03156     if (empty($CFG->handlebounces)) {
03157         return false;
03158     }
03159 
03160     if (empty($user->id)) { 
03161         return false;
03162     }
03163 
03164     // set sensible defaults
03165     if (empty($CFG->minbounces)) {
03166         $CFG->minbounces = 10;
03167     }
03168     if (empty($CFG->bounceratio)) {
03169         $CFG->bounceratio = .20;
03170     }
03171     $bouncecount = 0;
03172     $sendcount = 0;
03173     if ($bounce = $DB->get_record('user_preferences', array ('userid'=>$user->id, 'name'=>'email_bounce_count'))) {
03174         $bouncecount = $bounce->value;
03175     }
03176     if ($send = $DB->get_record('user_preferences', array('userid'=>$user->id, 'name'=>'email_send_count'))) {
03177         $sendcount = $send->value;
03178     }
03179     return ($bouncecount >= $CFG->minbounces && $bouncecount/$sendcount >= $CFG->bounceratio);
03180 }
03181 
03190 function set_send_count($user,$reset=false) {
03191     global $DB;
03192 
03193     if (empty($user->id)) { 
03194         return;
03195     }
03196 
03197     if ($pref = $DB->get_record('user_preferences', array('userid'=>$user->id, 'name'=>'email_send_count'))) {
03198         $pref->value = (!empty($reset)) ? 0 : $pref->value+1;
03199         $DB->update_record('user_preferences', $pref);
03200     }
03201     else if (!empty($reset)) { // if it's not there and we're resetting, don't bother.
03202         // make a new one
03203         $pref = new stdClass();
03204         $pref->name   = 'email_send_count';
03205         $pref->value  = 1;
03206         $pref->userid = $user->id;
03207         $DB->insert_record('user_preferences', $pref, false);
03208     }
03209 }
03210 
03218 function set_bounce_count($user,$reset=false) {
03219     global $DB;
03220 
03221     if ($pref = $DB->get_record('user_preferences', array('userid'=>$user->id, 'name'=>'email_bounce_count'))) {
03222         $pref->value = (!empty($reset)) ? 0 : $pref->value+1;
03223         $DB->update_record('user_preferences', $pref);
03224     }
03225     else if (!empty($reset)) { // if it's not there and we're resetting, don't bother.
03226         // make a new one
03227         $pref = new stdClass();
03228         $pref->name   = 'email_bounce_count';
03229         $pref->value  = 1;
03230         $pref->userid = $user->id;
03231         $DB->insert_record('user_preferences', $pref, false);
03232     }
03233 }
03234 
03240 function update_login_count() {
03241     global $SESSION;
03242 
03243     $max_logins = 10;
03244 
03245     if (empty($SESSION->logincount)) {
03246         $SESSION->logincount = 1;
03247     } else {
03248         $SESSION->logincount++;
03249     }
03250 
03251     if ($SESSION->logincount > $max_logins) {
03252         unset($SESSION->wantsurl);
03253         print_error('errortoomanylogins');
03254     }
03255 }
03256 
03262 function reset_login_count() {
03263     global $SESSION;
03264 
03265     $SESSION->logincount = 0;
03266 }
03267 
03279 function isediting() {
03280     global $PAGE;
03281     debugging('call to deprecated function isediting(). Please use $PAGE->user_is_editing() instead', DEBUG_DEVELOPER);
03282     return $PAGE->user_is_editing();
03283 }
03284 
03292 function ismoving($courseid) {
03293     global $USER;
03294 
03295     if (!empty($USER->activitycopy)) {
03296         return ($USER->activitycopycourse == $courseid);
03297     }
03298     return false;
03299 }
03300 
03317 function fullname($user, $override=false) {
03318     global $CFG, $SESSION;
03319 
03320     if (!isset($user->firstname) and !isset($user->lastname)) {
03321         return '';
03322     }
03323 
03324     if (!$override) {
03325         if (!empty($CFG->forcefirstname)) {
03326             $user->firstname = $CFG->forcefirstname;
03327         }
03328         if (!empty($CFG->forcelastname)) {
03329             $user->lastname = $CFG->forcelastname;
03330         }
03331     }
03332 
03333     if (!empty($SESSION->fullnamedisplay)) {
03334         $CFG->fullnamedisplay = $SESSION->fullnamedisplay;
03335     }
03336 
03337     if (!isset($CFG->fullnamedisplay) or $CFG->fullnamedisplay === 'firstname lastname') {
03338         return $user->firstname .' '. $user->lastname;
03339 
03340     } else if ($CFG->fullnamedisplay == 'lastname firstname') {
03341         return $user->lastname .' '. $user->firstname;
03342 
03343     } else if ($CFG->fullnamedisplay == 'firstname') {
03344         if ($override) {
03345             return get_string('fullnamedisplay', '', $user);
03346         } else {
03347             return $user->firstname;
03348         }
03349     }
03350 
03351     return get_string('fullnamedisplay', '', $user);
03352 }
03353 
03362 function get_extra_user_fields($context, $already = array()) {
03363     global $CFG;
03364 
03365     // Only users with permission get the extra fields
03366     if (!has_capability('moodle/site:viewuseridentity', $context)) {
03367         return array();
03368     }
03369 
03370     // Split showuseridentity on comma
03371     if (empty($CFG->showuseridentity)) {
03372         // Explode gives wrong result with empty string
03373         $extra = array();
03374     } else {
03375         $extra =  explode(',', $CFG->showuseridentity);
03376     }
03377     $renumber = false;
03378     foreach ($extra as $key => $field) {
03379         if (in_array($field, $already)) {
03380             unset($extra[$key]);
03381             $renumber = true;
03382         }
03383     }
03384     if ($renumber) {
03385         // For consistency, if entries are removed from array, renumber it
03386         // so they are numbered as you would expect
03387         $extra = array_merge($extra);
03388     }
03389     return $extra;
03390 }
03391 
03404 function get_extra_user_fields_sql($context, $alias='', $prefix='',
03405         $already = array()) {
03406     $fields = get_extra_user_fields($context, $already);
03407     $result = '';
03408     // Add punctuation for alias
03409     if ($alias !== '') {
03410         $alias .= '.';
03411     }
03412     foreach ($fields as $field) {
03413         $result .= ', ' . $alias . $field;
03414         if ($prefix) {
03415             $result .= ' AS ' . $prefix . $field;
03416         }
03417     }
03418     return $result;
03419 }
03420 
03427 function get_user_field_name($field) {
03428     // Some fields have language strings which are not the same as field name
03429     switch ($field) {
03430         case 'phone1' : return get_string('phone');
03431     }
03432     // Otherwise just use the same lang string
03433     return get_string($field);
03434 }
03435 
03444 function exists_auth_plugin($auth) {
03445     global $CFG;
03446 
03447     if (file_exists("{$CFG->dirroot}/auth/$auth/auth.php")) {
03448         return is_readable("{$CFG->dirroot}/auth/$auth/auth.php");
03449     }
03450     return false;
03451 }
03452 
03459 function is_enabled_auth($auth) {
03460     if (empty($auth)) {
03461         return false;
03462     }
03463 
03464     $enabled = get_enabled_auth_plugins();
03465 
03466     return in_array($auth, $enabled);
03467 }
03468 
03476 function get_auth_plugin($auth) {
03477     global $CFG;
03478 
03479     // check the plugin exists first
03480     if (! exists_auth_plugin($auth)) {
03481         print_error('authpluginnotfound', 'debug', '', $auth);
03482     }
03483 
03484     // return auth plugin instance
03485     require_once "{$CFG->dirroot}/auth/$auth/auth.php";
03486     $class = "auth_plugin_$auth";
03487     return new $class;
03488 }
03489 
03496 function get_enabled_auth_plugins($fix=false) {
03497     global $CFG;
03498 
03499     $default = array('manual', 'nologin');
03500 
03501     if (empty($CFG->auth)) {
03502         $auths = array();
03503     } else {
03504         $auths = explode(',', $CFG->auth);
03505     }
03506 
03507     if ($fix) {
03508         $auths = array_unique($auths);
03509         foreach($auths as $k=>$authname) {
03510             if (!exists_auth_plugin($authname) or in_array($authname, $default)) {
03511                 unset($auths[$k]);
03512             }
03513         }
03514         $newconfig = implode(',', $auths);
03515         if (!isset($CFG->auth) or $newconfig != $CFG->auth) {
03516             set_config('auth', $newconfig);
03517         }
03518     }
03519 
03520     return (array_merge($default, $auths));
03521 }
03522 
03530 function is_internal_auth($auth) {
03531     $authplugin = get_auth_plugin($auth); // throws error if bad $auth
03532     return $authplugin->is_internal();
03533 }
03534 
03546 function is_restored_user($username) {
03547     global $CFG, $DB;
03548 
03549     return $DB->record_exists('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'password'=>'restored'));
03550 }
03551 
03557 function get_user_fieldnames() {
03558     global $DB;
03559 
03560     $fieldarray = $DB->get_columns('user');
03561     unset($fieldarray['id']);
03562     $fieldarray = array_keys($fieldarray);
03563 
03564     return $fieldarray;
03565 }
03566 
03577 function create_user_record($username, $password, $auth = 'manual') {
03578     global $CFG, $DB;
03579 
03580     //just in case check text case
03581     $username = trim(moodle_strtolower($username));
03582 
03583     $authplugin = get_auth_plugin($auth);
03584 
03585     $newuser = new stdClass();
03586 
03587     if ($newinfo = $authplugin->get_userinfo($username)) {
03588         $newinfo = truncate_userinfo($newinfo);
03589         foreach ($newinfo as $key => $value){
03590             $newuser->$key = $value;
03591         }
03592     }
03593 
03594     if (!empty($newuser->email)) {
03595         if (email_is_not_allowed($newuser->email)) {
03596             unset($newuser->email);
03597         }
03598     }
03599 
03600     if (!isset($newuser->city)) {
03601         $newuser->city = '';
03602     }
03603 
03604     $newuser->auth = $auth;
03605     $newuser->username = $username;
03606 
03607     // fix for MDL-8480
03608     // user CFG lang for user if $newuser->lang is empty
03609     // or $user->lang is not an installed language
03610     if (empty($newuser->lang) || !get_string_manager()->translation_exists($newuser->lang)) {
03611         $newuser->lang = $CFG->lang;
03612     }
03613     $newuser->confirmed = 1;
03614     $newuser->lastip = getremoteaddr();
03615     $newuser->timecreated = time();
03616     $newuser->timemodified = $newuser->timecreated;
03617     $newuser->mnethostid = $CFG->mnet_localhost_id;
03618 
03619     $newuser->id = $DB->insert_record('user', $newuser);
03620     $user = get_complete_user_data('id', $newuser->id);
03621     if (!empty($CFG->{'auth_'.$newuser->auth.'_forcechangepassword'})){
03622         set_user_preference('auth_forcepasswordchange', 1, $user);
03623     }
03624     update_internal_user_password($user, $password);
03625 
03626     // fetch full user record for the event, the complete user data contains too much info
03627     // and we want to be consistent with other places that trigger this event
03628     events_trigger('user_created', $DB->get_record('user', array('id'=>$user->id)));
03629 
03630     return $user;
03631 }
03632 
03640 function update_user_record($username) {
03641     global $DB, $CFG;
03642 
03643     $username = trim(moodle_strtolower($username)); 
03644 
03645     $oldinfo = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST);
03646     $newuser = array();
03647     $userauth = get_auth_plugin($oldinfo->auth);
03648 
03649     if ($newinfo = $userauth->get_userinfo($username)) {
03650         $newinfo = truncate_userinfo($newinfo);
03651         foreach ($newinfo as $key => $value){
03652             $key = strtolower($key);
03653             if (!property_exists($oldinfo, $key) or $key === 'username' or $key === 'id'
03654                     or $key === 'auth' or $key === 'mnethostid' or $key === 'deleted') {
03655                 // unknown or must not be changed
03656                 continue;
03657             }
03658             $confval = $userauth->config->{'field_updatelocal_' . $key};
03659             $lockval = $userauth->config->{'field_lock_' . $key};
03660             if (empty($confval) || empty($lockval)) {
03661                 continue;
03662             }
03663             if ($confval === 'onlogin') {
03664                 // MDL-4207 Don't overwrite modified user profile values with
03665                 // empty LDAP values when 'unlocked if empty' is set. The purpose
03666                 // of the setting 'unlocked if empty' is to allow the user to fill
03667                 // in a value for the selected field _if LDAP is giving
03668                 // nothing_ for this field. Thus it makes sense to let this value
03669                 // stand in until LDAP is giving a value for this field.
03670                 if (!(empty($value) && $lockval === 'unlockedifempty')) {
03671                     if ((string)$oldinfo->$key !== (string)$value) {
03672                         $newuser[$key] = (string)$value;
03673                     }
03674                 }
03675             }
03676         }
03677         if ($newuser) {
03678             $newuser['id'] = $oldinfo->id;
03679             $newuser['timemodified'] = time();
03680             $DB->update_record('user', $newuser);
03681             // fetch full user record for the event, the complete user data contains too much info
03682             // and we want to be consistent with other places that trigger this event
03683             events_trigger('user_updated', $DB->get_record('user', array('id'=>$oldinfo->id)));
03684         }
03685     }
03686 
03687     return get_complete_user_data('id', $oldinfo->id);
03688 }
03689 
03699 function truncate_userinfo($info) {
03700     // define the limits
03701     $limit = array(
03702                     'username'    => 100,
03703                     'idnumber'    => 255,
03704                     'firstname'   => 100,
03705                     'lastname'    => 100,
03706                     'email'       => 100,
03707                     'icq'         =>  15,
03708                     'phone1'      =>  20,
03709                     'phone2'      =>  20,
03710                     'institution' =>  40,
03711                     'department'  =>  30,
03712                     'address'     =>  70,
03713                     'city'        => 120,
03714                     'country'     =>   2,
03715                     'url'         => 255,
03716                     );
03717 
03718     $textlib = textlib_get_instance();
03719     // apply where needed
03720     foreach (array_keys($info) as $key) {
03721         if (!empty($limit[$key])) {
03722             $info[$key] = trim($textlib->substr($info[$key],0, $limit[$key]));
03723         }
03724     }
03725 
03726     return $info;
03727 }
03728 
03738 function delete_user($user) {
03739     global $CFG, $DB;
03740     require_once($CFG->libdir.'/grouplib.php');
03741     require_once($CFG->libdir.'/gradelib.php');
03742     require_once($CFG->dirroot.'/message/lib.php');
03743     require_once($CFG->dirroot.'/tag/lib.php');
03744 
03745     // delete all grades - backup is kept in grade_grades_history table
03746     grade_user_delete($user->id);
03747 
03748     //move unread messages from this user to read
03749     message_move_userfrom_unread2read($user->id);
03750 
03751     // TODO: remove from cohorts using standard API here
03752 
03753     // remove user tags
03754     tag_set('user', $user->id, array());
03755 
03756     // unconditionally unenrol from all courses
03757     enrol_user_delete($user);
03758 
03759     // unenrol from all roles in all contexts
03760     role_unassign_all(array('userid'=>$user->id)); // this might be slow but it is really needed - modules might do some extra cleanup!
03761 
03762     //now do a brute force cleanup
03763 
03764     // remove from all cohorts
03765     $DB->delete_records('cohort_members', array('userid'=>$user->id));
03766 
03767     // remove from all groups
03768     $DB->delete_records('groups_members', array('userid'=>$user->id));
03769 
03770     // brute force unenrol from all courses
03771     $DB->delete_records('user_enrolments', array('userid'=>$user->id));
03772 
03773     // purge user preferences
03774     $DB->delete_records('user_preferences', array('userid'=>$user->id));
03775 
03776     // purge user extra profile info
03777     $DB->delete_records('user_info_data', array('userid'=>$user->id));
03778 
03779     // last course access not necessary either
03780     $DB->delete_records('user_lastaccess', array('userid'=>$user->id));
03781 
03782     // remove all user tokens
03783     $DB->delete_records('external_tokens', array('userid'=>$user->id));
03784 
03785     // unauthorise the user for all services
03786     $DB->delete_records('external_services_users', array('userid'=>$user->id));
03787 
03788     // force logout - may fail if file based sessions used, sorry
03789     session_kill_user($user->id);
03790 
03791     // now do a final accesslib cleanup - removes all role assignments in user context and context itself
03792     delete_context(CONTEXT_USER, $user->id);
03793 
03794     // workaround for bulk deletes of users with the same email address
03795     $delname = "$user->email.".time();
03796     while ($DB->record_exists('user', array('username'=>$delname))) { // no need to use mnethostid here
03797         $delname++;
03798     }
03799 
03800     // mark internal user record as "deleted"
03801     $updateuser = new stdClass();
03802     $updateuser->id           = $user->id;
03803     $updateuser->deleted      = 1;
03804     $updateuser->username     = $delname;            // Remember it just in case
03805     $updateuser->email        = md5($user->username);// Store hash of username, useful importing/restoring users
03806     $updateuser->idnumber     = '';                  // Clear this field to free it up
03807     $updateuser->timemodified = time();
03808 
03809     $DB->update_record('user', $updateuser);
03810 
03811     // We will update the user's timemodified, as it will be passed to the user_deleted event, which
03812     // should know about this updated property persisted to the user's table.
03813     $user->timemodified = $updateuser->timemodified;
03814 
03815     // notify auth plugin - do not block the delete even when plugin fails
03816     $authplugin = get_auth_plugin($user->auth);
03817     $authplugin->user_delete($user);
03818 
03819     // any plugin that needs to cleanup should register this event
03820     events_trigger('user_deleted', $user);
03821 
03822     return true;
03823 }
03824 
03832 function guest_user() {
03833     global $CFG, $DB;
03834 
03835     if ($newuser = $DB->get_record('user', array('id'=>$CFG->siteguest))) {
03836         $newuser->confirmed = 1;
03837         $newuser->lang = $CFG->lang;
03838         $newuser->lastip = getremoteaddr();
03839     }
03840 
03841     return $newuser;
03842 }
03843 
03864 function authenticate_user_login($username, $password) {
03865     global $CFG, $DB;
03866 
03867     $authsenabled = get_enabled_auth_plugins();
03868 
03869     if ($user = get_complete_user_data('username', $username, $CFG->mnet_localhost_id)) {
03870         $auth = empty($user->auth) ? 'manual' : $user->auth;  // use manual if auth not set
03871         if (!empty($user->suspended)) {
03872             add_to_log(SITEID, 'login', 'error', 'index.php', $username);
03873             error_log('[client '.getremoteaddr()."]  $CFG->wwwroot  Suspended Login:  $username  ".$_SERVER['HTTP_USER_AGENT']);
03874             return false;
03875         }
03876         if ($auth=='nologin' or !is_enabled_auth($auth)) {
03877             add_to_log(SITEID, 'login', 'error', 'index.php', $username);
03878             error_log('[client '.getremoteaddr()."]  $CFG->wwwroot  Disabled Login:  $username  ".$_SERVER['HTTP_USER_AGENT']);
03879             return false;
03880         }
03881         $auths = array($auth);
03882 
03883     } else {
03884         // check if there's a deleted record (cheaply)
03885         if ($DB->get_field('user', 'id', array('username'=>$username, 'deleted'=>1))) {
03886             error_log('[client '.getremoteaddr()."]  $CFG->wwwroot  Deleted Login:  $username  ".$_SERVER['HTTP_USER_AGENT']);
03887             return false;
03888         }
03889 
03890         // User does not exist
03891         $auths = $authsenabled;
03892         $user = new stdClass();
03893         $user->id = 0;
03894     }
03895 
03896     foreach ($auths as $auth) {
03897         $authplugin = get_auth_plugin($auth);
03898 
03899         // on auth fail fall through to the next plugin
03900         if (!$authplugin->user_login($username, $password)) {
03901             continue;
03902         }
03903 
03904         // successful authentication
03905         if ($user->id) {                          // User already exists in database
03906             if (empty($user->auth)) {             // For some reason auth isn't set yet
03907                 $DB->set_field('user', 'auth', $auth, array('username'=>$username));
03908                 $user->auth = $auth;
03909             }
03910             if (empty($user->firstaccess)) { //prevent firstaccess from remaining 0 for manual account that never required confirmation
03911                 $DB->set_field('user','firstaccess', $user->timemodified, array('id' => $user->id));
03912                 $user->firstaccess = $user->timemodified;
03913             }
03914 
03915             update_internal_user_password($user, $password); // just in case salt or encoding were changed (magic quotes too one day)
03916 
03917             if ($authplugin->is_synchronised_with_external()) { // update user record from external DB
03918                 $user = update_user_record($username);
03919             }
03920         } else {
03921             // if user not found and user creation is not disabled, create it
03922             if (empty($CFG->authpreventaccountcreation)) {
03923                 $user = create_user_record($username, $password, $auth);
03924             } else {
03925                 continue;
03926             }
03927         }
03928 
03929         $authplugin->sync_roles($user);
03930 
03931         foreach ($authsenabled as $hau) {
03932             $hauth = get_auth_plugin($hau);
03933             $hauth->user_authenticated_hook($user, $username, $password);
03934         }
03935 
03936         if (empty($user->id)) {
03937             return false;
03938         }
03939 
03940         if (!empty($user->suspended)) {
03941             // just in case some auth plugin suspended account
03942             add_to_log(SITEID, 'login', 'error', 'index.php', $username);
03943             error_log('[client '.getremoteaddr()."]  $CFG->wwwroot  Suspended Login:  $username  ".$_SERVER['HTTP_USER_AGENT']);
03944             return false;
03945         }
03946 
03947         return $user;
03948     }
03949 
03950     // failed if all the plugins have failed
03951     add_to_log(SITEID, 'login', 'error', 'index.php', $username);
03952     if (debugging('', DEBUG_ALL)) {
03953         error_log('[client '.getremoteaddr()."]  $CFG->wwwroot  Failed Login:  $username  ".$_SERVER['HTTP_USER_AGENT']);
03954     }
03955     return false;
03956 }
03957 
03970 function complete_user_login($user) {
03971     global $CFG, $USER;
03972 
03973     // regenerate session id and delete old session,
03974     // this helps prevent session fixation attacks from the same domain
03975     session_regenerate_id(true);
03976 
03977     // let enrol plugins deal with new enrolments if necessary
03978     enrol_check_plugins($user);
03979 
03980     // check enrolments, load caps and setup $USER object
03981     session_set_user($user);
03982 
03983     // reload preferences from DB
03984     unset($USER->preference);
03985     check_user_preferences_loaded($USER);
03986 
03987     // update login times
03988     update_user_login_times();
03989 
03990     // extra session prefs init
03991     set_login_session_preferences();
03992 
03993     if (isguestuser()) {
03994         // no need to continue when user is THE guest
03995         return $USER;
03996     }
03997 
03999     $userauth = get_auth_plugin($USER->auth);
04000 
04002     if (get_user_preferences('auth_forcepasswordchange', false)){
04003         if ($userauth->can_change_password()) {
04004             if ($changeurl = $userauth->change_password_url()) {
04005                 redirect($changeurl);
04006             } else {
04007                 redirect($CFG->httpswwwroot.'/login/change_password.php');
04008             }
04009         } else {
04010             print_error('nopasswordchangeforced', 'auth');
04011         }
04012     }
04013     return $USER;
04014 }
04015 
04024 function validate_internal_user_password($user, $password) {
04025     global $CFG;
04026 
04027     if (!isset($CFG->passwordsaltmain)) {
04028         $CFG->passwordsaltmain = '';
04029     }
04030 
04031     $validated = false;
04032 
04033     if ($user->password === 'not cached') {
04034         // internal password is not used at all, it can not validate
04035 
04036     } else if ($user->password === md5($password.$CFG->passwordsaltmain)
04037             or $user->password === md5($password)
04038             or $user->password === md5(addslashes($password).$CFG->passwordsaltmain)
04039             or $user->password === md5(addslashes($password))) {
04040         // note: we are intentionally using the addslashes() here because we
04041         //       need to accept old password hashes of passwords with magic quotes
04042         $validated = true;
04043 
04044     } else {
04045         for ($i=1; $i<=20; $i++) { //20 alternative salts should be enough, right?
04046             $alt = 'passwordsaltalt'.$i;
04047             if (!empty($CFG->$alt)) {
04048                 if ($user->password === md5($password.$CFG->$alt) or $user->password === md5(addslashes($password).$CFG->$alt)) {
04049                     $validated = true;
04050                     break;
04051                 }
04052             }
04053         }
04054     }
04055 
04056     if ($validated) {
04057         // force update of password hash using latest main password salt and encoding if needed
04058         update_internal_user_password($user, $password);
04059     }
04060 
04061     return $validated;
04062 }
04063 
04070 function hash_internal_user_password($password) {
04071     global $CFG;
04072 
04073     if (isset($CFG->passwordsaltmain)) {
04074         return md5($password.$CFG->passwordsaltmain);
04075     } else {
04076         return md5($password);
04077     }
04078 }
04079 
04087 function update_internal_user_password($user, $password) {
04088     global $DB;
04089 
04090     $authplugin = get_auth_plugin($user->auth);
04091     if ($authplugin->prevent_local_passwords()) {
04092         $hashedpassword = 'not cached';
04093     } else {
04094         $hashedpassword = hash_internal_user_password($password);
04095     }
04096 
04097     if ($user->password !== $hashedpassword) {
04098         $DB->set_field('user', 'password',  $hashedpassword, array('id'=>$user->id));
04099         $user->password = $hashedpassword;
04100     }
04101 
04102     return true;
04103 }
04104 
04116 function get_complete_user_data($field, $value, $mnethostid = null) {
04117     global $CFG, $DB;
04118 
04119     if (!$field || !$value) {
04120         return false;
04121     }
04122 
04124     $params = array('fieldval'=>$value);
04125     $constraints = "$field = :fieldval AND deleted <> 1";
04126 
04127     // If we are loading user data based on anything other than id,
04128     // we must also restrict our search based on mnet host.
04129     if ($field != 'id') {
04130         if (empty($mnethostid)) {
04131             // if empty, we restrict to local users
04132             $mnethostid = $CFG->mnet_localhost_id;
04133         }
04134     }
04135     if (!empty($mnethostid)) {
04136         $params['mnethostid'] = $mnethostid;
04137         $constraints .= " AND mnethostid = :mnethostid";
04138     }
04139 
04141 
04142     if (! $user = $DB->get_record_select('user', $constraints, $params)) {
04143         return false;
04144     }
04145 
04147 
04148     // preload preference cache
04149     check_user_preferences_loaded($user);
04150 
04151     // load course enrolment related stuff
04152     $user->lastcourseaccess    = array(); // during last session
04153     $user->currentcourseaccess = array(); // during current session
04154     if ($lastaccesses = $DB->get_records('user_lastaccess', array('userid'=>$user->id))) {
04155         foreach ($lastaccesses as $lastaccess) {
04156             $user->lastcourseaccess[$lastaccess->courseid] = $lastaccess->timeaccess;
04157         }
04158     }
04159 
04160     $sql = "SELECT g.id, g.courseid
04161               FROM {groups} g, {groups_members} gm
04162              WHERE gm.groupid=g.id AND gm.userid=?";
04163 
04164     // this is a special hack to speedup calendar display
04165     $user->groupmember = array();
04166     if (!isguestuser($user)) {
04167         if ($groups = $DB->get_records_sql($sql, array($user->id))) {
04168             foreach ($groups as $group) {
04169                 if (!array_key_exists($group->courseid, $user->groupmember)) {
04170                     $user->groupmember[$group->courseid] = array();
04171                 }
04172                 $user->groupmember[$group->courseid][$group->id] = $group->id;
04173             }
04174         }
04175     }
04176 
04178     $user->profile = array();
04179     if (!isguestuser($user)) {
04180         require_once($CFG->dirroot.'/user/profile/lib.php');
04181         profile_load_custom_fields($user);
04182     }
04183 
04185     if (!empty($user->description)) {
04186         $user->description = true;   // No need to cart all of it around
04187     }
04188     if (isguestuser($user)) {
04189         $user->lang       = $CFG->lang;               // Guest language always same as site
04190         $user->firstname  = get_string('guestuser');  // Name always in current language
04191         $user->lastname   = ' ';
04192     }
04193 
04194     return $user;
04195 }
04196 
04205 function check_password_policy($password, &$errmsg) {
04206     global $CFG;
04207 
04208     if (empty($CFG->passwordpolicy)) {
04209         return true;
04210     }
04211 
04212     $textlib = textlib_get_instance();
04213     $errmsg = '';
04214     if ($textlib->strlen($password) < $CFG->minpasswordlength) {
04215         $errmsg .= '<div>'. get_string('errorminpasswordlength', 'auth', $CFG->minpasswordlength) .'</div>';
04216 
04217     }
04218     if (preg_match_all('/[[:digit:]]/u', $password, $matches) < $CFG->minpassworddigits) {
04219         $errmsg .= '<div>'. get_string('errorminpassworddigits', 'auth', $CFG->minpassworddigits) .'</div>';
04220 
04221     }
04222     if (preg_match_all('/[[:lower:]]/u', $password, $matches) < $CFG->minpasswordlower) {
04223         $errmsg .= '<div>'. get_string('errorminpasswordlower', 'auth', $CFG->minpasswordlower) .'</div>';
04224 
04225     }
04226     if (preg_match_all('/[[:upper:]]/u', $password, $matches) < $CFG->minpasswordupper) {
04227         $errmsg .= '<div>'. get_string('errorminpasswordupper', 'auth', $CFG->minpasswordupper) .'</div>';
04228 
04229     }
04230     if (preg_match_all('/[^[:upper:][:lower:][:digit:]]/u', $password, $matches) < $CFG->minpasswordnonalphanum) {
04231         $errmsg .= '<div>'. get_string('errorminpasswordnonalphanum', 'auth', $CFG->minpasswordnonalphanum) .'</div>';
04232     }
04233     if (!check_consecutive_identical_characters($password, $CFG->maxconsecutiveidentchars)) {
04234         $errmsg .= '<div>'. get_string('errormaxconsecutiveidentchars', 'auth', $CFG->maxconsecutiveidentchars) .'</div>';
04235     }
04236 
04237     if ($errmsg == '') {
04238         return true;
04239     } else {
04240         return false;
04241     }
04242 }
04243 
04244 
04252 function set_login_session_preferences() {
04253     global $SESSION, $CFG;
04254 
04255     $SESSION->justloggedin = true;
04256 
04257     unset($SESSION->lang);
04258 }
04259 
04260 
04273 function delete_course($courseorid, $showfeedback = true) {
04274     global $DB;
04275 
04276     if (is_object($courseorid)) {
04277         $courseid = $courseorid->id;
04278         $course   = $courseorid;
04279     } else {
04280         $courseid = $courseorid;
04281         if (!$course = $DB->get_record('course', array('id'=>$courseid))) {
04282             return false;
04283         }
04284     }
04285     $context = get_context_instance(CONTEXT_COURSE, $courseid);
04286 
04287     // frontpage course can not be deleted!!
04288     if ($courseid == SITEID) {
04289         return false;
04290     }
04291 
04292     // make the course completely empty
04293     remove_course_contents($courseid, $showfeedback);
04294 
04295     // delete the course and related context instance
04296     delete_context(CONTEXT_COURSE, $courseid);
04297 
04298     // We will update the course's timemodified, as it will be passed to the course_deleted event,
04299     // which should know about this updated property, as this event is meant to pass the full course record
04300     $course->timemodified = time();
04301 
04302     $DB->delete_records("course", array("id"=>$courseid));
04303 
04304     //trigger events
04305     $course->context = $context; // you can not fetch context in the event because it was already deleted
04306     events_trigger('course_deleted', $course);
04307 
04308     return true;
04309 }
04310 
04330 function remove_course_contents($courseid, $showfeedback = true, array $options = null) {
04331     global $CFG, $DB, $OUTPUT;
04332     require_once($CFG->libdir.'/completionlib.php');
04333     require_once($CFG->libdir.'/questionlib.php');
04334     require_once($CFG->libdir.'/gradelib.php');
04335     require_once($CFG->dirroot.'/group/lib.php');
04336     require_once($CFG->dirroot.'/tag/coursetagslib.php');
04337     require_once($CFG->dirroot.'/comment/lib.php');
04338     require_once($CFG->dirroot.'/rating/lib.php');
04339 
04340     // NOTE: these concatenated strings are suboptimal, but it is just extra info...
04341     $strdeleted = get_string('deleted').' - ';
04342 
04343     // Some crazy wishlist of stuff we should skip during purging of course content
04344     $options = (array)$options;
04345 
04346     $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
04347     $coursecontext = context_course::instance($courseid);
04348     $fs = get_file_storage();
04349 
04350     // Delete course completion information, this has to be done before grades and enrols
04351     $cc = new completion_info($course);
04352     $cc->clear_criteria();
04353     if ($showfeedback) {
04354         echo $OUTPUT->notification($strdeleted.get_string('completion', 'completion'), 'notifysuccess');
04355     }
04356 
04357     // Remove all data from gradebook - this needs to be done before course modules
04358     // because while deleting this information, the system may need to reference
04359     // the course modules that own the grades.
04360     remove_course_grades($courseid, $showfeedback);
04361     remove_grade_letters($coursecontext, $showfeedback);
04362 
04363     // Delete course blocks in any all child contexts,
04364     // they may depend on modules so delete them first
04365     $childcontexts = $coursecontext->get_child_contexts(); // returns all subcontexts since 2.2
04366     foreach ($childcontexts as $childcontext) {
04367         blocks_delete_all_for_context($childcontext->id);
04368     }
04369     unset($childcontexts);
04370     blocks_delete_all_for_context($coursecontext->id);
04371     if ($showfeedback) {
04372         echo $OUTPUT->notification($strdeleted.get_string('type_block_plural', 'plugin'), 'notifysuccess');
04373     }
04374 
04375     // Delete every instance of every module,
04376     // this has to be done before deleting of course level stuff
04377     $locations = get_plugin_list('mod');
04378     foreach ($locations as $modname=>$moddir) {
04379         if ($modname === 'NEWMODULE') {
04380             continue;
04381         }
04382         if ($module = $DB->get_record('modules', array('name'=>$modname))) {
04383             include_once("$moddir/lib.php");                 // Shows php warning only if plugin defective
04384             $moddelete = $modname .'_delete_instance';       // Delete everything connected to an instance
04385             $moddeletecourse = $modname .'_delete_course';   // Delete other stray stuff (uncommon)
04386 
04387             if ($instances = $DB->get_records($modname, array('course'=>$course->id))) {
04388                 foreach ($instances as $instance) {
04389                     if ($cm = get_coursemodule_from_instance($modname, $instance->id, $course->id)) {
04391                         question_delete_activity($cm,  $showfeedback);
04392                     }
04393                     if (function_exists($moddelete)) {
04394                         // This purges all module data in related tables, extra user prefs, settings, etc.
04395                         $moddelete($instance->id);
04396                     } else {
04397                         // NOTE: we should not allow installation of modules with missing delete support!
04398                         debugging("Defective module '$modname' detected when deleting course contents: missing function $moddelete()!");
04399                         $DB->delete_records($modname, array('id'=>$instance->id));
04400                     }
04401 
04402                     if ($cm) {
04403                         // Delete cm and its context - orphaned contexts are purged in cron in case of any race condition
04404                         context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
04405                         $DB->delete_records('course_modules', array('id'=>$cm->id));
04406                     }
04407                 }
04408             }
04409             if (function_exists($moddeletecourse)) {
04410                 // Execute ptional course cleanup callback
04411                 $moddeletecourse($course, $showfeedback);
04412             }
04413             if ($instances and $showfeedback) {
04414                 echo $OUTPUT->notification($strdeleted.get_string('pluginname', $modname), 'notifysuccess');
04415             }
04416         } else {
04417             // Ooops, this module is not properly installed, force-delete it in the next block
04418         }
04419     }
04420     // We have tried to delete everything the nice way - now let's force-delete any remaining module data
04421     $cms = $DB->get_records('course_modules', array('course'=>$course->id));
04422     foreach ($cms as $cm) {
04423         if ($module = $DB->get_record('modules', array('id'=>$cm->module))) {
04424             try {
04425                 $DB->delete_records($module->name, array('id'=>$cm->instance));
04426             } catch (Exception $e) {
04427                 // Ignore weird or missing table problems
04428             }
04429         }
04430         context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
04431         $DB->delete_records('course_modules', array('id'=>$cm->id));
04432     }
04433     // Remove all data from availability and completion tables that is associated
04434     // with course-modules belonging to this course. Note this is done even if the
04435     // features are not enabled now, in case they were enabled previously
04436     $DB->delete_records_select('course_modules_completion',
04437            'coursemoduleid IN (SELECT id from {course_modules} WHERE course=?)',
04438            array($courseid));
04439     $DB->delete_records_select('course_modules_availability',
04440            'coursemoduleid IN (SELECT id from {course_modules} WHERE course=?)',
04441            array($courseid));
04442     if ($showfeedback) {
04443         echo $OUTPUT->notification($strdeleted.get_string('type_mod_plural', 'plugin'), 'notifysuccess');
04444     }
04445 
04446     // Cleanup the rest of plugins
04447     $cleanuplugintypes = array('report', 'coursereport', 'format');
04448     foreach ($cleanuplugintypes as $type) {
04449         $plugins = get_plugin_list_with_function($type, 'delete_course', 'lib.php');
04450         foreach ($plugins as $plugin=>$pluginfunction) {
04451             $pluginfunction($course->id, $showfeedback);
04452         }
04453         if ($showfeedback) {
04454             echo $OUTPUT->notification($strdeleted.get_string('type_'.$type.'_plural', 'plugin'), 'notifysuccess');
04455         }
04456     }
04457 
04458     // Delete questions and question categories
04459     question_delete_course($course, $showfeedback);
04460     if ($showfeedback) {
04461         echo $OUTPUT->notification($strdeleted.get_string('questions', 'question'), 'notifysuccess');
04462     }
04463 
04464     // Make sure there are no subcontexts left - all valid blocks and modules should be already gone
04465     $childcontexts = $coursecontext->get_child_contexts(); // returns all subcontexts since 2.2
04466     foreach ($childcontexts as $childcontext) {
04467         $childcontext->delete();
04468     }
04469     unset($childcontexts);
04470 
04471     // Remove all roles and enrolments by default
04472     if (empty($options['keep_roles_and_enrolments'])) {
04473         // this hack is used in restore when deleting contents of existing course
04474         role_unassign_all(array('contextid'=>$coursecontext->id), true);
04475         enrol_course_delete($course);
04476         if ($showfeedback) {
04477             echo $OUTPUT->notification($strdeleted.get_string('type_enrol_plural', 'plugin'), 'notifysuccess');
04478         }
04479     }
04480 
04481     // Delete any groups, removing members and grouping/course links first.
04482     if (empty($options['keep_groups_and_groupings'])) {
04483         groups_delete_groupings($course->id, $showfeedback);
04484         groups_delete_groups($course->id, $showfeedback);
04485     }
04486 
04487     // filters be gone!
04488     filter_delete_all_for_context($coursecontext->id);
04489 
04490     // die comments!
04491     comment::delete_comments($coursecontext->id);
04492 
04493     // ratings are history too
04494     $delopt = new stdclass();
04495     $delopt->contextid = $coursecontext->id;
04496     $rm = new rating_manager();
04497     $rm->delete_ratings($delopt);
04498 
04499     // Delete course tags
04500     coursetag_delete_course_tags($course->id, $showfeedback);
04501 
04502     // Delete calendar events
04503     $DB->delete_records('event', array('courseid'=>$course->id));
04504     $fs->delete_area_files($coursecontext->id, 'calendar');
04505 
04506     // Delete all related records in other core tables that may have a courseid
04507     // This array stores the tables that need to be cleared, as
04508     // table_name => column_name that contains the course id.
04509     $tablestoclear = array(
04510         'log' => 'course',               // Course logs (NOTE: this might be changed in the future)
04511         'backup_courses' => 'courseid',  // Scheduled backup stuff
04512         'user_lastaccess' => 'courseid', // User access info
04513     );
04514     foreach ($tablestoclear as $table => $col) {
04515         $DB->delete_records($table, array($col=>$course->id));
04516     }
04517 
04518     // delete all course backup files
04519     $fs->delete_area_files($coursecontext->id, 'backup');
04520 
04521     // cleanup course record - remove links to deleted stuff
04522     $oldcourse = new stdClass();
04523     $oldcourse->id               = $course->id;
04524     $oldcourse->summary          = '';
04525     $oldcourse->modinfo          = NULL;
04526     $oldcourse->legacyfiles      = 0;
04527     $oldcourse->enablecompletion = 0;
04528     if (!empty($options['keep_groups_and_groupings'])) {
04529         $oldcourse->defaultgroupingid = 0;
04530     }
04531     $DB->update_record('course', $oldcourse);
04532 
04533     // Delete course sections and user selections
04534     $DB->delete_records('course_sections', array('course'=>$course->id));
04535     $DB->delete_records('course_display', array('course'=>$course->id));
04536 
04537     // delete legacy, section and any other course files
04538     $fs->delete_area_files($coursecontext->id, 'course'); // files from summary and section
04539 
04540     // Delete all remaining stuff linked to context such as files, comments, ratings, etc.
04541     if (empty($options['keep_roles_and_enrolments']) and empty($options['keep_groups_and_groupings'])) {
04542         // Easy, do not delete the context itself...
04543         $coursecontext->delete_content();
04544 
04545     } else {
04546         // Hack alert!!!!
04547         // We can not drop all context stuff because it would bork enrolments and roles,
04548         // there might be also files used by enrol plugins...
04549     }
04550 
04551     // Delete legacy files - just in case some files are still left there after conversion to new file api,
04552     // also some non-standard unsupported plugins may try to store something there
04553     fulldelete($CFG->dataroot.'/'.$course->id);
04554 
04555     // Finally trigger the event
04556     $course->context = $coursecontext; // you can not access context in cron event later after course is deleted
04557     $course->options = $options;       // not empty if we used any crazy hack
04558     events_trigger('course_content_removed', $course);
04559 
04560     return true;
04561 }
04562 
04574 function shift_course_mod_dates($modname, $fields, $timeshift, $courseid) {
04575     global $CFG, $DB;
04576     include_once($CFG->dirroot.'/mod/'.$modname.'/lib.php');
04577 
04578     $return = true;
04579     foreach ($fields as $field) {
04580         $updatesql = "UPDATE {".$modname."}
04581                           SET $field = $field + ?
04582                         WHERE course=? AND $field<>0 AND $field<>0";
04583         $return = $DB->execute($updatesql, array($timeshift, $courseid)) && $return;
04584     }
04585 
04586     $refreshfunction = $modname.'_refresh_events';
04587     if (function_exists($refreshfunction)) {
04588         $refreshfunction($courseid);
04589     }
04590 
04591     return $return;
04592 }
04593 
04601 function reset_course_userdata($data) {
04602     global $CFG, $USER, $DB;
04603     require_once($CFG->libdir.'/gradelib.php');
04604     require_once($CFG->libdir.'/completionlib.php');
04605     require_once($CFG->dirroot.'/group/lib.php');
04606 
04607     $data->courseid = $data->id;
04608     $context = get_context_instance(CONTEXT_COURSE, $data->courseid);
04609 
04610     // calculate the time shift of dates
04611     if (!empty($data->reset_start_date)) {
04612         // time part of course startdate should be zero
04613         $data->timeshift = $data->reset_start_date - usergetmidnight($data->reset_start_date_old);
04614     } else {
04615         $data->timeshift = 0;
04616     }
04617 
04618     // result array: component, item, error
04619     $status = array();
04620 
04621     // start the resetting
04622     $componentstr = get_string('general');
04623 
04624     // move the course start time
04625     if (!empty($data->reset_start_date) and $data->timeshift) {
04626         // change course start data
04627         $DB->set_field('course', 'startdate', $data->reset_start_date, array('id'=>$data->courseid));
04628         // update all course and group events - do not move activity events
04629         $updatesql = "UPDATE {event}
04630                          SET timestart = timestart + ?
04631                        WHERE courseid=? AND instance=0";
04632         $DB->execute($updatesql, array($data->timeshift, $data->courseid));
04633 
04634         $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
04635     }
04636 
04637     if (!empty($data->reset_logs)) {
04638         $DB->delete_records('log', array('course'=>$data->courseid));
04639         $status[] = array('component'=>$componentstr, 'item'=>get_string('deletelogs'), 'error'=>false);
04640     }
04641 
04642     if (!empty($data->reset_events)) {
04643         $DB->delete_records('event', array('courseid'=>$data->courseid));
04644         $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteevents', 'calendar'), 'error'=>false);
04645     }
04646 
04647     if (!empty($data->reset_notes)) {
04648         require_once($CFG->dirroot.'/notes/lib.php');
04649         note_delete_all($data->courseid);
04650         $status[] = array('component'=>$componentstr, 'item'=>get_string('deletenotes', 'notes'), 'error'=>false);
04651     }
04652 
04653     if (!empty($data->delete_blog_associations)) {
04654         require_once($CFG->dirroot.'/blog/lib.php');
04655         blog_remove_associations_for_course($data->courseid);
04656         $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteblogassociations', 'blog'), 'error'=>false);
04657     }
04658 
04659     if (!empty($data->reset_course_completion)) {
04660         // Delete course completion information
04661         $course = $DB->get_record('course', array('id'=>$data->courseid));
04662         $cc = new completion_info($course);
04663         $cc->delete_course_completion_data();
04664         $status[] = array('component'=>$componentstr, 'item'=>get_string('deletecoursecompletiondata', 'completion'), 'error'=>false);
04665     }
04666 
04667     $componentstr = get_string('roles');
04668 
04669     if (!empty($data->reset_roles_overrides)) {
04670         $children = get_child_contexts($context);
04671         foreach ($children as $child) {
04672             $DB->delete_records('role_capabilities', array('contextid'=>$child->id));
04673         }
04674         $DB->delete_records('role_capabilities', array('contextid'=>$context->id));
04675         //force refresh for logged in users
04676         mark_context_dirty($context->path);
04677         $status[] = array('component'=>$componentstr, 'item'=>get_string('deletecourseoverrides', 'role'), 'error'=>false);
04678     }
04679 
04680     if (!empty($data->reset_roles_local)) {
04681         $children = get_child_contexts($context);
04682         foreach ($children as $child) {
04683             role_unassign_all(array('contextid'=>$child->id));
04684         }
04685         //force refresh for logged in users
04686         mark_context_dirty($context->path);
04687         $status[] = array('component'=>$componentstr, 'item'=>get_string('deletelocalroles', 'role'), 'error'=>false);
04688     }
04689 
04690     // First unenrol users - this cleans some of related user data too, such as forum subscriptions, tracking, etc.
04691     $data->unenrolled = array();
04692     if (!empty($data->unenrol_users)) {
04693         $plugins = enrol_get_plugins(true);
04694         $instances = enrol_get_instances($data->courseid, true);
04695         foreach ($instances as $key=>$instance) {
04696             if (!isset($plugins[$instance->enrol])) {
04697                 unset($instances[$key]);
04698                 continue;
04699             }
04700             if (!$plugins[$instance->enrol]->allow_unenrol($instance)) {
04701                 unset($instances[$key]);
04702             }
04703         }
04704 
04705         $sqlempty = $DB->sql_empty();
04706         foreach($data->unenrol_users as $withroleid) {
04707             $sql = "SELECT DISTINCT ue.userid, ue.enrolid
04708                       FROM {user_enrolments} ue
04709                       JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)
04710                       JOIN {context} c ON (c.contextlevel = :courselevel AND c.instanceid = e.courseid)
04711                       JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.roleid = :roleid AND ra.userid = ue.userid)";
04712             $params = array('courseid'=>$data->courseid, 'roleid'=>$withroleid, 'courselevel'=>CONTEXT_COURSE);
04713 
04714             $rs = $DB->get_recordset_sql($sql, $params);
04715             foreach ($rs as $ue) {
04716                 if (!isset($instances[$ue->enrolid])) {
04717                     continue;
04718                 }
04719                 $plugins[$instances[$ue->enrolid]->enrol]->unenrol_user($instances[$ue->enrolid], $ue->userid);
04720                 $data->unenrolled[$ue->userid] = $ue->userid;
04721             }
04722         }
04723     }
04724     if (!empty($data->unenrolled)) {
04725         $status[] = array('component'=>$componentstr, 'item'=>get_string('unenrol', 'enrol').' ('.count($data->unenrolled).')', 'error'=>false);
04726     }
04727 
04728 
04729     $componentstr = get_string('groups');
04730 
04731     // remove all group members
04732     if (!empty($data->reset_groups_members)) {
04733         groups_delete_group_members($data->courseid);
04734         $status[] = array('component'=>$componentstr, 'item'=>get_string('removegroupsmembers', 'group'), 'error'=>false);
04735     }
04736 
04737     // remove all groups
04738     if (!empty($data->reset_groups_remove)) {
04739         groups_delete_groups($data->courseid, false);
04740         $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallgroups', 'group'), 'error'=>false);
04741     }
04742 
04743     // remove all grouping members
04744     if (!empty($data->reset_groupings_members)) {
04745         groups_delete_groupings_groups($data->courseid, false);
04746         $status[] = array('component'=>$componentstr, 'item'=>get_string('removegroupingsmembers', 'group'), 'error'=>false);
04747     }
04748 
04749     // remove all groupings
04750     if (!empty($data->reset_groupings_remove)) {
04751         groups_delete_groupings($data->courseid, false);
04752         $status[] = array('component'=>$componentstr, 'item'=>get_string('deleteallgroupings', 'group'), 'error'=>false);
04753     }
04754 
04755     // Look in every instance of every module for data to delete
04756     $unsupported_mods = array();
04757     if ($allmods = $DB->get_records('modules') ) {
04758         foreach ($allmods as $mod) {
04759             $modname = $mod->name;
04760             if (!$DB->count_records($modname, array('course'=>$data->courseid))) {
04761                 continue; // skip mods with no instances
04762             }
04763             $modfile = $CFG->dirroot.'/mod/'. $modname.'/lib.php';
04764             $moddeleteuserdata = $modname.'_reset_userdata';   // Function to delete user data
04765             if (file_exists($modfile)) {
04766                 include_once($modfile);
04767                 if (function_exists($moddeleteuserdata)) {
04768                     $modstatus = $moddeleteuserdata($data);
04769                     if (is_array($modstatus)) {
04770                         $status = array_merge($status, $modstatus);
04771                     } else {
04772                         debugging('Module '.$modname.' returned incorrect staus - must be an array!');
04773                     }
04774                 } else {
04775                     $unsupported_mods[] = $mod;
04776                 }
04777             } else {
04778                 debugging('Missing lib.php in '.$modname.' module!');
04779             }
04780         }
04781     }
04782 
04783     // mention unsupported mods
04784     if (!empty($unsupported_mods)) {
04785         foreach($unsupported_mods as $mod) {
04786             $status[] = array('component'=>get_string('modulenameplural', $mod->name), 'item'=>'', 'error'=>get_string('resetnotimplemented'));
04787         }
04788     }
04789 
04790 
04791     $componentstr = get_string('gradebook', 'grades');
04792     // reset gradebook
04793     if (!empty($data->reset_gradebook_items)) {
04794         remove_course_grades($data->courseid, false);
04795         grade_grab_course_grades($data->courseid);
04796         grade_regrade_final_grades($data->courseid);
04797         $status[] = array('component'=>$componentstr, 'item'=>get_string('removeallcourseitems', 'grades'), 'error'=>false);
04798 
04799     } else if (!empty($data->reset_gradebook_grades)) {
04800         grade_course_reset($data->courseid);
04801         $status[] = array('component'=>$componentstr, 'item'=>get_string('removeallcoursegrades', 'grades'), 'error'=>false);
04802     }
04803     // reset comments
04804     if (!empty($data->reset_comments)) {
04805         require_once($CFG->dirroot.'/comment/lib.php');
04806         comment::reset_course_page_comments($context);
04807     }
04808 
04809     return $status;
04810 }
04811 
04819 function generate_email_processing_address($modid,$modargs) {
04820     global $CFG;
04821 
04822     $header = $CFG->mailprefix . substr(base64_encode(pack('C',$modid)),0,2).$modargs;
04823     return $header . substr(md5($header.get_site_identifier()),0,16).'@'.$CFG->maildomain;
04824 }
04825 
04835 function moodle_process_email($modargs,$body) {
04836     global $DB;
04837 
04838     // the first char should be an unencoded letter. We'll take this as an action
04839     switch ($modargs{0}) {
04840         case 'B': { // bounce
04841             list(,$userid) = unpack('V',base64_decode(substr($modargs,1,8)));
04842             if ($user = $DB->get_record("user", array('id'=>$userid), "id,email")) {
04843                 // check the half md5 of their email
04844                 $md5check = substr(md5($user->email),0,16);
04845                 if ($md5check == substr($modargs, -16)) {
04846                     set_bounce_count($user);
04847                 }
04848                 // else maybe they've already changed it?
04849             }
04850         }
04851         break;
04852         // maybe more later?
04853     }
04854 }
04855 
04857 
04865 function get_mailer($action='get') {
04866     global $CFG;
04867 
04868     static $mailer  = null;
04869     static $counter = 0;
04870 
04871     if (!isset($CFG->smtpmaxbulk)) {
04872         $CFG->smtpmaxbulk = 1;
04873     }
04874 
04875     if ($action == 'get') {
04876         $prevkeepalive = false;
04877 
04878         if (isset($mailer) and $mailer->Mailer == 'smtp') {
04879             if ($counter < $CFG->smtpmaxbulk and !$mailer->IsError()) {
04880                 $counter++;
04881                 // reset the mailer
04882                 $mailer->Priority         = 3;
04883                 $mailer->CharSet          = 'UTF-8'; // our default
04884                 $mailer->ContentType      = "text/plain";
04885                 $mailer->Encoding         = "8bit";
04886                 $mailer->From             = "root@localhost";
04887                 $mailer->FromName         = "Root User";
04888                 $mailer->Sender           = "";
04889                 $mailer->Subject          = "";
04890                 $mailer->Body             = "";
04891                 $mailer->AltBody          = "";
04892                 $mailer->ConfirmReadingTo = "";
04893 
04894                 $mailer->ClearAllRecipients();
04895                 $mailer->ClearReplyTos();
04896                 $mailer->ClearAttachments();
04897                 $mailer->ClearCustomHeaders();
04898                 return $mailer;
04899             }
04900 
04901             $prevkeepalive = $mailer->SMTPKeepAlive;
04902             get_mailer('flush');
04903         }
04904 
04905         include_once($CFG->libdir.'/phpmailer/moodle_phpmailer.php');
04906         $mailer = new moodle_phpmailer();
04907 
04908         $counter = 1;
04909 
04910         $mailer->Version   = 'Moodle '.$CFG->version;         // mailer version
04911         $mailer->PluginDir = $CFG->libdir.'/phpmailer/';      // plugin directory (eg smtp plugin)
04912         $mailer->CharSet   = 'UTF-8';
04913 
04914         // some MTAs may do double conversion of LF if CRLF used, CRLF is required line ending in RFC 822bis
04915         if (isset($CFG->mailnewline) and $CFG->mailnewline == 'CRLF') {
04916             $mailer->LE = "\r\n";
04917         } else {
04918             $mailer->LE = "\n";
04919         }
04920 
04921         if ($CFG->smtphosts == 'qmail') {
04922             $mailer->IsQmail();                              // use Qmail system
04923 
04924         } else if (empty($CFG->smtphosts)) {
04925             $mailer->IsMail();                               // use PHP mail() = sendmail
04926 
04927         } else {
04928             $mailer->IsSMTP();                               // use SMTP directly
04929             if (!empty($CFG->debugsmtp)) {
04930                 $mailer->SMTPDebug = true;
04931             }
04932             $mailer->Host          = $CFG->smtphosts;        // specify main and backup servers
04933             $mailer->SMTPKeepAlive = $prevkeepalive;         // use previous keepalive
04934 
04935             if ($CFG->smtpuser) {                            // Use SMTP authentication
04936                 $mailer->SMTPAuth = true;
04937                 $mailer->Username = $CFG->smtpuser;
04938                 $mailer->Password = $CFG->smtppass;
04939             }
04940         }
04941 
04942         return $mailer;
04943     }
04944 
04945     $nothing = null;
04946 
04947     // keep smtp session open after sending
04948     if ($action == 'buffer') {
04949         if (!empty($CFG->smtpmaxbulk)) {
04950             get_mailer('flush');
04951             $m = get_mailer();
04952             if ($m->Mailer == 'smtp') {
04953                 $m->SMTPKeepAlive = true;
04954             }
04955         }
04956         return $nothing;
04957     }
04958 
04959     // close smtp session, but continue buffering
04960     if ($action == 'flush') {
04961         if (isset($mailer) and $mailer->Mailer == 'smtp') {
04962             if (!empty($mailer->SMTPDebug)) {
04963                 echo '<pre>'."\n";
04964             }
04965             $mailer->SmtpClose();
04966             if (!empty($mailer->SMTPDebug)) {
04967                 echo '</pre>';
04968             }
04969         }
04970         return $nothing;
04971     }
04972 
04973     // close smtp session, do not buffer anymore
04974     if ($action == 'close') {
04975         if (isset($mailer) and $mailer->Mailer == 'smtp') {
04976             get_mailer('flush');
04977             $mailer->SMTPKeepAlive = false;
04978         }
04979         $mailer = null; // better force new instance
04980         return $nothing;
04981     }
04982 }
04983 
05005 function email_to_user($user, $from, $subject, $messagetext, $messagehtml='', $attachment='', $attachname='', $usetrueaddress=true, $replyto='', $replytoname='', $wordwrapwidth=79) {
05006 
05007     global $CFG, $FULLME;
05008 
05009     if (empty($user) || empty($user->email)) {
05010         mtrace('Error: lib/moodlelib.php email_to_user(): User is null or has no email');
05011         return false;
05012     }
05013 
05014     if (!empty($user->deleted)) {
05015         // do not mail delted users
05016         mtrace('Error: lib/moodlelib.php email_to_user(): User is deleted');
05017         return false;
05018     }
05019 
05020     if (!empty($CFG->noemailever)) {
05021         // hidden setting for development sites, set in config.php if needed
05022         mtrace('Error: lib/moodlelib.php email_to_user(): Not sending email due to noemailever config setting');
05023         return true;
05024     }
05025 
05026     if (!empty($CFG->divertallemailsto)) {
05027         $subject = "[DIVERTED {$user->email}] $subject";
05028         $user = clone($user);
05029         $user->email = $CFG->divertallemailsto;
05030     }
05031 
05032     // skip mail to suspended users
05033     if ((isset($user->auth) && $user->auth=='nologin') or (isset($user->suspended) && $user->suspended)) {
05034         return true;
05035     }
05036 
05037     if (!validate_email($user->email)) {
05038         // we can not send emails to invalid addresses - it might create security issue or confuse the mailer
05039         $invalidemail = "User $user->id (".fullname($user).") email ($user->email) is invalid! Not sending.";
05040         error_log($invalidemail);
05041         if (CLI_SCRIPT) {
05042             // do not print this in standard web pages
05043             mtrace($invalidemail);
05044         }
05045         return false;
05046     }
05047 
05048     if (over_bounce_threshold($user)) {
05049         $bouncemsg = "User $user->id (".fullname($user).") is over bounce threshold! Not sending.";
05050         error_log($bouncemsg);
05051         mtrace('Error: lib/moodlelib.php email_to_user(): '.$bouncemsg);
05052         return false;
05053     }
05054 
05055     // If the user is a remote mnet user, parse the email text for URL to the
05056     // wwwroot and modify the url to direct the user's browser to login at their
05057     // home site (identity provider - idp) before hitting the link itself
05058     if (is_mnet_remote_user($user)) {
05059         require_once($CFG->dirroot.'/mnet/lib.php');
05060 
05061         $jumpurl = mnet_get_idp_jump_url($user);
05062         $callback = partial('mnet_sso_apply_indirection', $jumpurl);
05063 
05064         $messagetext = preg_replace_callback("%($CFG->wwwroot[^[:space:]]*)%",
05065                 $callback,
05066                 $messagetext);
05067         $messagehtml = preg_replace_callback("%href=[\"'`]($CFG->wwwroot[\w_:\?=#&@/;.~-]*)[\"'`]%",
05068                 $callback,
05069                 $messagehtml);
05070     }
05071     $mail = get_mailer();
05072 
05073     if (!empty($mail->SMTPDebug)) {
05074         echo '<pre>' . "\n";
05075     }
05076 
05077     $temprecipients = array();
05078     $tempreplyto = array();
05079 
05080     $supportuser = generate_email_supportuser();
05081 
05082     // make up an email address for handling bounces
05083     if (!empty($CFG->handlebounces)) {
05084         $modargs = 'B'.base64_encode(pack('V',$user->id)).substr(md5($user->email),0,16);
05085         $mail->Sender = generate_email_processing_address(0,$modargs);
05086     } else {
05087         $mail->Sender = $supportuser->email;
05088     }
05089 
05090     if (is_string($from)) { // So we can pass whatever we want if there is need
05091         $mail->From     = $CFG->noreplyaddress;
05092         $mail->FromName = $from;
05093     } else if ($usetrueaddress and $from->maildisplay) {
05094         $mail->From     = $from->email;
05095         $mail->FromName = fullname($from);
05096     } else {
05097         $mail->From     = $CFG->noreplyaddress;
05098         $mail->FromName = fullname($from);
05099         if (empty($replyto)) {
05100             $tempreplyto[] = array($CFG->noreplyaddress, get_string('noreplyname'));
05101         }
05102     }
05103 
05104     if (!empty($replyto)) {
05105         $tempreplyto[] = array($replyto, $replytoname);
05106     }
05107 
05108     $mail->Subject = substr($subject, 0, 900);
05109 
05110     $temprecipients[] = array($user->email, fullname($user));
05111 
05112     $mail->WordWrap = $wordwrapwidth;                   // set word wrap
05113 
05114     if (!empty($from->customheaders)) {                 // Add custom headers
05115         if (is_array($from->customheaders)) {
05116             foreach ($from->customheaders as $customheader) {
05117                 $mail->AddCustomHeader($customheader);
05118             }
05119         } else {
05120             $mail->AddCustomHeader($from->customheaders);
05121         }
05122     }
05123 
05124     if (!empty($from->priority)) {
05125         $mail->Priority = $from->priority;
05126     }
05127 
05128     if ($messagehtml && !empty($user->mailformat) && $user->mailformat == 1) { // Don't ever send HTML to users who don't want it
05129         $mail->IsHTML(true);
05130         $mail->Encoding = 'quoted-printable';           // Encoding to use
05131         $mail->Body    =  $messagehtml;
05132         $mail->AltBody =  "\n$messagetext\n";
05133     } else {
05134         $mail->IsHTML(false);
05135         $mail->Body =  "\n$messagetext\n";
05136     }
05137 
05138     if ($attachment && $attachname) {
05139         if (preg_match( "~\\.\\.~" ,$attachment )) {    // Security check for ".." in dir path
05140             $temprecipients[] = array($supportuser->email, fullname($supportuser, true));
05141             $mail->AddStringAttachment('Error in attachment.  User attempted to attach a filename with a unsafe name.', 'error.txt', '8bit', 'text/plain');
05142         } else {
05143             require_once($CFG->libdir.'/filelib.php');
05144             $mimetype = mimeinfo('type', $attachname);
05145             $mail->AddAttachment($CFG->dataroot .'/'. $attachment, $attachname, 'base64', $mimetype);
05146         }
05147     }
05148 
05149     // Check if the email should be sent in an other charset then the default UTF-8
05150     if ((!empty($CFG->sitemailcharset) || !empty($CFG->allowusermailcharset))) {
05151 
05152         // use the defined site mail charset or eventually the one preferred by the recipient
05153         $charset = $CFG->sitemailcharset;
05154         if (!empty($CFG->allowusermailcharset)) {
05155             if ($useremailcharset = get_user_preferences('mailcharset', '0', $user->id)) {
05156                 $charset = $useremailcharset;
05157             }
05158         }
05159 
05160         // convert all the necessary strings if the charset is supported
05161         $charsets = get_list_of_charsets();
05162         unset($charsets['UTF-8']);
05163         if (in_array($charset, $charsets)) {
05164             $textlib = textlib_get_instance();
05165             $mail->CharSet  = $charset;
05166             $mail->FromName = $textlib->convert($mail->FromName, 'utf-8', strtolower($charset));
05167             $mail->Subject  = $textlib->convert($mail->Subject, 'utf-8', strtolower($charset));
05168             $mail->Body     = $textlib->convert($mail->Body, 'utf-8', strtolower($charset));
05169             $mail->AltBody  = $textlib->convert($mail->AltBody, 'utf-8', strtolower($charset));
05170 
05171             foreach ($temprecipients as $key => $values) {
05172                 $temprecipients[$key][1] = $textlib->convert($values[1], 'utf-8', strtolower($charset));
05173             }
05174             foreach ($tempreplyto as $key => $values) {
05175                 $tempreplyto[$key][1] = $textlib->convert($values[1], 'utf-8', strtolower($charset));
05176             }
05177         }
05178     }
05179 
05180     foreach ($temprecipients as $values) {
05181         $mail->AddAddress($values[0], $values[1]);
05182     }
05183     foreach ($tempreplyto as $values) {
05184         $mail->AddReplyTo($values[0], $values[1]);
05185     }
05186 
05187     if ($mail->Send()) {
05188         set_send_count($user);
05189         $mail->IsSMTP();                               // use SMTP directly
05190         if (!empty($mail->SMTPDebug)) {
05191             echo '</pre>';
05192         }
05193         return true;
05194     } else {
05195         mtrace('ERROR: '. $mail->ErrorInfo);
05196         add_to_log(SITEID, 'library', 'mailer', $FULLME, 'ERROR: '. $mail->ErrorInfo);
05197         if (!empty($mail->SMTPDebug)) {
05198             echo '</pre>';
05199         }
05200         return false;
05201     }
05202 }
05203 
05210 function generate_email_signoff() {
05211     global $CFG;
05212 
05213     $signoff = "\n";
05214     if (!empty($CFG->supportname)) {
05215         $signoff .= $CFG->supportname."\n";
05216     }
05217     if (!empty($CFG->supportemail)) {
05218         $signoff .= $CFG->supportemail."\n";
05219     }
05220     if (!empty($CFG->supportpage)) {
05221         $signoff .= $CFG->supportpage."\n";
05222     }
05223     return $signoff;
05224 }
05225 
05231 function generate_email_supportuser() {
05232     global $CFG;
05233 
05234     static $supportuser;
05235 
05236     if (!empty($supportuser)) {
05237         return $supportuser;
05238     }
05239 
05240     $supportuser = new stdClass();
05241     $supportuser->email = $CFG->supportemail ? $CFG->supportemail : $CFG->noreplyaddress;
05242     $supportuser->firstname = $CFG->supportname ? $CFG->supportname : get_string('noreplyname');
05243     $supportuser->lastname = '';
05244     $supportuser->maildisplay = true;
05245 
05246     return $supportuser;
05247 }
05248 
05249 
05258 function setnew_password_and_mail($user) {
05259     global $CFG, $DB;
05260 
05261     $site  = get_site();
05262 
05263     $supportuser = generate_email_supportuser();
05264 
05265     $newpassword = generate_password();
05266 
05267     $DB->set_field('user', 'password', hash_internal_user_password($newpassword), array('id'=>$user->id));
05268 
05269     $a = new stdClass();
05270     $a->firstname   = fullname($user, true);
05271     $a->sitename    = format_string($site->fullname);
05272     $a->username    = $user->username;
05273     $a->newpassword = $newpassword;
05274     $a->link        = $CFG->wwwroot .'/login/';
05275     $a->signoff     = generate_email_signoff();
05276 
05277     $message = get_string('newusernewpasswordtext', '', $a);
05278 
05279     $subject = format_string($site->fullname) .': '. get_string('newusernewpasswordsubj');
05280 
05281     //directly email rather than using the messaging system to ensure its not routed to a popup or jabber
05282     return email_to_user($user, $supportuser, $subject, $message);
05283 
05284 }
05285 
05292 function reset_password_and_mail($user) {
05293     global $CFG;
05294 
05295     $site  = get_site();
05296     $supportuser = generate_email_supportuser();
05297 
05298     $userauth = get_auth_plugin($user->auth);
05299     if (!$userauth->can_reset_password() or !is_enabled_auth($user->auth)) {
05300         trigger_error("Attempt to reset user password for user $user->username with Auth $user->auth.");
05301         return false;
05302     }
05303 
05304     $newpassword = generate_password();
05305 
05306     if (!$userauth->user_update_password($user, $newpassword)) {
05307         print_error("cannotsetpassword");
05308     }
05309 
05310     $a = new stdClass();
05311     $a->firstname   = $user->firstname;
05312     $a->lastname    = $user->lastname;
05313     $a->sitename    = format_string($site->fullname);
05314     $a->username    = $user->username;
05315     $a->newpassword = $newpassword;
05316     $a->link        = $CFG->httpswwwroot .'/login/change_password.php';
05317     $a->signoff     = generate_email_signoff();
05318 
05319     $message = get_string('newpasswordtext', '', $a);
05320 
05321     $subject  = format_string($site->fullname) .': '. get_string('changedpassword');
05322 
05323     unset_user_preference('create_password', $user); // prevent cron from generating the password
05324 
05325     //directly email rather than using the messaging system to ensure its not routed to a popup or jabber
05326     return email_to_user($user, $supportuser, $subject, $message);
05327 
05328 }
05329 
05337  function send_confirmation_email($user) {
05338     global $CFG;
05339 
05340     $site = get_site();
05341     $supportuser = generate_email_supportuser();
05342 
05343     $data = new stdClass();
05344     $data->firstname = fullname($user);
05345     $data->sitename  = format_string($site->fullname);
05346     $data->admin     = generate_email_signoff();
05347 
05348     $subject = get_string('emailconfirmationsubject', '', format_string($site->fullname));
05349 
05350     $data->link  = $CFG->wwwroot .'/login/confirm.php?data='. $user->secret .'/'. urlencode($user->username);
05351     $message     = get_string('emailconfirmation', '', $data);
05352     $messagehtml = text_to_html(get_string('emailconfirmation', '', $data), false, false, true);
05353 
05354     $user->mailformat = 1;  // Always send HTML version as well
05355 
05356     //directly email rather than using the messaging system to ensure its not routed to a popup or jabber
05357     return email_to_user($user, $supportuser, $subject, $message, $messagehtml);
05358 
05359 }
05360 
05368 function send_password_change_confirmation_email($user) {
05369     global $CFG;
05370 
05371     $site = get_site();
05372     $supportuser = generate_email_supportuser();
05373 
05374     $data = new stdClass();
05375     $data->firstname = $user->firstname;
05376     $data->lastname  = $user->lastname;
05377     $data->sitename  = format_string($site->fullname);
05378     $data->link      = $CFG->httpswwwroot .'/login/forgot_password.php?p='. $user->secret .'&s='. urlencode($user->username);
05379     $data->admin     = generate_email_signoff();
05380 
05381     $message = get_string('emailpasswordconfirmation', '', $data);
05382     $subject = get_string('emailpasswordconfirmationsubject', '', format_string($site->fullname));
05383 
05384     //directly email rather than using the messaging system to ensure its not routed to a popup or jabber
05385     return email_to_user($user, $supportuser, $subject, $message);
05386 
05387 }
05388 
05396 function send_password_change_info($user) {
05397     global $CFG;
05398 
05399     $site = get_site();
05400     $supportuser = generate_email_supportuser();
05401     $systemcontext = get_context_instance(CONTEXT_SYSTEM);
05402 
05403     $data = new stdClass();
05404     $data->firstname = $user->firstname;
05405     $data->lastname  = $user->lastname;
05406     $data->sitename  = format_string($site->fullname);
05407     $data->admin     = generate_email_signoff();
05408 
05409     $userauth = get_auth_plugin($user->auth);
05410 
05411     if (!is_enabled_auth($user->auth) or $user->auth == 'nologin') {
05412         $message = get_string('emailpasswordchangeinfodisabled', '', $data);
05413         $subject = get_string('emailpasswordchangeinfosubject', '', format_string($site->fullname));
05414         //directly email rather than using the messaging system to ensure its not routed to a popup or jabber
05415         return email_to_user($user, $supportuser, $subject, $message);
05416     }
05417 
05418     if ($userauth->can_change_password() and $userauth->change_password_url()) {
05419         // we have some external url for password changing
05420         $data->link .= $userauth->change_password_url();
05421 
05422     } else {
05423         //no way to change password, sorry
05424         $data->link = '';
05425     }
05426 
05427     if (!empty($data->link) and has_capability('moodle/user:changeownpassword', $systemcontext, $user->id)) {
05428         $message = get_string('emailpasswordchangeinfo', '', $data);
05429         $subject = get_string('emailpasswordchangeinfosubject', '', format_string($site->fullname));
05430     } else {
05431         $message = get_string('emailpasswordchangeinfofail', '', $data);
05432         $subject = get_string('emailpasswordchangeinfosubject', '', format_string($site->fullname));
05433     }
05434 
05435     //directly email rather than using the messaging system to ensure its not routed to a popup or jabber
05436     return email_to_user($user, $supportuser, $subject, $message);
05437 
05438 }
05439 
05448 function email_is_not_allowed($email) {
05449     global $CFG;
05450 
05451     if (!empty($CFG->allowemailaddresses)) {
05452         $allowed = explode(' ', $CFG->allowemailaddresses);
05453         foreach ($allowed as $allowedpattern) {
05454             $allowedpattern = trim($allowedpattern);
05455             if (!$allowedpattern) {
05456                 continue;
05457             }
05458             if (strpos($allowedpattern, '.') === 0) {
05459                 if (strpos(strrev($email), strrev($allowedpattern)) === 0) {
05460                     // subdomains are in a form ".example.com" - matches "xxx@anything.example.com"
05461                     return false;
05462                 }
05463 
05464             } else if (strpos(strrev($email), strrev('@'.$allowedpattern)) === 0) { // Match!   (bug 5250)
05465                 return false;
05466             }
05467         }
05468         return get_string('emailonlyallowed', '', $CFG->allowemailaddresses);
05469 
05470     } else if (!empty($CFG->denyemailaddresses)) {
05471         $denied = explode(' ', $CFG->denyemailaddresses);
05472         foreach ($denied as $deniedpattern) {
05473             $deniedpattern = trim($deniedpattern);
05474             if (!$deniedpattern) {
05475                 continue;
05476             }
05477             if (strpos($deniedpattern, '.') === 0) {
05478                 if (strpos(strrev($email), strrev($deniedpattern)) === 0) {
05479                     // subdomains are in a form ".example.com" - matches "xxx@anything.example.com"
05480                     return get_string('emailnotallowed', '', $CFG->denyemailaddresses);
05481                 }
05482 
05483             } else if (strpos(strrev($email), strrev('@'.$deniedpattern)) === 0) { // Match!   (bug 5250)
05484                 return get_string('emailnotallowed', '', $CFG->denyemailaddresses);
05485             }
05486         }
05487     }
05488 
05489     return false;
05490 }
05491 
05493 
05499 function get_file_storage() {
05500     global $CFG;
05501 
05502     static $fs = null;
05503 
05504     if ($fs) {
05505         return $fs;
05506     }
05507 
05508     require_once("$CFG->libdir/filelib.php");
05509 
05510     if (isset($CFG->filedir)) {
05511         $filedir = $CFG->filedir;
05512     } else {
05513         $filedir = $CFG->dataroot.'/filedir';
05514     }
05515 
05516     if (isset($CFG->trashdir)) {
05517         $trashdirdir = $CFG->trashdir;
05518     } else {
05519         $trashdirdir = $CFG->dataroot.'/trashdir';
05520     }
05521 
05522     $fs = new file_storage($filedir, $trashdirdir, "$CFG->tempdir/filestorage", $CFG->directorypermissions, $CFG->filepermissions);
05523 
05524     return $fs;
05525 }
05526 
05532 function get_file_browser() {
05533     global $CFG;
05534 
05535     static $fb = null;
05536 
05537     if ($fb) {
05538         return $fb;
05539     }
05540 
05541     require_once("$CFG->libdir/filelib.php");
05542 
05543     $fb = new file_browser();
05544 
05545     return $fb;
05546 }
05547 
05554 function get_file_packer($mimetype='application/zip') {
05555     global $CFG;
05556 
05557     static $fp = array();;
05558 
05559     if (isset($fp[$mimetype])) {
05560         return $fp[$mimetype];
05561     }
05562 
05563     switch ($mimetype) {
05564         case 'application/zip':
05565         case 'application/vnd.moodle.backup':
05566             $classname = 'zip_packer';
05567             break;
05568         case 'application/x-tar':
05569 //            $classname = 'tar_packer';
05570 //            break;
05571         default:
05572             return false;
05573     }
05574 
05575     require_once("$CFG->libdir/filestorage/$classname.php");
05576     $fp[$mimetype] = new $classname();
05577 
05578     return $fp[$mimetype];
05579 }
05580 
05587 function valid_uploaded_file($newfile) {
05588     if (empty($newfile)) {
05589         return '';
05590     }
05591     if (is_uploaded_file($newfile['tmp_name']) and $newfile['size'] > 0) {
05592         return $newfile['tmp_name'];
05593     } else {
05594         return '';
05595     }
05596 }
05597 
05621 function get_max_upload_file_size($sitebytes=0, $coursebytes=0, $modulebytes=0) {
05622 
05623     if (! $filesize = ini_get('upload_max_filesize')) {
05624         $filesize = '5M';
05625     }
05626     $minimumsize = get_real_size($filesize);
05627 
05628     if ($postsize = ini_get('post_max_size')) {
05629         $postsize = get_real_size($postsize);
05630         if ($postsize < $minimumsize) {
05631             $minimumsize = $postsize;
05632         }
05633     }
05634 
05635     if ($sitebytes and $sitebytes < $minimumsize) {
05636         $minimumsize = $sitebytes;
05637     }
05638 
05639     if ($coursebytes and $coursebytes < $minimumsize) {
05640         $minimumsize = $coursebytes;
05641     }
05642 
05643     if ($modulebytes and $modulebytes < $minimumsize) {
05644         $minimumsize = $modulebytes;
05645     }
05646 
05647     return $minimumsize;
05648 }
05649 
05666 function get_max_upload_sizes($sitebytes=0, $coursebytes=0, $modulebytes=0) {
05667     global $CFG;
05668 
05669     if (!$maxsize = get_max_upload_file_size($sitebytes, $coursebytes, $modulebytes)) {
05670         return array();
05671     }
05672 
05673     $filesize[intval($maxsize)] = display_size($maxsize);
05674 
05675     $sizelist = array(10240, 51200, 102400, 512000, 1048576, 2097152,
05676                       5242880, 10485760, 20971520, 52428800, 104857600);
05677 
05678     // Allow maxbytes to be selected if it falls outside the above boundaries
05679     if (isset($CFG->maxbytes) && !in_array(get_real_size($CFG->maxbytes), $sizelist)) {
05680         // note: get_real_size() is used in order to prevent problems with invalid values
05681         $sizelist[] = get_real_size($CFG->maxbytes);
05682     }
05683 
05684     foreach ($sizelist as $sizebytes) {
05685        if ($sizebytes < $maxsize) {
05686            $filesize[intval($sizebytes)] = display_size($sizebytes);
05687        }
05688     }
05689 
05690     krsort($filesize, SORT_NUMERIC);
05691 
05692     return $filesize;
05693 }
05694 
05713 function get_directory_list($rootdir, $excludefiles='', $descend=true, $getdirs=false, $getfiles=true) {
05714 
05715     $dirs = array();
05716 
05717     if (!$getdirs and !$getfiles) {   // Nothing to show
05718         return $dirs;
05719     }
05720 
05721     if (!is_dir($rootdir)) {          // Must be a directory
05722         return $dirs;
05723     }
05724 
05725     if (!$dir = opendir($rootdir)) {  // Can't open it for some reason
05726         return $dirs;
05727     }
05728 
05729     if (!is_array($excludefiles)) {
05730         $excludefiles = array($excludefiles);
05731     }
05732 
05733     while (false !== ($file = readdir($dir))) {
05734         $firstchar = substr($file, 0, 1);
05735         if ($firstchar == '.' or $file == 'CVS' or in_array($file, $excludefiles)) {
05736             continue;
05737         }
05738         $fullfile = $rootdir .'/'. $file;
05739         if (filetype($fullfile) == 'dir') {
05740             if ($getdirs) {
05741                 $dirs[] = $file;
05742             }
05743             if ($descend) {
05744                 $subdirs = get_directory_list($fullfile, $excludefiles, $descend, $getdirs, $getfiles);
05745                 foreach ($subdirs as $subdir) {
05746                     $dirs[] = $file .'/'. $subdir;
05747                 }
05748             }
05749         } else if ($getfiles) {
05750             $dirs[] = $file;
05751         }
05752     }
05753     closedir($dir);
05754 
05755     asort($dirs);
05756 
05757     return $dirs;
05758 }
05759 
05760 
05770 function get_directory_size($rootdir, $excludefile='') {
05771     global $CFG;
05772 
05773     // do it this way if we can, it's much faster
05774     if (!empty($CFG->pathtodu) && is_executable(trim($CFG->pathtodu))) {
05775         $command = trim($CFG->pathtodu).' -sk '.escapeshellarg($rootdir);
05776         $output = null;
05777         $return = null;
05778         exec($command,$output,$return);
05779         if (is_array($output)) {
05780             return get_real_size(intval($output[0]).'k'); // we told it to return k.
05781         }
05782     }
05783 
05784     if (!is_dir($rootdir)) {          // Must be a directory
05785         return 0;
05786     }
05787 
05788     if (!$dir = @opendir($rootdir)) {  // Can't open it for some reason
05789         return 0;
05790     }
05791 
05792     $size = 0;
05793 
05794     while (false !== ($file = readdir($dir))) {
05795         $firstchar = substr($file, 0, 1);
05796         if ($firstchar == '.' or $file == 'CVS' or $file == $excludefile) {
05797             continue;
05798         }
05799         $fullfile = $rootdir .'/'. $file;
05800         if (filetype($fullfile) == 'dir') {
05801             $size += get_directory_size($fullfile, $excludefile);
05802         } else {
05803             $size += filesize($fullfile);
05804         }
05805     }
05806     closedir($dir);
05807 
05808     return $size;
05809 }
05810 
05823 function display_size($size) {
05824 
05825     static $gb, $mb, $kb, $b;
05826 
05827     if (empty($gb)) {
05828         $gb = get_string('sizegb');
05829         $mb = get_string('sizemb');
05830         $kb = get_string('sizekb');
05831         $b  = get_string('sizeb');
05832     }
05833 
05834     if ($size >= 1073741824) {
05835         $size = round($size / 1073741824 * 10) / 10 . $gb;
05836     } else if ($size >= 1048576) {
05837         $size = round($size / 1048576 * 10) / 10 . $mb;
05838     } else if ($size >= 1024) {
05839         $size = round($size / 1024 * 10) / 10 . $kb;
05840     } else {
05841         $size = intval($size) .' '. $b; // file sizes over 2GB can not work in 32bit PHP anyway
05842     }
05843     return $size;
05844 }
05845 
05854 function clean_filename($string) {
05855     return clean_param($string, PARAM_FILE);
05856 }
05857 
05858 
05860 
05866 function current_language() {
05867     global $CFG, $USER, $SESSION, $COURSE;
05868 
05869     if (!empty($COURSE->id) and $COURSE->id != SITEID and !empty($COURSE->lang)) {    // Course language can override all other settings for this page
05870         $return = $COURSE->lang;
05871 
05872     } else if (!empty($SESSION->lang)) {    // Session language can override other settings
05873         $return = $SESSION->lang;
05874 
05875     } else if (!empty($USER->lang)) {
05876         $return = $USER->lang;
05877 
05878     } else if (isset($CFG->lang)) {
05879         $return = $CFG->lang;
05880 
05881     } else {
05882         $return = 'en';
05883     }
05884 
05885     $return = str_replace('_utf8', '', $return);  // Just in case this slipped in from somewhere by accident
05886 
05887     return $return;
05888 }
05889 
05898 function get_parent_language($lang=null) {
05899     global $COURSE, $SESSION;
05900 
05901     //let's hack around the current language
05902     if (!empty($lang)) {
05903         $old_course_lang  = empty($COURSE->lang) ? '' : $COURSE->lang;
05904         $old_session_lang = empty($SESSION->lang) ? '' : $SESSION->lang;
05905         $COURSE->lang  = '';
05906         $SESSION->lang = $lang;
05907     }
05908 
05909     $parentlang = get_string('parentlanguage', 'langconfig');
05910     if ($parentlang === 'en') {
05911         $parentlang = '';
05912     }
05913 
05914     //let's hack around the current language
05915     if (!empty($lang)) {
05916         $COURSE->lang  = $old_course_lang;
05917         $SESSION->lang = $old_session_lang;
05918     }
05919 
05920     return $parentlang;
05921 }
05922 
05932 function get_string_manager($forcereload=false) {
05933     global $CFG;
05934 
05935     static $singleton = null;
05936 
05937     if ($forcereload) {
05938         $singleton = null;
05939     }
05940     if ($singleton === null) {
05941         if (empty($CFG->early_install_lang)) {
05942 
05943             if (empty($CFG->langcacheroot)) {
05944                 $langcacheroot = $CFG->cachedir . '/lang';
05945             } else {
05946                 $langcacheroot = $CFG->langcacheroot;
05947             }
05948 
05949             if (empty($CFG->langlist)) {
05950                  $translist = array();
05951             } else {
05952                 $translist = explode(',', $CFG->langlist);
05953             }
05954 
05955             if (empty($CFG->langmenucachefile)) {
05956                 $langmenucache = $CFG->cachedir . '/languages';
05957             } else {
05958                 $langmenucache = $CFG->langmenucachefile;
05959             }
05960 
05961             $singleton = new core_string_manager($CFG->langotherroot, $CFG->langlocalroot, $langcacheroot,
05962                                                  !empty($CFG->langstringcache), $translist, $langmenucache);
05963 
05964         } else {
05965             $singleton = new install_string_manager();
05966         }
05967     }
05968 
05969     return $singleton;
05970 }
05971 
05972 
05981 interface string_manager {
05992     public function get_string($identifier, $component = '', $a = NULL, $lang = NULL);
05993 
06006     public function string_exists($identifier, $component);
06007 
06014     public function get_list_of_countries($returnall = false, $lang = NULL);
06015 
06024     public function get_list_of_languages($lang = NULL, $standard = 'iso6392');
06025 
06033     public function translation_exists($lang, $includeall = true);
06034 
06040     public function get_list_of_translations($returnall = false);
06041 
06048     public function get_list_of_currencies($lang = NULL);
06049 
06058     public function load_component_strings($component, $lang, $disablecache=false, $disablelocal=false);
06059 
06063     public function reset_caches();
06064 }
06065 
06066 
06074 class core_string_manager implements string_manager {
06076     protected $otherroot;
06078     protected $localroot;
06080     protected $cacheroot;
06082     protected $cache = array();
06084     protected $countgetstring = 0;
06086     protected $countmemcache = 0;
06088     protected $countdiskcache = 0;
06090     protected $usediskcache;
06091     /* @var array limit list of translations */
06092     protected $translist;
06094     protected $menucache;
06095 
06106     public function __construct($otherroot, $localroot, $cacheroot, $usediskcache, $translist, $menucache) {
06107         $this->otherroot    = $otherroot;
06108         $this->localroot    = $localroot;
06109         $this->cacheroot    = $cacheroot;
06110         $this->usediskcache = $usediskcache;
06111         $this->translist    = $translist;
06112         $this->menucache    = $menucache;
06113     }
06114 
06120     public function get_language_dependencies($lang) {
06121         if ($lang === 'en') {
06122             return array();
06123         }
06124         if (!file_exists("$this->otherroot/$lang/langconfig.php")) {
06125             return array();
06126         }
06127         $string = array();
06128         include("$this->otherroot/$lang/langconfig.php");
06129 
06130         if (empty($string['parentlanguage'])) {
06131             return array($lang);
06132         } else {
06133             $parentlang = $string['parentlanguage'];
06134             unset($string);
06135             return array_merge($this->get_language_dependencies($parentlang), array($lang));
06136         }
06137     }
06138 
06147     public function load_component_strings($component, $lang, $disablecache=false, $disablelocal=false) {
06148         global $CFG;
06149 
06150         list($plugintype, $pluginname) = normalize_component($component);
06151         if ($plugintype == 'core' and is_null($pluginname)) {
06152             $component = 'core';
06153         } else {
06154             $component = $plugintype . '_' . $pluginname;
06155         }
06156 
06157         if (!$disablecache) {
06158             // try in-memory cache first
06159             if (isset($this->cache[$lang][$component])) {
06160                 $this->countmemcache++;
06161                 return $this->cache[$lang][$component];
06162             }
06163 
06164             // try on-disk cache then
06165             if ($this->usediskcache and file_exists($this->cacheroot . "/$lang/$component.php")) {
06166                 $this->countdiskcache++;
06167                 include($this->cacheroot . "/$lang/$component.php");
06168                 return $this->cache[$lang][$component];
06169             }
06170         }
06171 
06172         // no cache found - let us merge all possible sources of the strings
06173         if ($plugintype === 'core') {
06174             $file = $pluginname;
06175             if ($file === null) {
06176                 $file = 'moodle';
06177             }
06178             $string = array();
06179             // first load english pack
06180             if (!file_exists("$CFG->dirroot/lang/en/$file.php")) {
06181                 return array();
06182             }
06183             include("$CFG->dirroot/lang/en/$file.php");
06184             $originalkeys = array_keys($string);
06185             $originalkeys = array_flip($originalkeys);
06186 
06187             // and then corresponding local if present and allowed
06188             if (!$disablelocal and file_exists("$this->localroot/en_local/$file.php")) {
06189                 include("$this->localroot/en_local/$file.php");
06190             }
06191             // now loop through all langs in correct order
06192             $deps = $this->get_language_dependencies($lang);
06193             foreach ($deps as $dep) {
06194                 // the main lang string location
06195                 if (file_exists("$this->otherroot/$dep/$file.php")) {
06196                     include("$this->otherroot/$dep/$file.php");
06197                 }
06198                 if (!$disablelocal and file_exists("$this->localroot/{$dep}_local/$file.php")) {
06199                     include("$this->localroot/{$dep}_local/$file.php");
06200                 }
06201             }
06202 
06203         } else {
06204             if (!$location = get_plugin_directory($plugintype, $pluginname) or !is_dir($location)) {
06205                 return array();
06206             }
06207             if ($plugintype === 'mod') {
06208                 // bloody mod hack
06209                 $file = $pluginname;
06210             } else {
06211                 $file = $plugintype . '_' . $pluginname;
06212             }
06213             $string = array();
06214             // first load English pack
06215             if (!file_exists("$location/lang/en/$file.php")) {
06216                 //English pack does not exist, so do not try to load anything else
06217                 return array();
06218             }
06219             include("$location/lang/en/$file.php");
06220             $originalkeys = array_keys($string);
06221             $originalkeys = array_flip($originalkeys);
06222             // and then corresponding local english if present
06223             if (!$disablelocal and file_exists("$this->localroot/en_local/$file.php")) {
06224                 include("$this->localroot/en_local/$file.php");
06225             }
06226 
06227             // now loop through all langs in correct order
06228             $deps = $this->get_language_dependencies($lang);
06229             foreach ($deps as $dep) {
06230                 // legacy location - used by contrib only
06231                 if (file_exists("$location/lang/$dep/$file.php")) {
06232                     include("$location/lang/$dep/$file.php");
06233                 }
06234                 // the main lang string location
06235                 if (file_exists("$this->otherroot/$dep/$file.php")) {
06236                     include("$this->otherroot/$dep/$file.php");
06237                 }
06238                 // local customisations
06239                 if (!$disablelocal and file_exists("$this->localroot/{$dep}_local/$file.php")) {
06240                     include("$this->localroot/{$dep}_local/$file.php");
06241                 }
06242             }
06243         }
06244 
06245         // we do not want any extra strings from other languages - everything must be in en lang pack
06246         $string = array_intersect_key($string, $originalkeys);
06247 
06248         // now we have a list of strings from all possible sources. put it into both in-memory and on-disk
06249         // caches so we do not need to do all this merging and dependencies resolving again
06250         $this->cache[$lang][$component] = $string;
06251         if ($this->usediskcache) {
06252             check_dir_exists("$this->cacheroot/$lang");
06253             file_put_contents("$this->cacheroot/$lang/$component.php", "<?php \$this->cache['$lang']['$component'] = ".var_export($string, true).";");
06254         }
06255         return $string;
06256     }
06257 
06270     public function string_exists($identifier, $component) {
06271        $identifier = clean_param($identifier, PARAM_STRINGID);
06272         if (empty($identifier)) {
06273             return false;
06274         }
06275         $lang = current_language();
06276         $string = $this->load_component_strings($component, $lang);
06277         return isset($string[$identifier]);
06278     }
06279 
06290     public function get_string($identifier, $component = '', $a = NULL, $lang = NULL) {
06291         $this->countgetstring++;
06292         // there are very many uses of these time formating strings without the 'langconfig' component,
06293         // it would not be reasonable to expect that all of them would be converted during 2.0 migration
06294         static $langconfigstrs = array(
06295                 'strftimedate' => 1,
06296                 'strftimedatefullshort' => 1,
06297                 'strftimedateshort' => 1,
06298                 'strftimedatetime' => 1,
06299                 'strftimedatetimeshort' => 1,
06300                 'strftimedaydate' => 1,
06301                 'strftimedaydatetime' => 1,
06302                 'strftimedayshort' => 1,
06303                 'strftimedaytime' => 1,
06304                 'strftimemonthyear' => 1,
06305                 'strftimerecent' => 1,
06306                 'strftimerecentfull' => 1,
06307                 'strftimetime' => 1);
06308 
06309         if (empty($component)) {
06310             if (isset($langconfigstrs[$identifier])) {
06311                 $component = 'langconfig';
06312             } else {
06313                 $component = 'moodle';
06314             }
06315         }
06316 
06317         if ($lang === NULL) {
06318             $lang = current_language();
06319         }
06320 
06321         $string = $this->load_component_strings($component, $lang);
06322 
06323         if (!isset($string[$identifier])) {
06324             if ($component === 'pix' or $component === 'core_pix') {
06325                 // this component contains only alt tags for emoticons,
06326                 // not all of them are supposed to be defined
06327                 return '';
06328             }
06329             if ($identifier === 'parentlanguage' and ($component === 'langconfig' or $component === 'core_langconfig')) {
06330                 // parentlanguage is a special string, undefined means use English if not defined
06331                 return 'en';
06332             }
06333             if ($this->usediskcache) {
06334                 // maybe the on-disk cache is dirty - let the last attempt be to find the string in original sources
06335                 $string = $this->load_component_strings($component, $lang, true);
06336             }
06337             if (!isset($string[$identifier])) {
06338                 // the string is still missing - should be fixed by developer
06339                 list($plugintype, $pluginname) = normalize_component($component);
06340                 if ($plugintype == 'core') {
06341                     $file = "lang/en/{$component}.php";
06342                 } else if ($plugintype == 'mod') {
06343                     $file = "mod/{$pluginname}/lang/en/{$pluginname}.php";
06344                 } else {
06345                     $path = get_plugin_directory($plugintype, $pluginname);
06346                     $file = "{$path}/lang/en/{$plugintype}_{$pluginname}.php";
06347                 }
06348                 debugging("Invalid get_string() identifier: '{$identifier}' or component '{$component}'. " .
06349                         "Perhaps you are missing \$string['{$identifier}'] = ''; in {$file}?", DEBUG_DEVELOPER);
06350                 return "[[$identifier]]";
06351             }
06352         }
06353 
06354         $string = $string[$identifier];
06355 
06356         if ($a !== NULL) {
06357             if (is_object($a) or is_array($a)) {
06358                 $a = (array)$a;
06359                 $search = array();
06360                 $replace = array();
06361                 foreach ($a as $key=>$value) {
06362                     if (is_int($key)) {
06363                         // we do not support numeric keys - sorry!
06364                         continue;
06365                     }
06366                     if (is_object($value) or is_array($value)) {
06367                         // we support just string as value
06368                         continue;
06369                     }
06370                     $search[]  = '{$a->'.$key.'}';
06371                     $replace[] = (string)$value;
06372                 }
06373                 if ($search) {
06374                     $string = str_replace($search, $replace, $string);
06375                 }
06376             } else {
06377                 $string = str_replace('{$a}', (string)$a, $string);
06378             }
06379         }
06380 
06381         return $string;
06382     }
06383 
06388     public function get_performance_summary() {
06389         return array(array(
06390             'langcountgetstring' => $this->countgetstring,
06391             'langcountmemcache' => $this->countmemcache,
06392             'langcountdiskcache' => $this->countdiskcache,
06393         ), array(
06394             'langcountgetstring' => 'get_string calls',
06395             'langcountmemcache' => 'strings mem cache hits',
06396             'langcountdiskcache' => 'strings disk cache hits',
06397         ));
06398     }
06399 
06407     public function get_list_of_countries($returnall = false, $lang = NULL) {
06408         global $CFG;
06409 
06410         if ($lang === NULL) {
06411             $lang = current_language();
06412         }
06413 
06414         $countries = $this->load_component_strings('core_countries', $lang);
06415         collatorlib::asort($countries);
06416         if (!$returnall and !empty($CFG->allcountrycodes)) {
06417             $enabled = explode(',', $CFG->allcountrycodes);
06418             $return = array();
06419             foreach ($enabled as $c) {
06420                 if (isset($countries[$c])) {
06421                     $return[$c] = $countries[$c];
06422                 }
06423             }
06424             return $return;
06425         }
06426 
06427         return $countries;
06428     }
06429 
06439     public function get_list_of_languages($lang = NULL, $standard = 'iso6391') {
06440         if ($lang === NULL) {
06441             $lang = current_language();
06442         }
06443 
06444         if ($standard === 'iso6392') {
06445             $langs = $this->load_component_strings('core_iso6392', $lang);
06446             ksort($langs);
06447             return $langs;
06448 
06449         } else if ($standard === 'iso6391') {
06450             $langs2 = $this->load_component_strings('core_iso6392', $lang);
06451             static $mapping = array('aar' => 'aa', 'abk' => 'ab', 'afr' => 'af', 'aka' => 'ak', 'sqi' => 'sq', 'amh' => 'am', 'ara' => 'ar', 'arg' => 'an', 'hye' => 'hy',
06452                 'asm' => 'as', 'ava' => 'av', 'ave' => 'ae', 'aym' => 'ay', 'aze' => 'az', 'bak' => 'ba', 'bam' => 'bm', 'eus' => 'eu', 'bel' => 'be', 'ben' => 'bn', 'bih' => 'bh',
06453                 'bis' => 'bi', 'bos' => 'bs', 'bre' => 'br', 'bul' => 'bg', 'mya' => 'my', 'cat' => 'ca', 'cha' => 'ch', 'che' => 'ce', 'zho' => 'zh', 'chu' => 'cu', 'chv' => 'cv',
06454                 'cor' => 'kw', 'cos' => 'co', 'cre' => 'cr', 'ces' => 'cs', 'dan' => 'da', 'div' => 'dv', 'nld' => 'nl', 'dzo' => 'dz', 'eng' => 'en', 'epo' => 'eo', 'est' => 'et',
06455                 'ewe' => 'ee', 'fao' => 'fo', 'fij' => 'fj', 'fin' => 'fi', 'fra' => 'fr', 'fry' => 'fy', 'ful' => 'ff', 'kat' => 'ka', 'deu' => 'de', 'gla' => 'gd', 'gle' => 'ga',
06456                 'glg' => 'gl', 'glv' => 'gv', 'ell' => 'el', 'grn' => 'gn', 'guj' => 'gu', 'hat' => 'ht', 'hau' => 'ha', 'heb' => 'he', 'her' => 'hz', 'hin' => 'hi', 'hmo' => 'ho',
06457                 'hrv' => 'hr', 'hun' => 'hu', 'ibo' => 'ig', 'isl' => 'is', 'ido' => 'io', 'iii' => 'ii', 'iku' => 'iu', 'ile' => 'ie', 'ina' => 'ia', 'ind' => 'id', 'ipk' => 'ik',
06458                 'ita' => 'it', 'jav' => 'jv', 'jpn' => 'ja', 'kal' => 'kl', 'kan' => 'kn', 'kas' => 'ks', 'kau' => 'kr', 'kaz' => 'kk', 'khm' => 'km', 'kik' => 'ki', 'kin' => 'rw',
06459                 'kir' => 'ky', 'kom' => 'kv', 'kon' => 'kg', 'kor' => 'ko', 'kua' => 'kj', 'kur' => 'ku', 'lao' => 'lo', 'lat' => 'la', 'lav' => 'lv', 'lim' => 'li', 'lin' => 'ln',
06460                 'lit' => 'lt', 'ltz' => 'lb', 'lub' => 'lu', 'lug' => 'lg', 'mkd' => 'mk', 'mah' => 'mh', 'mal' => 'ml', 'mri' => 'mi', 'mar' => 'mr', 'msa' => 'ms', 'mlg' => 'mg',
06461                 'mlt' => 'mt', 'mon' => 'mn', 'nau' => 'na', 'nav' => 'nv', 'nbl' => 'nr', 'nde' => 'nd', 'ndo' => 'ng', 'nep' => 'ne', 'nno' => 'nn', 'nob' => 'nb', 'nor' => 'no',
06462                 'nya' => 'ny', 'oci' => 'oc', 'oji' => 'oj', 'ori' => 'or', 'orm' => 'om', 'oss' => 'os', 'pan' => 'pa', 'fas' => 'fa', 'pli' => 'pi', 'pol' => 'pl', 'por' => 'pt',
06463                 'pus' => 'ps', 'que' => 'qu', 'roh' => 'rm', 'ron' => 'ro', 'run' => 'rn', 'rus' => 'ru', 'sag' => 'sg', 'san' => 'sa', 'sin' => 'si', 'slk' => 'sk', 'slv' => 'sl',
06464                 'sme' => 'se', 'smo' => 'sm', 'sna' => 'sn', 'snd' => 'sd', 'som' => 'so', 'sot' => 'st', 'spa' => 'es', 'srd' => 'sc', 'srp' => 'sr', 'ssw' => 'ss', 'sun' => 'su',
06465                 'swa' => 'sw', 'swe' => 'sv', 'tah' => 'ty', 'tam' => 'ta', 'tat' => 'tt', 'tel' => 'te', 'tgk' => 'tg', 'tgl' => 'tl', 'tha' => 'th', 'bod' => 'bo', 'tir' => 'ti',
06466                 'ton' => 'to', 'tsn' => 'tn', 'tso' => 'ts', 'tuk' => 'tk', 'tur' => 'tr', 'twi' => 'tw', 'uig' => 'ug', 'ukr' => 'uk', 'urd' => 'ur', 'uzb' => 'uz', 'ven' => 've',
06467                 'vie' => 'vi', 'vol' => 'vo', 'cym' => 'cy', 'wln' => 'wa', 'wol' => 'wo', 'xho' => 'xh', 'yid' => 'yi', 'yor' => 'yo', 'zha' => 'za', 'zul' => 'zu');
06468             $langs1 = array();
06469             foreach ($mapping as $c2=>$c1) {
06470                 $langs1[$c1] = $langs2[$c2];
06471             }
06472             ksort($langs1);
06473             return $langs1;
06474 
06475         } else {
06476             debugging('Unsupported $standard parameter in get_list_of_languages() method: '.$standard);
06477         }
06478 
06479         return array();
06480     }
06481 
06489     public function translation_exists($lang, $includeall = true) {
06490 
06491         if (strpos($lang, '_local') !== false) {
06492             // _local packs are not real translations
06493             return false;
06494         }
06495         if (!$includeall and !empty($this->translist)) {
06496             if (!in_array($lang, $this->translist)) {
06497                 return false;
06498             }
06499         }
06500         if ($lang === 'en') {
06501             // part of distribution
06502             return true;
06503         }
06504         return file_exists("$this->otherroot/$lang/langconfig.php");
06505     }
06506 
06512     public function get_list_of_translations($returnall = false) {
06513         global $CFG;
06514 
06515         $languages = array();
06516 
06517         if (!empty($CFG->langcache) and is_readable($this->menucache)) {
06518             // try to re-use the cached list of all available languages
06519             $cachedlist = json_decode(file_get_contents($this->menucache), true);
06520 
06521             if (is_array($cachedlist) and !empty($cachedlist)) {
06522                 // the cache file is restored correctly
06523 
06524                 if (!$returnall and !empty($this->translist)) {
06525                     // return just enabled translations
06526                     foreach ($cachedlist as $langcode => $langname) {
06527                         if (in_array($langcode, $this->translist)) {
06528                             $languages[$langcode] = $langname;
06529                         }
06530                     }
06531                     return $languages;
06532 
06533                 } else {
06534                     // return all translations
06535                     return $cachedlist;
06536                 }
06537             }
06538         }
06539 
06540         // the cached list of languages is not available, let us populate the list
06541 
06542         if (!$returnall and !empty($this->translist)) {
06543             // return only some translations
06544             foreach ($this->translist as $lang) {
06545                 $lang = trim($lang);   //Just trim spaces to be a bit more permissive
06546                 if (strstr($lang, '_local') !== false) {
06547                     continue;
06548                 }
06549                 if (strstr($lang, '_utf8') !== false) {
06550                     continue;
06551                 }
06552                 if ($lang !== 'en' and !file_exists("$this->otherroot/$lang/langconfig.php")) {
06553                     // some broken or missing lang - can not switch to it anyway
06554                     continue;
06555                 }
06556                 $string = $this->load_component_strings('langconfig', $lang);
06557                 if (!empty($string['thislanguage'])) {
06558                     $languages[$lang] = $string['thislanguage'].' ('. $lang .')';
06559                 }
06560                 unset($string);
06561             }
06562 
06563         } else {
06564             // return all languages available in system
06565             $langdirs = get_list_of_plugins('', '', $this->otherroot);
06566 
06567             $langdirs = array_merge($langdirs, array("$CFG->dirroot/lang/en"=>'en'));
06568             // Sort all
06569 
06570             // Loop through all langs and get info
06571             foreach ($langdirs as $lang) {
06572                 if (strstr($lang, '_local') !== false) {
06573                     continue;
06574                 }
06575                 if (strstr($lang, '_utf8') !== false) {
06576                     continue;
06577                 }
06578                 $string = $this->load_component_strings('langconfig', $lang);
06579                 if (!empty($string['thislanguage'])) {
06580                     $languages[$lang] = $string['thislanguage'].' ('. $lang .')';
06581                 }
06582                 unset($string);
06583             }
06584 
06585             if (!empty($CFG->langcache) and !empty($this->menucache)) {
06586                 // cache the list so that it can be used next time
06587                 collatorlib::asort($languages);
06588                 check_dir_exists(dirname($this->menucache), true, true);
06589                 file_put_contents($this->menucache, json_encode($languages));
06590             }
06591         }
06592 
06593         collatorlib::asort($languages);
06594 
06595         return $languages;
06596     }
06597 
06604     public function get_list_of_currencies($lang = NULL) {
06605         if ($lang === NULL) {
06606             $lang = current_language();
06607         }
06608 
06609         $currencies = $this->load_component_strings('core_currencies', $lang);
06610         asort($currencies);
06611 
06612         return $currencies;
06613     }
06614 
06618     public function reset_caches() {
06619         global $CFG;
06620         require_once("$CFG->libdir/filelib.php");
06621 
06622         // clear the on-disk disk with aggregated string files
06623         fulldelete($this->cacheroot);
06624 
06625         // clear the in-memory cache of loaded strings
06626         $this->cache = array();
06627 
06628         // clear the cache containing the list of available translations
06629         // and re-populate it again
06630         fulldelete($this->menucache);
06631         $this->get_list_of_translations(true);
06632     }
06633 }
06634 
06635 
06645 class install_string_manager implements string_manager {
06647     protected $installroot;
06648 
06652     public function __construct() {
06653         global $CFG;
06654         $this->installroot = "$CFG->dirroot/install/lang";
06655     }
06656 
06665     public function load_component_strings($component, $lang, $disablecache=false, $disablelocal=false) {
06666         // not needed in installer
06667         return array();
06668     }
06669 
06682     public function string_exists($identifier, $component) {
06683         $identifier = clean_param($identifier, PARAM_STRINGID);
06684         if (empty($identifier)) {
06685             return false;
06686         }
06687         // simple old style hack ;)
06688         $str = get_string($identifier, $component);
06689         return (strpos($str, '[[') === false);
06690     }
06691 
06702     public function get_string($identifier, $component = '', $a = NULL, $lang = NULL) {
06703         if (!$component) {
06704             $component = 'moodle';
06705         }
06706 
06707         if ($lang === NULL) {
06708             $lang = current_language();
06709         }
06710 
06711         //get parent lang
06712         $parent = '';
06713         if ($lang !== 'en' and $identifier !== 'parentlanguage' and $component !== 'langconfig') {
06714             if (file_exists("$this->installroot/$lang/langconfig.php")) {
06715                 $string = array();
06716                 include("$this->installroot/$lang/langconfig.php");
06717                 if (isset($string['parentlanguage'])) {
06718                     $parent = $string['parentlanguage'];
06719                 }
06720                 unset($string);
06721             }
06722         }
06723 
06724         // include en string first
06725         if (!file_exists("$this->installroot/en/$component.php")) {
06726             return "[[$identifier]]";
06727         }
06728         $string = array();
06729         include("$this->installroot/en/$component.php");
06730 
06731         // now override en with parent if defined
06732         if ($parent and $parent !== 'en' and file_exists("$this->installroot/$parent/$component.php")) {
06733             include("$this->installroot/$parent/$component.php");
06734         }
06735 
06736         // finally override with requested language
06737         if ($lang !== 'en' and file_exists("$this->installroot/$lang/$component.php")) {
06738             include("$this->installroot/$lang/$component.php");
06739         }
06740 
06741         if (!isset($string[$identifier])) {
06742             return "[[$identifier]]";
06743         }
06744 
06745         $string = $string[$identifier];
06746 
06747         if ($a !== NULL) {
06748             if (is_object($a) or is_array($a)) {
06749                 $a = (array)$a;
06750                 $search = array();
06751                 $replace = array();
06752                 foreach ($a as $key=>$value) {
06753                     if (is_int($key)) {
06754                         // we do not support numeric keys - sorry!
06755                         continue;
06756                     }
06757                     $search[]  = '{$a->'.$key.'}';
06758                     $replace[] = (string)$value;
06759                 }
06760                 if ($search) {
06761                     $string = str_replace($search, $replace, $string);
06762                 }
06763             } else {
06764                 $string = str_replace('{$a}', (string)$a, $string);
06765             }
06766         }
06767 
06768         return $string;
06769     }
06770 
06778     public function get_list_of_countries($returnall = false, $lang = NULL) {
06779         //not used in installer
06780         return array();
06781     }
06782 
06791     public function get_list_of_languages($lang = NULL, $standard = 'iso6392') {
06792         //not used in installer
06793         return array();
06794     }
06795 
06803     public function translation_exists($lang, $includeall = true) {
06804         return file_exists($this->installroot.'/'.$lang.'/langconfig.php');
06805     }
06806 
06812     public function get_list_of_translations($returnall = false) {
06813         // return all is ignored here - we need to know all langs in installer
06814         $languages = array();
06815         // Get raw list of lang directories
06816         $langdirs = get_list_of_plugins('install/lang');
06817         asort($langdirs);
06818         // Get some info from each lang
06819         foreach ($langdirs as $lang) {
06820             if (file_exists($this->installroot.'/'.$lang.'/langconfig.php')) {
06821                 $string = array();
06822                 include($this->installroot.'/'.$lang.'/langconfig.php');
06823                 if (!empty($string['thislanguage'])) {
06824                     $languages[$lang] = $string['thislanguage'].' ('.$lang.')';
06825                 }
06826             }
06827         }
06828         // Return array
06829         return $languages;
06830     }
06831 
06838     public function get_list_of_currencies($lang = NULL) {
06839         // not used in installer
06840         return array();
06841     }
06842 
06846     public function reset_caches() {}
06847 }
06848 
06849 
06904 function get_string($identifier, $component = '', $a = NULL) {
06905     global $CFG;
06906 
06907     $identifier = clean_param($identifier, PARAM_STRINGID);
06908     if (empty($identifier)) {
06909         throw new coding_exception('Invalid string identifier. Most probably some illegal character is part of the string identifier. Please fix your get_string() call and string definition');
06910     }
06911 
06912     if (func_num_args() > 3) {
06913         debugging('extralocations parameter in get_string() is not supported any more, please use standard lang locations only.');
06914     }
06915 
06916     if (strpos($component, '/') !== false) {
06917         debugging('The module name you passed to get_string is the deprecated format ' .
06918                 'like mod/mymod or block/myblock. The correct form looks like mymod, or block_myblock.' , DEBUG_DEVELOPER);
06919         $componentpath = explode('/', $component);
06920 
06921         switch ($componentpath[0]) {
06922             case 'mod':
06923                 $component = $componentpath[1];
06924                 break;
06925             case 'blocks':
06926             case 'block':
06927                 $component = 'block_'.$componentpath[1];
06928                 break;
06929             case 'enrol':
06930                 $component = 'enrol_'.$componentpath[1];
06931                 break;
06932             case 'format':
06933                 $component = 'format_'.$componentpath[1];
06934                 break;
06935             case 'grade':
06936                 $component = 'grade'.$componentpath[1].'_'.$componentpath[2];
06937                 break;
06938         }
06939     }
06940 
06941     $result = get_string_manager()->get_string($identifier, $component, $a);
06942 
06943     // Debugging feature lets you display string identifier and component
06944     if (isset($CFG->debugstringids) && $CFG->debugstringids && optional_param('strings', 0, PARAM_INT)) {
06945         $result .= ' {' . $identifier . '/' . $component . '}';
06946     }
06947     return $result;
06948 }
06949 
06957 function get_strings($array, $component = '') {
06958    $string = new stdClass;
06959    foreach ($array as $item) {
06960        $string->$item = get_string($item, $component);
06961    }
06962    return $string;
06963 }
06964 
06988 function print_string($identifier, $component = '', $a = NULL) {
06989     echo get_string($identifier, $component, $a);
06990 }
06991 
07000 function get_list_of_charsets() {
07001 
07002     $charsets = array(
07003         'EUC-JP'     => 'EUC-JP',
07004         'ISO-2022-JP'=> 'ISO-2022-JP',
07005         'ISO-8859-1' => 'ISO-8859-1',
07006         'SHIFT-JIS'  => 'SHIFT-JIS',
07007         'GB2312'     => 'GB2312',
07008         'GB18030'    => 'GB18030', // gb18030 not supported by typo and mbstring
07009         'UTF-8'      => 'UTF-8');
07010 
07011     asort($charsets);
07012 
07013     return $charsets;
07014 }
07015 
07021 function get_list_of_themes() {
07022     global $CFG;
07023 
07024     $themes = array();
07025 
07026     if (!empty($CFG->themelist)) {       // use admin's list of themes
07027         $themelist = explode(',', $CFG->themelist);
07028     } else {
07029         $themelist = array_keys(get_plugin_list("theme"));
07030     }
07031 
07032     foreach ($themelist as $key => $themename) {
07033         $theme = theme_config::load($themename);
07034         $themes[$themename] = $theme;
07035     }
07036 
07037     collatorlib::asort_objects_by_method($themes, 'get_theme_name');
07038 
07039     return $themes;
07040 }
07041 
07049 function get_list_of_timezones() {
07050     global $CFG, $DB;
07051 
07052     static $timezones;
07053 
07054     if (!empty($timezones)) {    // This function has been called recently
07055         return $timezones;
07056     }
07057 
07058     $timezones = array();
07059 
07060     if ($rawtimezones = $DB->get_records_sql("SELECT MAX(id), name FROM {timezone} GROUP BY name")) {
07061         foreach($rawtimezones as $timezone) {
07062             if (!empty($timezone->name)) {
07063                 if (get_string_manager()->string_exists(strtolower($timezone->name), 'timezones')) {
07064                     $timezones[$timezone->name] = get_string(strtolower($timezone->name), 'timezones');
07065                 } else {
07066                     $timezones[$timezone->name] = $timezone->name;
07067                 }
07068                 if (substr($timezones[$timezone->name], 0, 1) == '[') {  // No translation found
07069                     $timezones[$timezone->name] = $timezone->name;
07070                 }
07071             }
07072         }
07073     }
07074 
07075     asort($timezones);
07076 
07077     for ($i = -13; $i <= 13; $i += .5) {
07078         $tzstring = 'UTC';
07079         if ($i < 0) {
07080             $timezones[sprintf("%.1f", $i)] = $tzstring . $i;
07081         } else if ($i > 0) {
07082             $timezones[sprintf("%.1f", $i)] = $tzstring . '+' . $i;
07083         } else {
07084             $timezones[sprintf("%.1f", $i)] = $tzstring;
07085         }
07086     }
07087 
07088     return $timezones;
07089 }
07090 
07096 function get_emoticon_manager() {
07097     static $singleton = null;
07098 
07099     if (is_null($singleton)) {
07100         $singleton = new emoticon_manager();
07101     }
07102 
07103     return $singleton;
07104 }
07105 
07116 class emoticon_manager {
07117 
07123     public function get_emoticons() {
07124         global $CFG;
07125 
07126         if (empty($CFG->emoticons)) {
07127             return array();
07128         }
07129 
07130         $emoticons = $this->decode_stored_config($CFG->emoticons);
07131 
07132         if (!is_array($emoticons)) {
07133             // something is wrong with the format of stored setting
07134             debugging('Invalid format of emoticons setting, please resave the emoticons settings form', DEBUG_NORMAL);
07135             return array();
07136         }
07137 
07138         return $emoticons;
07139     }
07140 
07148     public function prepare_renderable_emoticon(stdClass $emoticon, array $attributes = array()) {
07149         $stringmanager = get_string_manager();
07150         if ($stringmanager->string_exists($emoticon->altidentifier, $emoticon->altcomponent)) {
07151             $alt = get_string($emoticon->altidentifier, $emoticon->altcomponent);
07152         } else {
07153             $alt = s($emoticon->text);
07154         }
07155         return new pix_emoticon($emoticon->imagename, $alt, $emoticon->imagecomponent, $attributes);
07156     }
07157 
07165     public function encode_stored_config(array $emoticons) {
07166         return json_encode($emoticons);
07167     }
07168 
07176     public function decode_stored_config($encoded) {
07177         $decoded = json_decode($encoded);
07178         if (!is_array($decoded)) {
07179             return null;
07180         }
07181         return $decoded;
07182     }
07183 
07189     public function default_emoticons() {
07190         return array(
07191             $this->prepare_emoticon_object(":-)", 's/smiley', 'smiley'),
07192             $this->prepare_emoticon_object(":)", 's/smiley', 'smiley'),
07193             $this->prepare_emoticon_object(":-D", 's/biggrin', 'biggrin'),
07194             $this->prepare_emoticon_object(";-)", 's/wink', 'wink'),
07195             $this->prepare_emoticon_object(":-/", 's/mixed', 'mixed'),
07196             $this->prepare_emoticon_object("V-.", 's/thoughtful', 'thoughtful'),
07197             $this->prepare_emoticon_object(":-P", 's/tongueout', 'tongueout'),
07198             $this->prepare_emoticon_object(":-p", 's/tongueout', 'tongueout'),
07199             $this->prepare_emoticon_object("B-)", 's/cool', 'cool'),
07200             $this->prepare_emoticon_object("^-)", 's/approve', 'approve'),
07201             $this->prepare_emoticon_object("8-)", 's/wideeyes', 'wideeyes'),
07202             $this->prepare_emoticon_object(":o)", 's/clown', 'clown'),
07203             $this->prepare_emoticon_object(":-(", 's/sad', 'sad'),
07204             $this->prepare_emoticon_object(":(", 's/sad', 'sad'),
07205             $this->prepare_emoticon_object("8-.", 's/shy', 'shy'),
07206             $this->prepare_emoticon_object(":-I", 's/blush', 'blush'),
07207             $this->prepare_emoticon_object(":-X", 's/kiss', 'kiss'),
07208             $this->prepare_emoticon_object("8-o", 's/surprise', 'surprise'),
07209             $this->prepare_emoticon_object("P-|", 's/blackeye', 'blackeye'),
07210             $this->prepare_emoticon_object("8-[", 's/angry', 'angry'),
07211             $this->prepare_emoticon_object("(grr)", 's/angry', 'angry'),
07212             $this->prepare_emoticon_object("xx-P", 's/dead', 'dead'),
07213             $this->prepare_emoticon_object("|-.", 's/sleepy', 'sleepy'),
07214             $this->prepare_emoticon_object("}-]", 's/evil', 'evil'),
07215             $this->prepare_emoticon_object("(h)", 's/heart', 'heart'),
07216             $this->prepare_emoticon_object("(heart)", 's/heart', 'heart'),
07217             $this->prepare_emoticon_object("(y)", 's/yes', 'yes', 'core'),
07218             $this->prepare_emoticon_object("(n)", 's/no', 'no', 'core'),
07219             $this->prepare_emoticon_object("(martin)", 's/martin', 'martin'),
07220             $this->prepare_emoticon_object("( )", 's/egg', 'egg'),
07221         );
07222     }
07223 
07234     protected function prepare_emoticon_object($text, $imagename, $altidentifier = null, $altcomponent = 'core_pix', $imagecomponent = 'core') {
07235         return (object)array(
07236             'text'           => $text,
07237             'imagename'      => $imagename,
07238             'imagecomponent' => $imagecomponent,
07239             'altidentifier'  => $altidentifier,
07240             'altcomponent'   => $altcomponent,
07241         );
07242     }
07243 }
07244 
07246 
07255 function rc4encrypt($data) {
07256     $password = get_site_identifier();
07257     return endecrypt($password, $data, '');
07258 }
07259 
07268 function rc4decrypt($data) {
07269     $password = get_site_identifier();
07270     return endecrypt($password, $data, 'de');
07271 }
07272 
07283 function endecrypt ($pwd, $data, $case) {
07284 
07285     if ($case == 'de') {
07286         $data = urldecode($data);
07287     }
07288 
07289     $key[] = '';
07290     $box[] = '';
07291     $temp_swap = '';
07292     $pwd_length = 0;
07293 
07294     $pwd_length = strlen($pwd);
07295 
07296     for ($i = 0; $i <= 255; $i++) {
07297         $key[$i] = ord(substr($pwd, ($i % $pwd_length), 1));
07298         $box[$i] = $i;
07299     }
07300 
07301     $x = 0;
07302 
07303     for ($i = 0; $i <= 255; $i++) {
07304         $x = ($x + $box[$i] + $key[$i]) % 256;
07305         $temp_swap = $box[$i];
07306         $box[$i] = $box[$x];
07307         $box[$x] = $temp_swap;
07308     }
07309 
07310     $temp = '';
07311     $k = '';
07312 
07313     $cipherby = '';
07314     $cipher = '';
07315 
07316     $a = 0;
07317     $j = 0;
07318 
07319     for ($i = 0; $i < strlen($data); $i++) {
07320         $a = ($a + 1) % 256;
07321         $j = ($j + $box[$a]) % 256;
07322         $temp = $box[$a];
07323         $box[$a] = $box[$j];
07324         $box[$j] = $temp;
07325         $k = $box[(($box[$a] + $box[$j]) % 256)];
07326         $cipherby = ord(substr($data, $i, 1)) ^ $k;
07327         $cipher .= chr($cipherby);
07328     }
07329 
07330     if ($case == 'de') {
07331         $cipher = urldecode(urlencode($cipher));
07332     } else {
07333         $cipher = urlencode($cipher);
07334     }
07335 
07336     return $cipher;
07337 }
07338 
07340 
07348 function get_plugin_directory($plugintype, $name) {
07349     global $CFG;
07350 
07351     if ($plugintype === '') {
07352         $plugintype = 'mod';
07353     }
07354 
07355     $types = get_plugin_types(true);
07356     if (!array_key_exists($plugintype, $types)) {
07357         return NULL;
07358     }
07359     $name = clean_param($name, PARAM_SAFEDIR); // just in case ;-)
07360 
07361     if (!empty($CFG->themedir) and $plugintype === 'theme') {
07362         if (!is_dir($types['theme'] . '/' . $name)) {
07363             // ok, so the theme is supposed to be in the $CFG->themedir
07364             return $CFG->themedir . '/' . $name;
07365         }
07366     }
07367 
07368     return $types[$plugintype].'/'.$name;
07369 }
07370 
07378 function get_component_directory($component) {
07379     global $CFG;
07380 /*
07381     $simpletest = false;
07382     if (strpos($component, 'simpletest_') === 0) {
07383         $subdir = substr($component, strlen('simpletest_'));
07384         //TODO: this looks borked, where is it used actually?
07385         return $subdir;
07386     }
07387 */
07388     list($type, $plugin) = normalize_component($component);
07389 
07390     if ($type === 'core') {
07391         if ($plugin === NULL ) {
07392             $path = $CFG->libdir;
07393         } else {
07394             $subsystems = get_core_subsystems();
07395             if (isset($subsystems[$plugin])) {
07396                 $path = $CFG->dirroot.'/'.$subsystems[$plugin];
07397             } else {
07398                 $path = NULL;
07399             }
07400         }
07401 
07402     } else {
07403         $path = get_plugin_directory($type, $plugin);
07404     }
07405 
07406     return $path;
07407 }
07408 
07414 function normalize_component($component) {
07415     if ($component === 'moodle' or $component === 'core') {
07416         $type = 'core';
07417         $plugin = NULL;
07418 
07419     } else if (strpos($component, '_') === false) {
07420         $subsystems = get_core_subsystems();
07421         if (array_key_exists($component, $subsystems)) {
07422             $type   = 'core';
07423             $plugin = $component;
07424         } else {
07425             // everything else is a module
07426             $type   = 'mod';
07427             $plugin = $component;
07428         }
07429 
07430     } else {
07431         list($type, $plugin) = explode('_', $component, 2);
07432         $plugintypes = get_plugin_types(false);
07433         if ($type !== 'core' and !array_key_exists($type, $plugintypes)) {
07434             $type   = 'mod';
07435             $plugin = $component;
07436         }
07437     }
07438 
07439     return array($type, $plugin);
07440 }
07441 
07456 function get_core_subsystems() {
07457     global $CFG;
07458 
07459     static $info = null;
07460 
07461     if (!$info) {
07462         $info = array(
07463             'access'      => NULL,
07464             'admin'       => $CFG->admin,
07465             'auth'        => 'auth',
07466             'backup'      => 'backup/util/ui',
07467             'block'       => 'blocks',
07468             'blog'        => 'blog',
07469             'bulkusers'   => NULL,
07470             'calendar'    => 'calendar',
07471             'cohort'      => 'cohort',
07472             'condition'   => NULL,
07473             'completion'  => NULL,
07474             'countries'   => NULL,
07475             'course'      => 'course',
07476             'currencies'  => NULL,
07477             'dbtransfer'  => NULL,
07478             'debug'       => NULL,
07479             'dock'        => NULL,
07480             'editor'      => 'lib/editor',
07481             'edufields'   => NULL,
07482             'enrol'       => 'enrol',
07483             'error'       => NULL,
07484             'filepicker'  => NULL,
07485             'files'       => 'files',
07486             'filters'     => NULL,
07487             'fonts'       => NULL,
07488             'form'        => 'lib/form',
07489             'grades'      => 'grade',
07490             'grading'     => 'grade/grading',
07491             'group'       => 'group',
07492             'help'        => NULL,
07493             'hub'         => NULL,
07494             'imscc'       => NULL,
07495             'install'     => NULL,
07496             'iso6392'     => NULL,
07497             'langconfig'  => NULL,
07498             'license'     => NULL,
07499             'mathslib'    => NULL,
07500             'message'     => 'message',
07501             'mimetypes'   => NULL,
07502             'mnet'        => 'mnet',
07503             'moodle.org'  => NULL, // the dot is nasty, watch out! should be renamed to moodleorg
07504             'my'          => 'my',
07505             'notes'       => 'notes',
07506             'pagetype'    => NULL,
07507             'pix'         => NULL,
07508             'plagiarism'  => 'plagiarism',
07509             'plugin'      => NULL,
07510             'portfolio'   => 'portfolio',
07511             'publish'     => 'course/publish',
07512             'question'    => 'question',
07513             'rating'      => 'rating',
07514             'register'    => 'admin/registration', //TODO: this is wrong, unfortunately we would need to modify hub code to pass around the correct url
07515             'repository'  => 'repository',
07516             'rss'         => 'rss',
07517             'role'        => $CFG->admin.'/role',
07518             'search'      => 'search',
07519             'table'       => NULL,
07520             'tag'         => 'tag',
07521             'timezones'   => NULL,
07522             'user'        => 'user',
07523             'userkey'     => NULL,
07524             'webservice'  => 'webservice',
07525         );
07526     }
07527 
07528     return $info;
07529 }
07530 
07536 function get_plugin_types($fullpaths=true) {
07537     global $CFG;
07538 
07539     static $info     = null;
07540     static $fullinfo = null;
07541 
07542     if (!$info) {
07543         $info = array('qtype'         => 'question/type',
07544                       'mod'           => 'mod',
07545                       'auth'          => 'auth',
07546                       'enrol'         => 'enrol',
07547                       'message'       => 'message/output',
07548                       'block'         => 'blocks',
07549                       'filter'        => 'filter',
07550                       'editor'        => 'lib/editor',
07551                       'format'        => 'course/format',
07552                       'profilefield'  => 'user/profile/field',
07553                       'report'        => 'report',
07554                       'coursereport'  => 'course/report', // must be after system reports
07555                       'gradeexport'   => 'grade/export',
07556                       'gradeimport'   => 'grade/import',
07557                       'gradereport'   => 'grade/report',
07558                       'gradingform'   => 'grade/grading/form',
07559                       'mnetservice'   => 'mnet/service',
07560                       'webservice'    => 'webservice',
07561                       'repository'    => 'repository',
07562                       'portfolio'     => 'portfolio',
07563                       'qbehaviour'    => 'question/behaviour',
07564                       'qformat'       => 'question/format',
07565                       'plagiarism'    => 'plagiarism',
07566                       'tool'          => $CFG->admin.'/tool',
07567                       'theme'         => 'theme',  // this is a bit hacky, themes may be in $CFG->themedir too
07568         );
07569 
07570         $mods = get_plugin_list('mod');
07571         foreach ($mods as $mod => $moddir) {
07572             if (file_exists("$moddir/db/subplugins.php")) {
07573                 $subplugins = array();
07574                 include("$moddir/db/subplugins.php");
07575                 foreach ($subplugins as $subtype=>$dir) {
07576                     $info[$subtype] = $dir;
07577                 }
07578             }
07579         }
07580 
07581         // local is always last!
07582         $info['local'] = 'local';
07583 
07584         $fullinfo = array();
07585         foreach ($info as $type => $dir) {
07586             $fullinfo[$type] = $CFG->dirroot.'/'.$dir;
07587         }
07588     }
07589 
07590     return ($fullpaths ? $fullinfo : $info);
07591 }
07592 
07598 function get_plugin_list($plugintype) {
07599     global $CFG;
07600 
07601     $ignored = array('CVS', '_vti_cnf', 'simpletest', 'db', 'yui', 'phpunit');
07602     if ($plugintype == 'auth') {
07603         // Historically we have had an auth plugin called 'db', so allow a special case.
07604         $key = array_search('db', $ignored);
07605         if ($key !== false) {
07606             unset($ignored[$key]);
07607         }
07608     }
07609 
07610     if ($plugintype === '') {
07611         $plugintype = 'mod';
07612     }
07613 
07614     $fulldirs = array();
07615 
07616     if ($plugintype === 'mod') {
07617         // mod is an exception because we have to call this function from get_plugin_types()
07618         $fulldirs[] = $CFG->dirroot.'/mod';
07619 
07620     } else if ($plugintype === 'theme') {
07621         $fulldirs[] = $CFG->dirroot.'/theme';
07622         // themes are special because they may be stored also in separate directory
07623         if (!empty($CFG->themedir) and file_exists($CFG->themedir) and is_dir($CFG->themedir) ) {
07624             $fulldirs[] = $CFG->themedir;
07625         }
07626 
07627     } else {
07628         $types = get_plugin_types(true);
07629         if (!array_key_exists($plugintype, $types)) {
07630             return array();
07631         }
07632         $fulldir = $types[$plugintype];
07633         if (!file_exists($fulldir)) {
07634             return array();
07635         }
07636         $fulldirs[] = $fulldir;
07637     }
07638 
07639     $result = array();
07640 
07641     foreach ($fulldirs as $fulldir) {
07642         if (!is_dir($fulldir)) {
07643             continue;
07644         }
07645         $items = new DirectoryIterator($fulldir);
07646         foreach ($items as $item) {
07647             if ($item->isDot() or !$item->isDir()) {
07648                 continue;
07649             }
07650             $pluginname = $item->getFilename();
07651             if (in_array($pluginname, $ignored)) {
07652                 continue;
07653             }
07654             $pluginname = clean_param($pluginname, PARAM_PLUGIN);
07655             if (empty($pluginname)) {
07656                 // better ignore plugins with problematic names here
07657                 continue;
07658             }
07659             $result[$pluginname] = $fulldir.'/'.$pluginname;
07660             unset($item);
07661         }
07662         unset($items);
07663     }
07664 
07665     //TODO: implement better sorting once we migrated all plugin names to 'pluginname', ksort does not work for unicode, that is why we have to sort by the dir name, not the strings!
07666     ksort($result);
07667     return $result;
07668 }
07669 
07679 function get_plugin_list_with_file($plugintype, $file, $include = false) {
07680     global $CFG; // Necessary in case it is referenced by include()d PHP scripts.
07681 
07682     $plugins = array();
07683 
07684     foreach(get_plugin_list($plugintype) as $plugin => $dir) {
07685         $path = $dir . '/' . $file;
07686         if (file_exists($path)) {
07687             if ($include) {
07688                 include_once($path);
07689             }
07690             $plugins[$plugin] = $path;
07691         }
07692     }
07693 
07694     return $plugins;
07695 }
07696 
07710 function get_plugin_list_with_function($plugintype, $function, $file = 'lib.php') {
07711     $pluginfunctions = array();
07712     foreach (get_plugin_list_with_file($plugintype, $file, true) as $plugin => $notused) {
07713         $fullfunction = $plugintype . '_' . $plugin . '_' . $function;
07714 
07715         if (function_exists($fullfunction)) {
07716             // Function exists with standard name. Store, indexed by
07717             // frankenstyle name of plugin
07718             $pluginfunctions[$plugintype . '_' . $plugin] = $fullfunction;
07719 
07720         } else if ($plugintype === 'mod') {
07721             // For modules, we also allow plugin without full frankenstyle
07722             // but just starting with the module name
07723             $shortfunction = $plugin . '_' . $function;
07724             if (function_exists($shortfunction)) {
07725                 $pluginfunctions[$plugintype . '_' . $plugin] = $shortfunction;
07726             }
07727         }
07728     }
07729     return $pluginfunctions;
07730 }
07731 
07745 function get_plugin_list_with_class($plugintype, $class, $file) {
07746     if ($class) {
07747         $suffix = '_' . $class;
07748     } else {
07749         $suffix = '';
07750     }
07751 
07752     $pluginclasses = array();
07753     foreach (get_plugin_list_with_file($plugintype, $file, true) as $plugin => $notused) {
07754         $classname = $plugintype . '_' . $plugin . $suffix;
07755         if (class_exists($classname)) {
07756             $pluginclasses[$plugintype . '_' . $plugin] = $classname;
07757         }
07758     }
07759 
07760     return $pluginclasses;
07761 }
07762 
07776 function get_list_of_plugins($directory='mod', $exclude='', $basedir='') {
07777     global $CFG;
07778 
07779     $plugins = array();
07780 
07781     if (empty($basedir)) {
07782         $basedir = $CFG->dirroot .'/'. $directory;
07783 
07784     } else {
07785         $basedir = $basedir .'/'. $directory;
07786     }
07787 
07788     if (file_exists($basedir) && filetype($basedir) == 'dir') {
07789         $dirhandle = opendir($basedir);
07790         while (false !== ($dir = readdir($dirhandle))) {
07791             $firstchar = substr($dir, 0, 1);
07792             if ($firstchar === '.' or $dir === 'CVS' or $dir === '_vti_cnf' or $dir === 'simpletest' or $dir === 'yui' or $dir === 'phpunit' or $dir === $exclude) {
07793                 continue;
07794             }
07795             if (filetype($basedir .'/'. $dir) != 'dir') {
07796                 continue;
07797             }
07798             $plugins[] = $dir;
07799         }
07800         closedir($dirhandle);
07801     }
07802     if ($plugins) {
07803         asort($plugins);
07804     }
07805     return $plugins;
07806 }
07807 
07821 function plugin_callback($type, $name, $feature, $action, $params = null, $default = null) {
07822     return component_callback($type . '_' . $name, $feature . '_' . $action, (array) $params, $default);
07823 }
07824 
07834 function component_callback($component, $function, array $params = array(), $default = null) {
07835     global $CFG; // this is needed for require_once() below
07836 
07837     $cleancomponent = clean_param($component, PARAM_COMPONENT);
07838     if (empty($cleancomponent)) {
07839         throw new coding_exception('Invalid component used in plugin/component_callback():' . $component);
07840     }
07841     $component = $cleancomponent;
07842 
07843     list($type, $name) = normalize_component($component);
07844     $component = $type . '_' . $name;
07845 
07846     $oldfunction = $name.'_'.$function;
07847     $function = $component.'_'.$function;
07848 
07849     $dir = get_component_directory($component);
07850     if (empty($dir)) {
07851         throw new coding_exception('Invalid component used in plugin/component_callback():' . $component);
07852     }
07853 
07854     // Load library and look for function
07855     if (file_exists($dir.'/lib.php')) {
07856         require_once($dir.'/lib.php');
07857     }
07858 
07859     if (!function_exists($function) and function_exists($oldfunction)) {
07860         if ($type !== 'mod' and $type !== 'core') {
07861             debugging("Please use new function name $function instead of legacy $oldfunction");
07862         }
07863         $function = $oldfunction;
07864     }
07865 
07866     if (function_exists($function)) {
07867         // Function exists, so just return function result
07868         $ret = call_user_func_array($function, $params);
07869         if (is_null($ret)) {
07870             return $default;
07871         } else {
07872             return $ret;
07873         }
07874     }
07875     return $default;
07876 }
07877 
07888 function plugin_supports($type, $name, $feature, $default = NULL) {
07889     global $CFG;
07890 
07891     if ($type === 'mod' and $name === 'NEWMODULE') {
07892         //somebody forgot to rename the module template
07893         return false;
07894     }
07895 
07896     $component = clean_param($type . '_' . $name, PARAM_COMPONENT);
07897     if (empty($component)) {
07898         throw new coding_exception('Invalid component used in plugin_supports():' . $type . '_' . $name);
07899     }
07900 
07901     $function = null;
07902 
07903     if ($type === 'mod') {
07904         // we need this special case because we support subplugins in modules,
07905         // otherwise it would end up in infinite loop
07906         if (file_exists("$CFG->dirroot/mod/$name/lib.php")) {
07907             include_once("$CFG->dirroot/mod/$name/lib.php");
07908             $function = $component.'_supports';
07909             if (!function_exists($function)) {
07910                 // legacy non-frankenstyle function name
07911                 $function = $name.'_supports';
07912             }
07913         } else {
07914             // invalid module
07915         }
07916 
07917     } else {
07918         if (!$path = get_plugin_directory($type, $name)) {
07919             // non existent plugin type
07920             return false;
07921         }
07922         if (file_exists("$path/lib.php")) {
07923             include_once("$path/lib.php");
07924             $function = $component.'_supports';
07925         }
07926     }
07927 
07928     if ($function and function_exists($function)) {
07929         $supports = $function($feature);
07930         if (is_null($supports)) {
07931             // plugin does not know - use default
07932             return $default;
07933         } else {
07934             return $supports;
07935         }
07936     }
07937 
07938     //plugin does not care, so use default
07939     return $default;
07940 }
07941 
07950 function check_php_version($version='5.2.4') {
07951     return (version_compare(phpversion(), $version) >= 0);
07952 }
07953 
07964  function check_browser_operating_system($brand) {
07965     if (empty($_SERVER['HTTP_USER_AGENT'])) {
07966         return false;
07967     }
07968 
07969     if (preg_match("/$brand/i", $_SERVER['HTTP_USER_AGENT'])) {
07970         return true;
07971     }
07972 
07973     return false;
07974  }
07975 
07985  function check_browser_version($brand, $version = null) {
07986     if (empty($_SERVER['HTTP_USER_AGENT'])) {
07987         return false;
07988     }
07989 
07990     $agent = $_SERVER['HTTP_USER_AGENT'];
07991 
07992     switch ($brand) {
07993 
07994       case 'Camino':   
07995           if (strpos($agent, 'Camino') === false) {
07996               return false;
07997           }
07998           if (empty($version)) {
07999               return true; // no version specified
08000           }
08001           if (preg_match("/Camino\/([0-9\.]+)/i", $agent, $match)) {
08002               if (version_compare($match[1], $version) >= 0) {
08003                   return true;
08004               }
08005           }
08006           break;
08007 
08008 
08009       case 'Firefox':   
08010           if (strpos($agent, 'Iceweasel') === false and strpos($agent, 'Firefox') === false) {
08011               return false;
08012           }
08013           if (empty($version)) {
08014               return true; // no version specified
08015           }
08016           if (preg_match("/(Iceweasel|Firefox)\/([0-9\.]+)/i", $agent, $match)) {
08017               if (version_compare($match[2], $version) >= 0) {
08018                   return true;
08019               }
08020           }
08021           break;
08022 
08023 
08024       case 'Gecko':   
08025           if (empty($version) and substr_count($agent, 'Camino')) {
08026               // MacOS X Camino support
08027               $version = 20041110;
08028           }
08029 
08030           // the proper string - Gecko/CCYYMMDD Vendor/Version
08031           // Faster version and work-a-round No IDN problem.
08032           if (preg_match("/Gecko\/([0-9]+)/i", $agent, $match)) {
08033               if ($match[1] > $version) {
08034                       return true;
08035                   }
08036               }
08037           break;
08038 
08039 
08040       case 'MSIE':   
08041           if (strpos($agent, 'Opera') !== false) {     // Reject Opera
08042               return false;
08043           }
08044           // in case of IE we have to deal with BC of the version parameter
08045           if (is_null($version)) {
08046               $version = 5.5; // anything older is not considered a browser at all!
08047           }
08048 
08049           //see: http://www.useragentstring.com/pages/Internet%20Explorer/
08050           if (preg_match("/MSIE ([0-9\.]+)/", $agent, $match)) {
08051               if (version_compare($match[1], $version) >= 0) {
08052                   return true;
08053               }
08054           }
08055           break;
08056 
08057 
08058       case 'Opera':  
08059           if (strpos($agent, 'Opera') === false) {
08060               return false;
08061           }
08062           if (empty($version)) {
08063               return true; // no version specified
08064           }
08065           if (preg_match("/Opera\/([0-9\.]+)/i", $agent, $match)) {
08066               if (version_compare($match[1], $version) >= 0) {
08067                   return true;
08068               }
08069           }
08070           break;
08071 
08072 
08073       case 'WebKit':  
08074           if (strpos($agent, 'AppleWebKit') === false) {
08075               return false;
08076           }
08077           if (empty($version)) {
08078               return true; // no version specified
08079           }
08080           if (preg_match("/AppleWebKit\/([0-9]+)/i", $agent, $match)) {
08081               if (version_compare($match[1], $version) >= 0) {
08082                   return true;
08083               }
08084           }
08085           break;
08086 
08087 
08088       case 'Safari':  
08089           if (strpos($agent, 'AppleWebKit') === false) {
08090               return false;
08091           }
08092           // Look for AppleWebKit, excluding strings with OmniWeb, Shiira and SymbianOS and any other mobile devices
08093           if (strpos($agent, 'OmniWeb')) { // Reject OmniWeb
08094               return false;
08095           }
08096           if (strpos($agent, 'Shiira')) { // Reject Shiira
08097               return false;
08098           }
08099           if (strpos($agent, 'SymbianOS')) { // Reject SymbianOS
08100               return false;
08101           }
08102           if (strpos($agent, 'Android')) { // Reject Androids too
08103               return false;
08104           }
08105           if (strpos($agent, 'iPhone') or strpos($agent, 'iPad') or strpos($agent, 'iPod')) {
08106               // No Apple mobile devices here - editor does not work, course ajax is not touch compatible, etc.
08107               return false;
08108           }
08109           if (strpos($agent, 'Chrome')) { // Reject chrome browsers - it needs to be tested explicitly
08110               return false;
08111           }
08112 
08113           if (empty($version)) {
08114               return true; // no version specified
08115           }
08116           if (preg_match("/AppleWebKit\/([0-9]+)/i", $agent, $match)) {
08117               if (version_compare($match[1], $version) >= 0) {
08118                   return true;
08119               }
08120           }
08121           break;
08122 
08123 
08124       case 'Chrome':
08125           if (strpos($agent, 'Chrome') === false) {
08126               return false;
08127           }
08128           if (empty($version)) {
08129               return true; // no version specified
08130           }
08131           if (preg_match("/Chrome\/(.*)[ ]+/i", $agent, $match)) {
08132               if (version_compare($match[1], $version) >= 0) {
08133                   return true;
08134               }
08135           }
08136           break;
08137 
08138 
08139       case 'Safari iOS':  
08140           if (strpos($agent, 'AppleWebKit') === false or strpos($agent, 'Safari') === false) {
08141               return false;
08142           }
08143           if (!strpos($agent, 'iPhone') and !strpos($agent, 'iPad') and !strpos($agent, 'iPod')) {
08144               return false;
08145           }
08146           if (empty($version)) {
08147               return true; // no version specified
08148           }
08149           if (preg_match("/AppleWebKit\/([0-9]+)/i", $agent, $match)) {
08150               if (version_compare($match[1], $version) >= 0) {
08151                   return true;
08152               }
08153           }
08154           break;
08155 
08156 
08157       case 'WebKit Android':  
08158           if (strpos($agent, 'Linux; U; Android') === false) {
08159               return false;
08160           }
08161           if (empty($version)) {
08162               return true; // no version specified
08163           }
08164           if (preg_match("/AppleWebKit\/([0-9]+)/i", $agent, $match)) {
08165               if (version_compare($match[1], $version) >= 0) {
08166                   return true;
08167               }
08168           }
08169           break;
08170 
08171     }
08172 
08173     return false;
08174 }
08175 
08183 function get_device_type() {
08184     global $CFG;
08185 
08186     if (empty($CFG->enabledevicedetection) || empty($_SERVER['HTTP_USER_AGENT'])) {
08187         return 'default';
08188     }
08189 
08190     $useragent = $_SERVER['HTTP_USER_AGENT'];
08191 
08192     if (!empty($CFG->devicedetectregex)) {
08193         $regexes = json_decode($CFG->devicedetectregex);
08194 
08195         foreach ($regexes as $value=>$regex) {
08196             if (preg_match($regex, $useragent)) {
08197                 return $value;
08198             }
08199         }
08200     }
08201 
08202     //mobile detection PHP direct copy from open source detectmobilebrowser.com
08203     $phonesregex = '/android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i';
08204     $modelsregex = '/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i';
08205     if (preg_match($phonesregex,$useragent) || preg_match($modelsregex,substr($useragent, 0, 4))){
08206         return 'mobile';
08207     }
08208 
08209     $tabletregex = '/Tablet browser|iPad|iProd|GT-P1000|GT-I9000|SHW-M180S|SGH-T849|SCH-I800|Build\/ERE27|sholest/i';
08210     if (preg_match($tabletregex, $useragent)) {
08211          return 'tablet';
08212     }
08213 
08214     // Safe way to check for IE6 and not get false positives for some IE 7/8 users
08215     if (substr($_SERVER['HTTP_USER_AGENT'], 0, 34) === 'Mozilla/4.0 (compatible; MSIE 6.0;') {
08216         return 'legacy';
08217     }
08218 
08219     return 'default';
08220 }
08221 
08228 function get_device_type_list($incusertypes = true) {
08229     global $CFG;
08230 
08231     $types = array('default', 'legacy', 'mobile', 'tablet');
08232 
08233     if ($incusertypes && !empty($CFG->devicedetectregex)) {
08234         $regexes = json_decode($CFG->devicedetectregex);
08235 
08236         foreach ($regexes as $value => $regex) {
08237             $types[] = $value;
08238         }
08239     }
08240 
08241     return $types;
08242 }
08243 
08250 function get_selected_theme_for_device_type($devicetype = null) {
08251     global $CFG;
08252 
08253     if (empty($devicetype)) {
08254         $devicetype = get_user_device_type();
08255     }
08256 
08257     $themevarname = get_device_cfg_var_name($devicetype);
08258     if (empty($CFG->$themevarname)) {
08259         return false;
08260     }
08261 
08262     return $CFG->$themevarname;
08263 }
08264 
08271 function get_device_cfg_var_name($devicetype = null) {
08272     if ($devicetype == 'default' || empty($devicetype)) {
08273         return 'theme';
08274     }
08275 
08276     return 'theme' . $devicetype;
08277 }
08278 
08286 function set_user_device_type($newdevice) {
08287     global $USER;
08288 
08289     $devicetype = get_device_type();
08290     $devicetypes = get_device_type_list();
08291 
08292     if ($newdevice == $devicetype) {
08293         unset_user_preference('switchdevice'.$devicetype);
08294     } else if (in_array($newdevice, $devicetypes)) {
08295         set_user_preference('switchdevice'.$devicetype, $newdevice);
08296     }
08297 }
08298 
08305 function get_user_device_type() {
08306     $device = get_device_type();
08307     $switched = get_user_preferences('switchdevice'.$device, false);
08308     if ($switched != false) {
08309         return $switched;
08310     }
08311     return $device;
08312 }
08313 
08320 function get_browser_version_classes() {
08321     $classes = array();
08322 
08323     if (check_browser_version("MSIE", "0")) {
08324         $classes[] = 'ie';
08325         if (check_browser_version("MSIE", 9)) {
08326             $classes[] = 'ie9';
08327         } else if (check_browser_version("MSIE", 8)) {
08328             $classes[] = 'ie8';
08329         } elseif (check_browser_version("MSIE", 7)) {
08330             $classes[] = 'ie7';
08331         } elseif (check_browser_version("MSIE", 6)) {
08332             $classes[] = 'ie6';
08333         }
08334 
08335     } else if (check_browser_version("Firefox") || check_browser_version("Gecko") || check_browser_version("Camino")) {
08336         $classes[] = 'gecko';
08337         if (preg_match('/rv\:([1-2])\.([0-9])/', $_SERVER['HTTP_USER_AGENT'], $matches)) {
08338             $classes[] = "gecko{$matches[1]}{$matches[2]}";
08339         }
08340 
08341     } else if (check_browser_version("WebKit")) {
08342         $classes[] = 'safari';
08343         if (check_browser_version("Safari iOS")) {
08344             $classes[] = 'ios';
08345 
08346         } else if (check_browser_version("WebKit Android")) {
08347             $classes[] = 'android';
08348         }
08349 
08350     } else if (check_browser_version("Opera")) {
08351         $classes[] = 'opera';
08352 
08353     }
08354 
08355     return $classes;
08356 }
08357 
08363 function can_use_rotated_text() {
08364     global $USER;
08365     return ajaxenabled(array('Firefox' => 2.0)) && !$USER->screenreader;;
08366 }
08367 
08373 function check_gd_version() {
08374     $gdversion = 0;
08375 
08376     if (function_exists('gd_info')){
08377         $gd_info = gd_info();
08378         if (substr_count($gd_info['GD Version'], '2.')) {
08379             $gdversion = 2;
08380         } else if (substr_count($gd_info['GD Version'], '1.')) {
08381             $gdversion = 1;
08382         }
08383 
08384     } else {
08385         ob_start();
08386         phpinfo(INFO_MODULES);
08387         $phpinfo = ob_get_contents();
08388         ob_end_clean();
08389 
08390         $phpinfo = explode("\n", $phpinfo);
08391 
08392 
08393         foreach ($phpinfo as $text) {
08394             $parts = explode('</td>', $text);
08395             foreach ($parts as $key => $val) {
08396                 $parts[$key] = trim(strip_tags($val));
08397             }
08398             if ($parts[0] == 'GD Version') {
08399                 if (substr_count($parts[1], '2.0')) {
08400                     $parts[1] = '2.0';
08401                 }
08402                 $gdversion = intval($parts[1]);
08403             }
08404         }
08405     }
08406 
08407     return $gdversion;   // 1, 2 or 0
08408 }
08409 
08420 function moodle_needs_upgrading() {
08421     global $CFG, $DB, $OUTPUT;
08422 
08423     if (empty($CFG->version)) {
08424         return true;
08425     }
08426 
08427     // main versio nfirst
08428     $version = null;
08429     include($CFG->dirroot.'/version.php');  // defines $version and upgrades
08430     if ($version > $CFG->version) {
08431         return true;
08432     }
08433 
08434     // modules
08435     $mods = get_plugin_list('mod');
08436     $installed = $DB->get_records('modules', array(), '', 'name, version');
08437     foreach ($mods as $mod => $fullmod) {
08438         if ($mod === 'NEWMODULE') {   // Someone has unzipped the template, ignore it
08439             continue;
08440         }
08441         $module = new stdClass();
08442         if (!is_readable($fullmod.'/version.php')) {
08443             continue;
08444         }
08445         include($fullmod.'/version.php');  // defines $module with version etc
08446         if (empty($installed[$mod])) {
08447             return true;
08448         } else if ($module->version > $installed[$mod]->version) {
08449             return true;
08450         }
08451     }
08452     unset($installed);
08453 
08454     // blocks
08455     $blocks = get_plugin_list('block');
08456     $installed = $DB->get_records('block', array(), '', 'name, version');
08457     require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
08458     foreach ($blocks as $blockname=>$fullblock) {
08459         if ($blockname === 'NEWBLOCK') {   // Someone has unzipped the template, ignore it
08460             continue;
08461         }
08462         if (!is_readable($fullblock.'/version.php')) {
08463             continue;
08464         }
08465         $plugin = new stdClass();
08466         $plugin->version = NULL;
08467         include($fullblock.'/version.php');
08468         if (empty($installed[$blockname])) {
08469             return true;
08470         } else if ($plugin->version > $installed[$blockname]->version) {
08471             return true;
08472         }
08473     }
08474     unset($installed);
08475 
08476     // now the rest of plugins
08477     $plugintypes = get_plugin_types();
08478     unset($plugintypes['mod']);
08479     unset($plugintypes['block']);
08480     foreach ($plugintypes as $type=>$unused) {
08481         $plugs = get_plugin_list($type);
08482         foreach ($plugs as $plug=>$fullplug) {
08483             $component = $type.'_'.$plug;
08484             if (!is_readable($fullplug.'/version.php')) {
08485                 continue;
08486             }
08487             $plugin = new stdClass();
08488             include($fullplug.'/version.php');  // defines $plugin with version etc
08489             $installedversion = get_config($component, 'version');
08490             if (empty($installedversion)) { // new installation
08491                 return true;
08492             } else if ($installedversion < $plugin->version) { // upgrade
08493                 return true;
08494             }
08495         }
08496     }
08497 
08498     return false;
08499 }
08500 
08510 function upgrade_set_timeout($max_execution_time=300) {
08511     global $CFG;
08512 
08513     if (!isset($CFG->upgraderunning) or $CFG->upgraderunning < time()) {
08514         $upgraderunning = get_config(null, 'upgraderunning');
08515     } else {
08516         $upgraderunning = $CFG->upgraderunning;
08517     }
08518 
08519     if (!$upgraderunning) {
08520         // upgrade not running or aborted
08521         print_error('upgradetimedout', 'admin', "$CFG->wwwroot/$CFG->admin/");
08522         die;
08523     }
08524 
08525     if ($max_execution_time < 60) {
08526         // protection against 0 here
08527         $max_execution_time = 60;
08528     }
08529 
08530     $expected_end = time() + $max_execution_time;
08531 
08532     if ($expected_end < $upgraderunning + 10 and $expected_end > $upgraderunning - 10) {
08533         // no need to store new end, it is nearly the same ;-)
08534         return;
08535     }
08536 
08537     set_time_limit($max_execution_time);
08538     set_config('upgraderunning', $expected_end); // keep upgrade locked until this time
08539 }
08540 
08542 
08551 function moodle_setlocale($locale='') {
08552     global $CFG;
08553 
08554     static $currentlocale = ''; // last locale caching
08555 
08556     $oldlocale = $currentlocale;
08557 
08559     if ($CFG->ostype == 'WINDOWS') {
08560         $stringtofetch = 'localewin';
08561     } else {
08562         $stringtofetch = 'locale';
08563     }
08564 
08566     if (!empty($locale)) {
08567         $currentlocale = $locale;
08568     } else if (!empty($CFG->locale)) { // override locale for all language packs
08569         $currentlocale = $CFG->locale;
08570     } else {
08571         $currentlocale = get_string($stringtofetch, 'langconfig');
08572     }
08573 
08575     if ($oldlocale == $currentlocale) {
08576         return;
08577     }
08578 
08582 
08584     $monetary= setlocale (LC_MONETARY, 0);
08585     $numeric = setlocale (LC_NUMERIC, 0);
08586     $ctype   = setlocale (LC_CTYPE, 0);
08587     if ($CFG->ostype != 'WINDOWS') {
08588         $messages= setlocale (LC_MESSAGES, 0);
08589     }
08591     setlocale (LC_ALL, $currentlocale);
08593     setlocale (LC_MONETARY, $monetary);
08594     setlocale (LC_NUMERIC, $numeric);
08595     if ($CFG->ostype != 'WINDOWS') {
08596         setlocale (LC_MESSAGES, $messages);
08597     }
08598     if ($currentlocale == 'tr_TR' or $currentlocale == 'tr_TR.UTF-8') { // To workaround a well-known PHP problem with Turkish letter Ii
08599         setlocale (LC_CTYPE, $ctype);
08600     }
08601 }
08602 
08613 function moodle_strtolower ($string, $encoding='') {
08614 
08615     //If not specified use utf8
08616     if (empty($encoding)) {
08617         $encoding = 'UTF-8';
08618     }
08619     //Use text services
08620     $textlib = textlib_get_instance();
08621 
08622     return $textlib->strtolower($string, $encoding);
08623 }
08624 
08633 function count_words($string) {
08634     $string = strip_tags($string);
08635     return count(preg_split("/\w\b/", $string)) - 1;
08636 }
08637 
08645 function count_letters($string) {
08647     $textlib = textlib_get_instance();
08648 
08649     $string = strip_tags($string); // Tags are out now
08650     $string = preg_replace('/[[:space:]]*/','',$string); //Whitespace are out now
08651 
08652     return $textlib->strlen($string);
08653 }
08654 
08661 function random_string ($length=15) {
08662     $pool  = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
08663     $pool .= 'abcdefghijklmnopqrstuvwxyz';
08664     $pool .= '0123456789';
08665     $poollen = strlen($pool);
08666     mt_srand ((double) microtime() * 1000000);
08667     $string = '';
08668     for ($i = 0; $i < $length; $i++) {
08669         $string .= substr($pool, (mt_rand()%($poollen)), 1);
08670     }
08671     return $string;
08672 }
08673 
08683 function complex_random_string($length=null) {
08684     $pool  = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
08685     $pool .= '`~!@#%^&*()_+-=[];,./<>?:{} ';
08686     $poollen = strlen($pool);
08687     mt_srand ((double) microtime() * 1000000);
08688     if ($length===null) {
08689         $length = floor(rand(24,32));
08690     }
08691     $string = '';
08692     for ($i = 0; $i < $length; $i++) {
08693         $string .= $pool[(mt_rand()%$poollen)];
08694     }
08695     return $string;
08696 }
08697 
08709 function shorten_text($text, $ideal=30, $exact = false, $ending='...') {
08710 
08711     global $CFG;
08712 
08713     // if the plain text is shorter than the maximum length, return the whole text
08714     if (strlen(preg_replace('/<.*?>/', '', $text)) <= $ideal) {
08715         return $text;
08716     }
08717 
08718     // Splits on HTML tags. Each open/close/empty tag will be the first thing
08719     // and only tag in its 'line'
08720     preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
08721 
08722     $total_length = strlen($ending);
08723     $truncate = '';
08724 
08725     // This array stores information about open and close tags and their position
08726     // in the truncated string. Each item in the array is an object with fields
08727     // ->open (true if open), ->tag (tag name in lower case), and ->pos
08728     // (byte position in truncated text)
08729     $tagdetails = array();
08730 
08731     foreach ($lines as $line_matchings) {
08732         // if there is any html-tag in this line, handle it and add it (uncounted) to the output
08733         if (!empty($line_matchings[1])) {
08734             // if it's an "empty element" with or without xhtml-conform closing slash (f.e. <br/>)
08735             if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1])) {
08736                     // do nothing
08737             // if tag is a closing tag (f.e. </b>)
08738             } else if (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) {
08739                 // record closing tag
08740                 $tagdetails[] = (object)array('open'=>false,
08741                     'tag'=>strtolower($tag_matchings[1]), 'pos'=>strlen($truncate));
08742             // if tag is an opening tag (f.e. <b>)
08743             } else if (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) {
08744                 // record opening tag
08745                 $tagdetails[] = (object)array('open'=>true,
08746                     'tag'=>strtolower($tag_matchings[1]), 'pos'=>strlen($truncate));
08747             }
08748             // add html-tag to $truncate'd text
08749             $truncate .= $line_matchings[1];
08750         }
08751 
08752         // calculate the length of the plain text part of the line; handle entities as one character
08753         $content_length = strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $line_matchings[2]));
08754         if ($total_length+$content_length > $ideal) {
08755             // the number of characters which are left
08756             $left = $ideal - $total_length;
08757             $entities_length = 0;
08758             // search for html entities
08759             if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $line_matchings[2], $entities, PREG_OFFSET_CAPTURE)) {
08760                 // calculate the real length of all entities in the legal range
08761                 foreach ($entities[0] as $entity) {
08762                     if ($entity[1]+1-$entities_length <= $left) {
08763                         $left--;
08764                         $entities_length += strlen($entity[0]);
08765                     } else {
08766                         // no more characters left
08767                         break;
08768                     }
08769                 }
08770             }
08771             $truncate .= substr($line_matchings[2], 0, $left+$entities_length);
08772             // maximum length is reached, so get off the loop
08773             break;
08774         } else {
08775             $truncate .= $line_matchings[2];
08776             $total_length += $content_length;
08777         }
08778 
08779         // if the maximum length is reached, get off the loop
08780         if($total_length >= $ideal) {
08781             break;
08782         }
08783     }
08784 
08785     // if the words shouldn't be cut in the middle...
08786     if (!$exact) {
08787         // ...search the last occurence of a space...
08788         for ($k=strlen($truncate);$k>0;$k--) {
08789             if (!empty($truncate[$k]) && ($char = $truncate[$k])) {
08790                 if ($char == '.' or $char == ' ') {
08791                     $breakpos = $k+1;
08792                     break;
08793                 } else if (ord($char) >= 0xE0) {  // Chinese/Japanese/Korean text
08794                     $breakpos = $k;               // can be truncated at any UTF-8
08795                     break;                        // character boundary.
08796                 }
08797             }
08798         }
08799 
08800         if (isset($breakpos)) {
08801             // ...and cut the text in this position
08802             $truncate = substr($truncate, 0, $breakpos);
08803         }
08804     }
08805 
08806     // add the defined ending to the text
08807     $truncate .= $ending;
08808 
08809     // Now calculate the list of open html tags based on the truncate position
08810     $open_tags = array();
08811     foreach ($tagdetails as $taginfo) {
08812         if(isset($breakpos) && $taginfo->pos >= $breakpos) {
08813             // Don't include tags after we made the break!
08814             break;
08815         }
08816         if($taginfo->open) {
08817             // add tag to the beginning of $open_tags list
08818             array_unshift($open_tags, $taginfo->tag);
08819         } else {
08820             $pos = array_search($taginfo->tag, array_reverse($open_tags, true)); // can have multiple exact same open tags, close the last one
08821             if ($pos !== false) {
08822                 unset($open_tags[$pos]);
08823             }
08824         }
08825     }
08826 
08827     // close all unclosed html-tags
08828     foreach ($open_tags as $tag) {
08829         $truncate .= '</' . $tag . '>';
08830     }
08831 
08832     return $truncate;
08833 }
08834 
08835 
08847 function getweek ($startdate, $thedate) {
08848     if ($thedate < $startdate) {   // error
08849         return 0;
08850     }
08851 
08852     return floor(($thedate - $startdate) / WEEKSECS) + 1;
08853 }
08854 
08865 function generate_password($maxlen=10) {
08866     global $CFG;
08867 
08868     if (empty($CFG->passwordpolicy)) {
08869         $fillers = PASSWORD_DIGITS;
08870         $wordlist = file($CFG->wordlist);
08871         $word1 = trim($wordlist[rand(0, count($wordlist) - 1)]);
08872         $word2 = trim($wordlist[rand(0, count($wordlist) - 1)]);
08873         $filler1 = $fillers[rand(0, strlen($fillers) - 1)];
08874         $password = $word1 . $filler1 . $word2;
08875     } else {
08876         $minlen = !empty($CFG->minpasswordlength) ? $CFG->minpasswordlength : 0;
08877         $digits = $CFG->minpassworddigits;
08878         $lower = $CFG->minpasswordlower;
08879         $upper = $CFG->minpasswordupper;
08880         $nonalphanum = $CFG->minpasswordnonalphanum;
08881         $total = $lower + $upper + $digits + $nonalphanum;
08882         // minlength should be the greater one of the two ( $minlen and $total )
08883         $minlen = $minlen < $total ? $total : $minlen;
08884         // maxlen can never be smaller than minlen
08885         $maxlen = $minlen > $maxlen ? $minlen : $maxlen;
08886         $additional = $maxlen - $total;
08887 
08888         // Make sure we have enough characters to fulfill
08889         // complexity requirements
08890         $passworddigits = PASSWORD_DIGITS;
08891         while ($digits > strlen($passworddigits)) {
08892             $passworddigits .= PASSWORD_DIGITS;
08893         }
08894         $passwordlower = PASSWORD_LOWER;
08895         while ($lower > strlen($passwordlower)) {
08896             $passwordlower .= PASSWORD_LOWER;
08897         }
08898         $passwordupper = PASSWORD_UPPER;
08899         while ($upper > strlen($passwordupper)) {
08900             $passwordupper .= PASSWORD_UPPER;
08901         }
08902         $passwordnonalphanum = PASSWORD_NONALPHANUM;
08903         while ($nonalphanum > strlen($passwordnonalphanum)) {
08904             $passwordnonalphanum .= PASSWORD_NONALPHANUM;
08905         }
08906 
08907         // Now mix and shuffle it all
08908         $password = str_shuffle (substr(str_shuffle ($passwordlower), 0, $lower) .
08909                                  substr(str_shuffle ($passwordupper), 0, $upper) .
08910                                  substr(str_shuffle ($passworddigits), 0, $digits) .
08911                                  substr(str_shuffle ($passwordnonalphanum), 0 , $nonalphanum) .
08912                                  substr(str_shuffle ($passwordlower .
08913                                                      $passwordupper .
08914                                                      $passworddigits .
08915                                                      $passwordnonalphanum), 0 , $additional));
08916     }
08917 
08918     return substr ($password, 0, $maxlen);
08919 }
08920 
08930 function format_float($float, $decimalpoints=1, $localized=true) {
08931     if (is_null($float)) {
08932         return '';
08933     }
08934     if ($localized) {
08935         return number_format($float, $decimalpoints, get_string('decsep', 'langconfig'), '');
08936     } else {
08937         return number_format($float, $decimalpoints, '.', '');
08938     }
08939 }
08940 
08948 function unformat_float($locale_float) {
08949     $locale_float = trim($locale_float);
08950 
08951     if ($locale_float == '') {
08952         return null;
08953     }
08954 
08955     $locale_float = str_replace(' ', '', $locale_float); // no spaces - those might be used as thousand separators
08956 
08957     return (float)str_replace(get_string('decsep', 'langconfig'), '.', $locale_float);
08958 }
08959 
08967 function swapshuffle($array) {
08968 
08969     srand ((double) microtime() * 10000000);
08970     $last = count($array) - 1;
08971     for ($i=0;$i<=$last;$i++) {
08972         $from = rand(0,$last);
08973         $curr = $array[$i];
08974         $array[$i] = $array[$from];
08975         $array[$from] = $curr;
08976     }
08977     return $array;
08978 }
08979 
08986 function swapshuffle_assoc($array) {
08987 
08988     $newarray = array();
08989     $newkeys = swapshuffle(array_keys($array));
08990 
08991     foreach ($newkeys as $newkey) {
08992         $newarray[$newkey] = $array[$newkey];
08993     }
08994     return $newarray;
08995 }
08996 
09008 function draw_rand_array($array, $draws) {
09009     srand ((double) microtime() * 10000000);
09010 
09011     $return = array();
09012 
09013     $last = count($array);
09014 
09015     if ($draws > $last) {
09016         $draws = $last;
09017     }
09018 
09019     while ($draws > 0) {
09020         $last--;
09021 
09022         $keys = array_keys($array);
09023         $rand = rand(0, $last);
09024 
09025         $return[$keys[$rand]] = $array[$keys[$rand]];
09026         unset($array[$keys[$rand]]);
09027 
09028         $draws--;
09029     }
09030 
09031     return $return;
09032 }
09033 
09041 function microtime_diff($a, $b) {
09042     list($a_dec, $a_sec) = explode(' ', $a);
09043     list($b_dec, $b_sec) = explode(' ', $b);
09044     return $b_sec - $a_sec + $b_dec - $a_dec;
09045 }
09046 
09055 function make_menu_from_list($list, $separator=',') {
09056 
09057     $array = array_reverse(explode($separator, $list), true);
09058     foreach ($array as $key => $item) {
09059         $outarray[$key+1] = trim($item);
09060     }
09061     return $outarray;
09062 }
09063 
09077 function make_grades_menu($gradingtype) {
09078     global $DB;
09079 
09080     $grades = array();
09081     if ($gradingtype < 0) {
09082         if ($scale = $DB->get_record('scale', array('id'=> (-$gradingtype)))) {
09083             return make_menu_from_list($scale->scale);
09084         }
09085     } else if ($gradingtype > 0) {
09086         for ($i=$gradingtype; $i>=0; $i--) {
09087             $grades[$i] = $i .' / '. $gradingtype;
09088         }
09089         return $grades;
09090     }
09091     return $grades;
09092 }
09093 
09106 function course_scale_used($courseid, $scaleid) {
09107     global $CFG, $DB;
09108 
09109     $return = 0;
09110 
09111     if (!empty($scaleid)) {
09112         if ($cms = get_course_mods($courseid)) {
09113             foreach ($cms as $cm) {
09114                 //Check cm->name/lib.php exists
09115                 if (file_exists($CFG->dirroot.'/mod/'.$cm->modname.'/lib.php')) {
09116                     include_once($CFG->dirroot.'/mod/'.$cm->modname.'/lib.php');
09117                     $function_name = $cm->modname.'_scale_used';
09118                     if (function_exists($function_name)) {
09119                         if ($function_name($cm->instance,$scaleid)) {
09120                             $return++;
09121                         }
09122                     }
09123                 }
09124             }
09125         }
09126 
09127         // check if any course grade item makes use of the scale
09128         $return += $DB->count_records('grade_items', array('courseid'=>$courseid, 'scaleid'=>$scaleid));
09129 
09130         // check if any outcome in the course makes use of the scale
09131         $return += $DB->count_records_sql("SELECT COUNT('x')
09132                                              FROM {grade_outcomes_courses} goc,
09133                                                   {grade_outcomes} go
09134                                             WHERE go.id = goc.outcomeid
09135                                                   AND go.scaleid = ? AND goc.courseid = ?",
09136                                           array($scaleid, $courseid));
09137     }
09138     return $return;
09139 }
09140 
09149 function site_scale_used($scaleid, &$courses) {
09150     $return = 0;
09151 
09152     if (!is_array($courses) || count($courses) == 0) {
09153         $courses = get_courses("all",false,"c.id,c.shortname");
09154     }
09155 
09156     if (!empty($scaleid)) {
09157         if (is_array($courses) && count($courses) > 0) {
09158             foreach ($courses as $course) {
09159                 $return += course_scale_used($course->id,$scaleid);
09160             }
09161         }
09162     }
09163     return $return;
09164 }
09165 
09175 function make_unique_id_code($extra='') {
09176 
09177     $hostname = 'unknownhost';
09178     if (!empty($_SERVER['HTTP_HOST'])) {
09179         $hostname = $_SERVER['HTTP_HOST'];
09180     } else if (!empty($_ENV['HTTP_HOST'])) {
09181         $hostname = $_ENV['HTTP_HOST'];
09182     } else if (!empty($_SERVER['SERVER_NAME'])) {
09183         $hostname = $_SERVER['SERVER_NAME'];
09184     } else if (!empty($_ENV['SERVER_NAME'])) {
09185         $hostname = $_ENV['SERVER_NAME'];
09186     }
09187 
09188     $date = gmdate("ymdHis");
09189 
09190     $random =  random_string(6);
09191 
09192     if ($extra) {
09193         return $hostname .'+'. $date .'+'. $random .'+'. $extra;
09194     } else {
09195         return $hostname .'+'. $date .'+'. $random;
09196     }
09197 }
09198 
09199 
09215 function address_in_subnet($addr, $subnetstr) {
09216 
09217     if ($addr == '0.0.0.0') {
09218         return false;
09219     }
09220     $subnets = explode(',', $subnetstr);
09221     $found = false;
09222     $addr = trim($addr);
09223     $addr = cleanremoteaddr($addr, false); // normalise
09224     if ($addr === null) {
09225         return false;
09226     }
09227     $addrparts = explode(':', $addr);
09228 
09229     $ipv6 = strpos($addr, ':');
09230 
09231     foreach ($subnets as $subnet) {
09232         $subnet = trim($subnet);
09233         if ($subnet === '') {
09234             continue;
09235         }
09236 
09237         if (strpos($subnet, '/') !== false) {
09239             list($ip, $mask) = explode('/', $subnet);
09240             $mask = trim($mask);
09241             if (!is_number($mask)) {
09242                 continue; // incorect mask number, eh?
09243             }
09244             $ip = cleanremoteaddr($ip, false); // normalise
09245             if ($ip === null) {
09246                 continue;
09247             }
09248             if (strpos($ip, ':') !== false) {
09249                 // IPv6
09250                 if (!$ipv6) {
09251                     continue;
09252                 }
09253                 if ($mask > 128 or $mask < 0) {
09254                     continue; // nonsense
09255                 }
09256                 if ($mask == 0) {
09257                     return true; // any address
09258                 }
09259                 if ($mask == 128) {
09260                     if ($ip === $addr) {
09261                         return true;
09262                     }
09263                     continue;
09264                 }
09265                 $ipparts = explode(':', $ip);
09266                 $modulo  = $mask % 16;
09267                 $ipnet   = array_slice($ipparts, 0, ($mask-$modulo)/16);
09268                 $addrnet = array_slice($addrparts, 0, ($mask-$modulo)/16);
09269                 if (implode(':', $ipnet) === implode(':', $addrnet)) {
09270                     if ($modulo == 0) {
09271                         return true;
09272                     }
09273                     $pos     = ($mask-$modulo)/16;
09274                     $ipnet   = hexdec($ipparts[$pos]);
09275                     $addrnet = hexdec($addrparts[$pos]);
09276                     $mask    = 0xffff << (16 - $modulo);
09277                     if (($addrnet & $mask) == ($ipnet & $mask)) {
09278                         return true;
09279                     }
09280                 }
09281 
09282             } else {
09283                 // IPv4
09284                 if ($ipv6) {
09285                     continue;
09286                 }
09287                 if ($mask > 32 or $mask < 0) {
09288                     continue; // nonsense
09289                 }
09290                 if ($mask == 0) {
09291                     return true;
09292                 }
09293                 if ($mask == 32) {
09294                     if ($ip === $addr) {
09295                         return true;
09296                     }
09297                     continue;
09298                 }
09299                 $mask = 0xffffffff << (32 - $mask);
09300                 if (((ip2long($addr) & $mask) == (ip2long($ip) & $mask))) {
09301                     return true;
09302                 }
09303             }
09304 
09305         } else if (strpos($subnet, '-') !== false)  {
09307             $parts = explode('-', $subnet);
09308             if (count($parts) != 2) {
09309                 continue;
09310             }
09311 
09312             if (strpos($subnet, ':') !== false) {
09313                 // IPv6
09314                 if (!$ipv6) {
09315                     continue;
09316                 }
09317                 $ipstart = cleanremoteaddr(trim($parts[0]), false); // normalise
09318                 if ($ipstart === null) {
09319                     continue;
09320                 }
09321                 $ipparts = explode(':', $ipstart);
09322                 $start = hexdec(array_pop($ipparts));
09323                 $ipparts[] = trim($parts[1]);
09324                 $ipend = cleanremoteaddr(implode(':', $ipparts), false); // normalise
09325                 if ($ipend === null) {
09326                     continue;
09327                 }
09328                 $ipparts[7] = '';
09329                 $ipnet = implode(':', $ipparts);
09330                 if (strpos($addr, $ipnet) !== 0) {
09331                     continue;
09332                 }
09333                 $ipparts = explode(':', $ipend);
09334                 $end = hexdec($ipparts[7]);
09335 
09336                 $addrend = hexdec($addrparts[7]);
09337 
09338                 if (($addrend >= $start) and ($addrend <= $end)) {
09339                     return true;
09340                 }
09341 
09342             } else {
09343                 // IPv4
09344                 if ($ipv6) {
09345                     continue;
09346                 }
09347                 $ipstart = cleanremoteaddr(trim($parts[0]), false); // normalise
09348                 if ($ipstart === null) {
09349                     continue;
09350                 }
09351                 $ipparts = explode('.', $ipstart);
09352                 $ipparts[3] = trim($parts[1]);
09353                 $ipend = cleanremoteaddr(implode('.', $ipparts), false); // normalise
09354                 if ($ipend === null) {
09355                     continue;
09356                 }
09357 
09358                 if ((ip2long($addr) >= ip2long($ipstart)) and (ip2long($addr) <= ip2long($ipend))) {
09359                     return true;
09360                 }
09361             }
09362 
09363         } else {
09365             if (strpos($subnet, ':') !== false) {
09366                 // IPv6
09367                 if (!$ipv6) {
09368                     continue;
09369                 }
09370                 $parts = explode(':', $subnet);
09371                 $count = count($parts);
09372                 if ($parts[$count-1] === '') {
09373                     unset($parts[$count-1]); // trim trailing :
09374                     $count--;
09375                     $subnet = implode('.', $parts);
09376                 }
09377                 $isip = cleanremoteaddr($subnet, false); // normalise
09378                 if ($isip !== null) {
09379                     if ($isip === $addr) {
09380                         return true;
09381                     }
09382                     continue;
09383                 } else if ($count > 8) {
09384                     continue;
09385                 }
09386                 $zeros = array_fill(0, 8-$count, '0');
09387                 $subnet = $subnet.':'.implode(':', $zeros).'/'.($count*16);
09388                 if (address_in_subnet($addr, $subnet)) {
09389                     return true;
09390                 }
09391 
09392             } else {
09393                 // IPv4
09394                 if ($ipv6) {
09395                     continue;
09396                 }
09397                 $parts = explode('.', $subnet);
09398                 $count = count($parts);
09399                 if ($parts[$count-1] === '') {
09400                     unset($parts[$count-1]); // trim trailing .
09401                     $count--;
09402                     $subnet = implode('.', $parts);
09403                 }
09404                 if ($count == 4) {
09405                     $subnet = cleanremoteaddr($subnet, false); // normalise
09406                     if ($subnet === $addr) {
09407                         return true;
09408                     }
09409                     continue;
09410                 } else if ($count > 4) {
09411                     continue;
09412                 }
09413                 $zeros = array_fill(0, 4-$count, '0');
09414                 $subnet = $subnet.'.'.implode('.', $zeros).'/'.($count*8);
09415                 if (address_in_subnet($addr, $subnet)) {
09416                     return true;
09417                 }
09418             }
09419         }
09420     }
09421 
09422     return false;
09423 }
09424 
09434 function mtrace($string, $eol="\n", $sleep=0) {
09435 
09436     if (defined('STDOUT')) {
09437         fwrite(STDOUT, $string.$eol);
09438     } else {
09439         echo $string . $eol;
09440     }
09441 
09442     flush();
09443 
09444     //delay to keep message on user's screen in case of subsequent redirect
09445     if ($sleep) {
09446         sleep($sleep);
09447     }
09448 }
09449 
09456 function cleardoubleslashes ($path) {
09457     return preg_replace('/(\/|\\\){1,}/','/',$path);
09458 }
09459 
09466 function remoteip_in_list($list){
09467     $inlist = false;
09468     $client_ip = getremoteaddr(null);
09469 
09470     if(!$client_ip){
09471         // ensure access on cli
09472         return true;
09473     }
09474 
09475     $list = explode("\n", $list);
09476     foreach($list as $subnet) {
09477         $subnet = trim($subnet);
09478         if (address_in_subnet($client_ip, $subnet)) {
09479             $inlist = true;
09480             break;
09481         }
09482     }
09483     return $inlist;
09484 }
09485 
09493 function getremoteaddr($default='0.0.0.0') {
09494     global $CFG;
09495 
09496     if (empty($CFG->getremoteaddrconf)) {
09497         // This will happen, for example, before just after the upgrade, as the
09498         // user is redirected to the admin screen.
09499         $variablestoskip = 0;
09500     } else {
09501         $variablestoskip = $CFG->getremoteaddrconf;
09502     }
09503     if (!($variablestoskip & GETREMOTEADDR_SKIP_HTTP_CLIENT_IP)) {
09504         if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
09505             $address = cleanremoteaddr($_SERVER['HTTP_CLIENT_IP']);
09506             return $address ? $address : $default;
09507         }
09508     }
09509     if (!($variablestoskip & GETREMOTEADDR_SKIP_HTTP_X_FORWARDED_FOR)) {
09510         if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
09511             $address = cleanremoteaddr($_SERVER['HTTP_X_FORWARDED_FOR']);
09512             return $address ? $address : $default;
09513         }
09514     }
09515     if (!empty($_SERVER['REMOTE_ADDR'])) {
09516         $address = cleanremoteaddr($_SERVER['REMOTE_ADDR']);
09517         return $address ? $address : $default;
09518     } else {
09519         return $default;
09520     }
09521 }
09522 
09531 function cleanremoteaddr($addr, $compress=false) {
09532     $addr = trim($addr);
09533 
09534     //TODO: maybe add a separate function is_addr_public() or something like this
09535 
09536     if (strpos($addr, ':') !== false) {
09537         // can be only IPv6
09538         $parts = explode(':', $addr);
09539         $count = count($parts);
09540 
09541         if (strpos($parts[$count-1], '.') !== false) {
09542             //legacy ipv4 notation
09543             $last = array_pop($parts);
09544             $ipv4 = cleanremoteaddr($last, true);
09545             if ($ipv4 === null) {
09546                 return null;
09547             }
09548             $bits = explode('.', $ipv4);
09549             $parts[] = dechex($bits[0]).dechex($bits[1]);
09550             $parts[] = dechex($bits[2]).dechex($bits[3]);
09551             $count = count($parts);
09552             $addr = implode(':', $parts);
09553         }
09554 
09555         if ($count < 3 or $count > 8) {
09556             return null; // severly malformed
09557         }
09558 
09559         if ($count != 8) {
09560             if (strpos($addr, '::') === false) {
09561                 return null; // malformed
09562             }
09563             // uncompress ::
09564             $insertat = array_search('', $parts, true);
09565             $missing = array_fill(0, 1 + 8 - $count, '0');
09566             array_splice($parts, $insertat, 1, $missing);
09567             foreach ($parts as $key=>$part) {
09568                 if ($part === '') {
09569                     $parts[$key] = '0';
09570                 }
09571             }
09572         }
09573 
09574         $adr = implode(':', $parts);
09575         if (!preg_match('/^([0-9a-f]{1,4})(:[0-9a-f]{1,4})*$/i', $adr)) {
09576             return null; // incorrect format - sorry
09577         }
09578 
09579         // normalise 0s and case
09580         $parts = array_map('hexdec', $parts);
09581         $parts = array_map('dechex', $parts);
09582 
09583         $result = implode(':', $parts);
09584 
09585         if (!$compress) {
09586             return $result;
09587         }
09588 
09589         if ($result === '0:0:0:0:0:0:0:0') {
09590             return '::'; // all addresses
09591         }
09592 
09593         $compressed = preg_replace('/(:0)+:0$/', '::', $result, 1);
09594         if ($compressed !== $result) {
09595             return $compressed;
09596         }
09597 
09598         $compressed = preg_replace('/^(0:){2,7}/', '::', $result, 1);
09599         if ($compressed !== $result) {
09600             return $compressed;
09601         }
09602 
09603         $compressed = preg_replace('/(:0){2,6}:/', '::', $result, 1);
09604         if ($compressed !== $result) {
09605             return $compressed;
09606         }
09607 
09608         return $result;
09609     }
09610 
09611     // first get all things that look like IPv4 addresses
09612     $parts = array();
09613     if (!preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $addr, $parts)) {
09614         return null;
09615     }
09616     unset($parts[0]);
09617 
09618     foreach ($parts as $key=>$match) {
09619         if ($match > 255) {
09620             return null;
09621         }
09622         $parts[$key] = (int)$match; // normalise 0s
09623     }
09624 
09625     return implode('.', $parts);
09626 }
09627 
09635 function fullclone($thing) {
09636     return unserialize(serialize($thing));
09637 }
09638 
09639 
09647 function moodle_request_shutdown() {
09648     global $CFG;
09649 
09650     // help apache server if possible
09651     $apachereleasemem = false;
09652     if (function_exists('apache_child_terminate') && function_exists('memory_get_usage')
09653             && ini_get_bool('child_terminate')) {
09654 
09655         $limit = (empty($CFG->apachemaxmem) ? 64*1024*1024 : $CFG->apachemaxmem); //64MB default
09656         if (memory_get_usage() > get_real_size($limit)) {
09657             $apachereleasemem = $limit;
09658             @apache_child_terminate();
09659         }
09660     }
09661 
09662     // deal with perf logging
09663     if (defined('MDL_PERF') || (!empty($CFG->perfdebug) and $CFG->perfdebug > 7)) {
09664         if ($apachereleasemem) {
09665             error_log('Mem usage over '.$apachereleasemem.': marking Apache child for reaping.');
09666         }
09667         if (defined('MDL_PERFTOLOG')) {
09668             $perf = get_performance_info();
09669             error_log("PERF: " . $perf['txt']);
09670         }
09671         if (defined('MDL_PERFINC')) {
09672             $inc = get_included_files();
09673             $ts  = 0;
09674             foreach($inc as $f) {
09675                 if (preg_match(':^/:', $f)) {
09676                     $fs  =  filesize($f);
09677                     $ts  += $fs;
09678                     $hfs =  display_size($fs);
09679                     error_log(substr($f,strlen($CFG->dirroot)) . " size: $fs ($hfs)"
09680                               , NULL, NULL, 0);
09681                 } else {
09682                     error_log($f , NULL, NULL, 0);
09683                 }
09684             }
09685             if ($ts > 0 ) {
09686                 $hts = display_size($ts);
09687                 error_log("Total size of files included: $ts ($hts)");
09688             }
09689         }
09690     }
09691 }
09692 
09700 function message_popup_window() {
09701     global $USER, $DB, $PAGE, $CFG, $SITE;
09702 
09703     if (!$PAGE->get_popup_notification_allowed() || empty($CFG->messaging)) {
09704         return;
09705     }
09706 
09707     if (!isloggedin() || isguestuser()) {
09708         return;
09709     }
09710 
09711     if (!isset($USER->message_lastpopup)) {
09712         $USER->message_lastpopup = 0;
09713     } else if ($USER->message_lastpopup > (time()-120)) {
09714         //dont run the query to check whether to display a popup if its been run in the last 2 minutes
09715         return;
09716     }
09717 
09718     //a quick query to check whether the user has new messages
09719     $messagecount = $DB->count_records('message', array('useridto' => $USER->id));
09720     if ($messagecount<1) {
09721         return;
09722     }
09723 
09724     //got unread messages so now do another query that joins with the user table
09725     $messagesql = "SELECT m.id, m.smallmessage, m.fullmessageformat, m.notification, u.firstname, u.lastname
09726                      FROM {message} m
09727                      JOIN {message_working} mw ON m.id=mw.unreadmessageid
09728                      JOIN {message_processors} p ON mw.processorid=p.id
09729                      JOIN {user} u ON m.useridfrom=u.id
09730                     WHERE m.useridto = :userid
09731                       AND p.name='popup'";
09732 
09733     //if the user was last notified over an hour ago we can renotify them of old messages
09734     //so don't worry about when the new message was sent
09735     $lastnotifiedlongago = $USER->message_lastpopup < (time()-3600);
09736     if (!$lastnotifiedlongago) {
09737         $messagesql .= 'AND m.timecreated > :lastpopuptime';
09738     }
09739 
09740     $message_users = $DB->get_records_sql($messagesql, array('userid'=>$USER->id, 'lastpopuptime'=>$USER->message_lastpopup));
09741 
09742     //if we have new messages to notify the user about
09743     if (!empty($message_users)) {
09744 
09745         $strmessages = '';
09746         if (count($message_users)>1) {
09747             $strmessages = get_string('unreadnewmessages', 'message', count($message_users));
09748         } else {
09749             $message_users = reset($message_users);
09750 
09751             //show who the message is from if its not a notification
09752             if (!$message_users->notification) {
09753                 $strmessages = get_string('unreadnewmessage', 'message', fullname($message_users) );
09754             }
09755 
09756             //try to display the small version of the message
09757             $smallmessage = null;
09758             if (!empty($message_users->smallmessage)) {
09759                 //display the first 200 chars of the message in the popup
09760                 $textlib = textlib_get_instance();
09761                 $smallmessage = null;
09762                 if ($textlib->strlen($message_users->smallmessage) > 200) {
09763                     $smallmessage = $textlib->substr($message_users->smallmessage,0,200).'...';
09764                 } else {
09765                     $smallmessage = $message_users->smallmessage;
09766                 }
09767 
09768                 //prevent html symbols being displayed
09769                 if ($message_users->fullmessageformat == FORMAT_HTML) {
09770                     $smallmessage = html_to_text($smallmessage);
09771                 } else {
09772                     $smallmessage = s($smallmessage);
09773                 }
09774             } else if ($message_users->notification) {
09775                 //its a notification with no smallmessage so just say they have a notification
09776                 $smallmessage = get_string('unreadnewnotification', 'message');
09777             }
09778             if (!empty($smallmessage)) {
09779                 $strmessages .= '<div id="usermessage">'.s($smallmessage).'</div>';
09780             }
09781         }
09782 
09783         $strgomessage = get_string('gotomessages', 'message');
09784         $strstaymessage = get_string('ignore','admin');
09785 
09786         $url = $CFG->wwwroot.'/message/index.php';
09787         $content =  html_writer::start_tag('div', array('id'=>'newmessageoverlay','class'=>'mdl-align')).
09788                         html_writer::start_tag('div', array('id'=>'newmessagetext')).
09789                             $strmessages.
09790                         html_writer::end_tag('div').
09791 
09792                         html_writer::start_tag('div', array('id'=>'newmessagelinks')).
09793                             html_writer::link($url, $strgomessage, array('id'=>'notificationyes')).'&nbsp;&nbsp;&nbsp;'.
09794                             html_writer::link('', $strstaymessage, array('id'=>'notificationno')).
09795                         html_writer::end_tag('div');
09796                     html_writer::end_tag('div');
09797 
09798         $PAGE->requires->js_init_call('M.core_message.init_notification', array('', $content, $url));
09799 
09800         $USER->message_lastpopup = time();
09801     }
09802 }
09803 
09813 function bounded_number($min, $value, $max) {
09814     if($value < $min) {
09815         return $min;
09816     }
09817     if($value > $max) {
09818         return $max;
09819     }
09820     return $value;
09821 }
09822 
09829 function array_is_nested($array) {
09830     foreach ($array as $value) {
09831         if (is_array($value)) {
09832             return true;
09833         }
09834     }
09835     return false;
09836 }
09837 
09849 function get_performance_info() {
09850     global $CFG, $PERF, $DB, $PAGE;
09851 
09852     $info = array();
09853     $info['html'] = '';         // holds userfriendly HTML representation
09854     $info['txt']  = me() . ' '; // holds log-friendly representation
09855 
09856     $info['realtime'] = microtime_diff($PERF->starttime, microtime());
09857 
09858     $info['html'] .= '<span class="timeused">'.$info['realtime'].' secs</span> ';
09859     $info['txt'] .= 'time: '.$info['realtime'].'s ';
09860 
09861     if (function_exists('memory_get_usage')) {
09862         $info['memory_total'] = memory_get_usage();
09863         $info['memory_growth'] = memory_get_usage() - $PERF->startmemory;
09864         $info['html'] .= '<span class="memoryused">RAM: '.display_size($info['memory_total']).'</span> ';
09865         $info['txt']  .= 'memory_total: '.$info['memory_total'].'B (' . display_size($info['memory_total']).') memory_growth: '.$info['memory_growth'].'B ('.display_size($info['memory_growth']).') ';
09866     }
09867 
09868     if (function_exists('memory_get_peak_usage')) {
09869         $info['memory_peak'] = memory_get_peak_usage();
09870         $info['html'] .= '<span class="memoryused">RAM peak: '.display_size($info['memory_peak']).'</span> ';
09871         $info['txt']  .= 'memory_peak: '.$info['memory_peak'].'B (' . display_size($info['memory_peak']).') ';
09872     }
09873 
09874     $inc = get_included_files();
09875     //error_log(print_r($inc,1));
09876     $info['includecount'] = count($inc);
09877     $info['html'] .= '<span class="included">Included '.$info['includecount'].' files</span> ';
09878     $info['txt']  .= 'includecount: '.$info['includecount'].' ';
09879 
09880     $filtermanager = filter_manager::instance();
09881     if (method_exists($filtermanager, 'get_performance_summary')) {
09882         list($filterinfo, $nicenames) = $filtermanager->get_performance_summary();
09883         $info = array_merge($filterinfo, $info);
09884         foreach ($filterinfo as $key => $value) {
09885             $info['html'] .= "<span class='$key'>$nicenames[$key]: $value </span> ";
09886             $info['txt'] .= "$key: $value ";
09887         }
09888     }
09889 
09890     $stringmanager = get_string_manager();
09891     if (method_exists($stringmanager, 'get_performance_summary')) {
09892         list($filterinfo, $nicenames) = $stringmanager->get_performance_summary();
09893         $info = array_merge($filterinfo, $info);
09894         foreach ($filterinfo as $key => $value) {
09895             $info['html'] .= "<span class='$key'>$nicenames[$key]: $value </span> ";
09896             $info['txt'] .= "$key: $value ";
09897         }
09898     }
09899 
09900      $jsmodules = $PAGE->requires->get_loaded_modules();
09901      if ($jsmodules) {
09902          $yuicount = 0;
09903          $othercount = 0;
09904          $details = '';
09905          foreach ($jsmodules as $module => $backtraces) {
09906              if (strpos($module, 'yui') === 0) {
09907                  $yuicount += 1;
09908              } else {
09909                  $othercount += 1;
09910              }
09911              $details .= "<div class='yui-module'><p>$module</p>";
09912              foreach ($backtraces as $backtrace) {
09913                  $details .= "<div class='backtrace'>$backtrace</div>";
09914              }
09915              $details .= '</div>';
09916          }
09917          $info['html'] .= "<span class='includedyuimodules'>Included YUI modules: $yuicount</span> ";
09918          $info['txt'] .= "includedyuimodules: $yuicount ";
09919          $info['html'] .= "<span class='includedjsmodules'>Other JavaScript modules: $othercount</span> ";
09920          $info['txt'] .= "includedjsmodules: $othercount ";
09921          // Slightly odd to output the details in a display: none div. The point
09922          // Is that it takes a lot of space, and if you care you can reveal it
09923          // using firebug.
09924          $info['html'] .= '<div id="yui-module-debug" class="notifytiny">'.$details.'</div>';
09925      }
09926 
09927     if (!empty($PERF->logwrites)) {
09928         $info['logwrites'] = $PERF->logwrites;
09929         $info['html'] .= '<span class="logwrites">Log DB writes '.$info['logwrites'].'</span> ';
09930         $info['txt'] .= 'logwrites: '.$info['logwrites'].' ';
09931     }
09932 
09933     $info['dbqueries'] = $DB->perf_get_reads().'/'.($DB->perf_get_writes() - $PERF->logwrites);
09934     $info['html'] .= '<span class="dbqueries">DB reads/writes: '.$info['dbqueries'].'</span> ';
09935     $info['txt'] .= 'db reads/writes: '.$info['dbqueries'].' ';
09936 
09937     if (function_exists('posix_times')) {
09938         $ptimes = posix_times();
09939         if (is_array($ptimes)) {
09940             foreach ($ptimes as $key => $val) {
09941                 $info[$key] = $ptimes[$key] -  $PERF->startposixtimes[$key];
09942             }
09943             $info['html'] .= "<span class=\"posixtimes\">ticks: $info[ticks] user: $info[utime] sys: $info[stime] cuser: $info[cutime] csys: $info[cstime]</span> ";
09944             $info['txt'] .= "ticks: $info[ticks] user: $info[utime] sys: $info[stime] cuser: $info[cutime] csys: $info[cstime] ";
09945         }
09946     }
09947 
09948     // Grab the load average for the last minute
09949     // /proc will only work under some linux configurations
09950     // while uptime is there under MacOSX/Darwin and other unices
09951     if (is_readable('/proc/loadavg') && $loadavg = @file('/proc/loadavg')) {
09952         list($server_load) = explode(' ', $loadavg[0]);
09953         unset($loadavg);
09954     } else if ( function_exists('is_executable') && is_executable('/usr/bin/uptime') && $loadavg = `/usr/bin/uptime` ) {
09955         if (preg_match('/load averages?: (\d+[\.,:]\d+)/', $loadavg, $matches)) {
09956             $server_load = $matches[1];
09957         } else {
09958             trigger_error('Could not parse uptime output!');
09959         }
09960     }
09961     if (!empty($server_load)) {
09962         $info['serverload'] = $server_load;
09963         $info['html'] .= '<span class="serverload">Load average: '.$info['serverload'].'</span> ';
09964         $info['txt'] .= "serverload: {$info['serverload']} ";
09965     }
09966 
09967     // Display size of session if session started
09968     if (session_id()) {
09969         $info['sessionsize'] = display_size(strlen(session_encode()));
09970         $info['html'] .= '<span class="sessionsize">Session: ' . $info['sessionsize'] . '</span> ';
09971         $info['txt'] .= "Session: {$info['sessionsize']} ";
09972     }
09973 
09974 /*    if (isset($rcache->hits) && isset($rcache->misses)) {
09975         $info['rcachehits'] = $rcache->hits;
09976         $info['rcachemisses'] = $rcache->misses;
09977         $info['html'] .= '<span class="rcache">Record cache hit/miss ratio : '.
09978             "{$rcache->hits}/{$rcache->misses}</span> ";
09979         $info['txt'] .= 'rcache: '.
09980             "{$rcache->hits}/{$rcache->misses} ";
09981     }*/
09982     $info['html'] = '<div class="performanceinfo siteinfo">'.$info['html'].'</div>';
09983     return $info;
09984 }
09985 
09989 function apd_get_profiling() {
09990     return shell_exec('pprofp -u ' . ini_get('apd.dumpdir') . '/pprof.' . getmypid() . '.*');
09991 }
09992 
10000 function remove_dir($dir, $content_only=false) {
10001     if (!file_exists($dir)) {
10002         // nothing to do
10003         return true;
10004     }
10005     $handle = opendir($dir);
10006     $result = true;
10007     while (false!==($item = readdir($handle))) {
10008         if($item != '.' && $item != '..') {
10009             if(is_dir($dir.'/'.$item)) {
10010                 $result = remove_dir($dir.'/'.$item) && $result;
10011             }else{
10012                 $result = unlink($dir.'/'.$item) && $result;
10013             }
10014         }
10015     }
10016     closedir($handle);
10017     if ($content_only) {
10018         clearstatcache(); // make sure file stat cache is properly invalidated
10019         return $result;
10020     }
10021     $result = rmdir($dir); // if anything left the result will be false, no need for && $result
10022     clearstatcache(); // make sure file stat cache is properly invalidated
10023     return $result;
10024 }
10025 
10034 function object_property_exists( $obj, $property ) {
10035     if (is_string( $obj )) {
10036         $properties = get_class_vars( $obj );
10037     }
10038     else {
10039         $properties = get_object_vars( $obj );
10040     }
10041     return array_key_exists( $property, $properties );
10042 }
10043 
10044 
10051 function custom_script_path() {
10052     global $CFG, $SCRIPT;
10053 
10054     if ($SCRIPT === null) {
10055         // Probably some weird external script
10056         return false;
10057     }
10058 
10059     $scriptpath = $CFG->customscripts . $SCRIPT;
10060 
10061     // check the custom script exists
10062     if (file_exists($scriptpath) and is_file($scriptpath)) {
10063         return $scriptpath;
10064     } else {
10065         return false;
10066     }
10067 }
10068 
10077 function is_mnet_remote_user($user) {
10078     global $CFG;
10079 
10080     if (!isset($CFG->mnet_localhost_id)) {
10081         include_once $CFG->dirroot . '/mnet/lib.php';
10082         $env = new mnet_environment();
10083         $env->init();
10084         unset($env);
10085     }
10086 
10087     return (!empty($user->mnethostid) && $user->mnethostid != $CFG->mnet_localhost_id);
10088 }
10089 
10098 function setup_lang_from_browser() {
10099 
10100     global $CFG, $SESSION, $USER;
10101 
10102     if (!empty($SESSION->lang) or !empty($USER->lang) or empty($CFG->autolang)) {
10103         // Lang is defined in session or user profile, nothing to do
10104         return;
10105     }
10106 
10107     if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { // There isn't list of browser langs, nothing to do
10108         return;
10109     }
10110 
10112     $rawlangs = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
10113     $rawlangs = str_replace('-', '_', $rawlangs);         // we are using underscores
10114     $rawlangs = explode(',', $rawlangs);                  // Convert to array
10115     $langs = array();
10116 
10117     $order = 1.0;
10118     foreach ($rawlangs as $lang) {
10119         if (strpos($lang, ';') === false) {
10120             $langs[(string)$order] = $lang;
10121             $order = $order-0.01;
10122         } else {
10123             $parts = explode(';', $lang);
10124             $pos = strpos($parts[1], '=');
10125             $langs[substr($parts[1], $pos+1)] = $parts[0];
10126         }
10127     }
10128     krsort($langs, SORT_NUMERIC);
10129 
10131     foreach ($langs as $lang) {
10132         $lang = strtolower(clean_param($lang, PARAM_SAFEDIR)); // clean it properly for include
10133         if (get_string_manager()->translation_exists($lang, false)) {
10134             $SESSION->lang = $lang; 
10135             break; 
10136         }
10137     }
10138     return;
10139 }
10140 
10150 function is_proxybypass( $url ) {
10151     global $CFG;
10152 
10153     // sanity check
10154     if (empty($CFG->proxyhost) or empty($CFG->proxybypass)) {
10155         return false;
10156     }
10157 
10158     // get the host part out of the url
10159     if (!$host = parse_url( $url, PHP_URL_HOST )) {
10160         return false;
10161     }
10162 
10163     // get the possible bypass hosts into an array
10164     $matches = explode( ',', $CFG->proxybypass );
10165 
10166     // check for a match
10167     // (IPs need to match the left hand side and hosts the right of the url,
10168     // but we can recklessly check both as there can't be a false +ve)
10169     $bypass = false;
10170     foreach ($matches as $match) {
10171         $match = trim($match);
10172 
10173         // try for IP match (Left side)
10174         $lhs = substr($host,0,strlen($match));
10175         if (strcasecmp($match,$lhs)==0) {
10176             return true;
10177         }
10178 
10179         // try for host match (Right side)
10180         $rhs = substr($host,-strlen($match));
10181         if (strcasecmp($match,$rhs)==0) {
10182             return true;
10183         }
10184     }
10185 
10186     // nothing matched.
10187     return false;
10188 }
10189 
10190 
10192 
10199 function is_newnav($navigation) {
10200     if (is_array($navigation) && !empty($navigation['newnav'])) {
10201         return true;
10202     } else {
10203         return false;
10204     }
10205 }
10206 
10216 function in_object_vars($var, $object) {
10217     $class_vars = get_class_vars(get_class($object));
10218     $class_vars = array_keys($class_vars);
10219     return in_array($var, $class_vars);
10220 }
10221 
10230 function object_array_unique($array, $keep_key_assoc = true) {
10231     $duplicate_keys = array();
10232     $tmp         = array();
10233 
10234     foreach ($array as $key=>$val) {
10235         // convert objects to arrays, in_array() does not support objects
10236         if (is_object($val)) {
10237             $val = (array)$val;
10238         }
10239 
10240         if (!in_array($val, $tmp)) {
10241             $tmp[] = $val;
10242         } else {
10243             $duplicate_keys[] = $key;
10244         }
10245     }
10246 
10247     foreach ($duplicate_keys as $key) {
10248         unset($array[$key]);
10249     }
10250 
10251     return $keep_key_assoc ? $array : array_values($array);
10252 }
10253 
10260 function is_primary_admin($userid){
10261     $primaryadmin =  get_admin();
10262 
10263     if($userid == $primaryadmin->id){
10264         return true;
10265     }else{
10266         return false;
10267     }
10268 }
10269 
10276 function get_site_identifier() {
10277     global $CFG;
10278     // Check to see if it is missing. If so, initialise it.
10279     if (empty($CFG->siteidentifier)) {
10280         set_config('siteidentifier', random_string(32) . $_SERVER['HTTP_HOST']);
10281     }
10282     // Return it.
10283     return $CFG->siteidentifier;
10284 }
10285 
10293 function check_consecutive_identical_characters($password, $maxchars) {
10294 
10295     if ($maxchars < 1) {
10296         return true; // 0 is to disable this check
10297     }
10298     if (strlen($password) <= $maxchars) {
10299         return true; // too short to fail this test
10300     }
10301 
10302     $previouschar = '';
10303     $consecutivecount = 1;
10304     foreach (str_split($password) as $char) {
10305         if ($char != $previouschar) {
10306             $consecutivecount = 1;
10307         }
10308         else {
10309             $consecutivecount++;
10310             if ($consecutivecount > $maxchars) {
10311                 return false; // check failed already
10312             }
10313         }
10314 
10315         $previouschar = $char;
10316     }
10317 
10318     return true;
10319 }
10320 
10341 function partial() {
10342     if (!class_exists('partial')) {
10343         class partial{
10344             var $values = array();
10345             var $func;
10346 
10347             function __construct($func, $args) {
10348                 $this->values = $args;
10349                 $this->func = $func;
10350             }
10351 
10352             function method() {
10353                 $args = func_get_args();
10354                 return call_user_func_array($this->func, array_merge($this->values, $args));
10355             }
10356         }
10357     }
10358     $args = func_get_args();
10359     $func = array_shift($args);
10360     $p = new partial($func, $args);
10361     return array($p, 'method');
10362 }
10363 
10370 function get_mnet_environment() {
10371     global $CFG;
10372     require_once($CFG->dirroot . '/mnet/lib.php');
10373     static $instance = null;
10374     if (empty($instance)) {
10375         $instance = new mnet_environment();
10376         $instance->init();
10377     }
10378     return $instance;
10379 }
10380 
10387 function get_mnet_remote_client() {
10388     if (!defined('MNET_SERVER')) {
10389         debugging(get_string('notinxmlrpcserver', 'mnet'));
10390         return false;
10391     }
10392     global $MNET_REMOTE_CLIENT;
10393     if (isset($MNET_REMOTE_CLIENT)) {
10394         return $MNET_REMOTE_CLIENT;
10395     }
10396     return false;
10397 }
10398 
10405 function set_mnet_remote_client($client) {
10406     if (!defined('MNET_SERVER')) {
10407         throw new moodle_exception('notinxmlrpcserver', 'mnet');
10408     }
10409     global $MNET_REMOTE_CLIENT;
10410     $MNET_REMOTE_CLIENT = $client;
10411 }
10412 
10419 function mnet_get_idp_jump_url($user) {
10420     global $CFG;
10421 
10422     static $mnetjumps = array();
10423     if (!array_key_exists($user->mnethostid, $mnetjumps)) {
10424         $idp = mnet_get_peer_host($user->mnethostid);
10425         $idpjumppath = mnet_get_app_jumppath($idp->applicationid);
10426         $mnetjumps[$user->mnethostid] = $idp->wwwroot . $idpjumppath . '?hostwwwroot=' . $CFG->wwwroot . '&wantsurl=';
10427     }
10428     return $mnetjumps[$user->mnethostid];
10429 }
10430 
10436 function get_home_page() {
10437     global $CFG;
10438 
10439     if (isloggedin() && !isguestuser() && !empty($CFG->defaulthomepage)) {
10440         if ($CFG->defaulthomepage == HOMEPAGE_MY) {
10441             return HOMEPAGE_MY;
10442         } else {
10443             return (int)get_user_preferences('user_home_page_preference', HOMEPAGE_MY);
10444         }
10445     }
10446     return HOMEPAGE_SITE;
10447 }
 All Data Structures Namespaces Files Functions Variables Enumerations