PageRenderTime 68ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 1ms

/moodle/lib/moodlelib.php

https://bitbucket.org/geek745/moodle-db2
PHP | 8514 lines | 5198 code | 1188 blank | 2128 comment | 1245 complexity | 78baf64e4ba00bd69d4399c9fed7842c MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause, LGPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php // $Id$
  2. ///////////////////////////////////////////////////////////////////////////
  3. // //
  4. // NOTICE OF COPYRIGHT //
  5. // //
  6. // Moodle - Modular Object-Oriented Dynamic Learning Environment //
  7. // http://moodle.org //
  8. // //
  9. // Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
  10. // //
  11. // This program is free software; you can redistribute it and/or modify //
  12. // it under the terms of the GNU General Public License as published by //
  13. // the Free Software Foundation; either version 2 of the License, or //
  14. // (at your option) any later version. //
  15. // //
  16. // This program is distributed in the hope that it will be useful, //
  17. // but WITHOUT ANY WARRANTY; without even the implied warranty of //
  18. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
  19. // GNU General Public License for more details: //
  20. // //
  21. // http://www.gnu.org/copyleft/gpl.html //
  22. // //
  23. ///////////////////////////////////////////////////////////////////////////
  24. /**
  25. * moodlelib.php - Moodle main library
  26. *
  27. * Main library file of miscellaneous general-purpose Moodle functions.
  28. * Other main libraries:
  29. * - weblib.php - functions that produce web output
  30. * - datalib.php - functions that access the database
  31. * @author Martin Dougiamas
  32. * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
  33. * @package moodlecore
  34. */
  35. /// CONSTANTS (Encased in phpdoc proper comments)/////////////////////////
  36. /**
  37. * Used by some scripts to check they are being called by Moodle
  38. */
  39. define('MOODLE_INTERNAL', true);
  40. /// Date and time constants ///
  41. /**
  42. * Time constant - the number of seconds in a year
  43. */
  44. define('YEARSECS', 31536000);
  45. /**
  46. * Time constant - the number of seconds in a week
  47. */
  48. define('WEEKSECS', 604800);
  49. /**
  50. * Time constant - the number of seconds in a day
  51. */
  52. define('DAYSECS', 86400);
  53. /**
  54. * Time constant - the number of seconds in an hour
  55. */
  56. define('HOURSECS', 3600);
  57. /**
  58. * Time constant - the number of seconds in a minute
  59. */
  60. define('MINSECS', 60);
  61. /**
  62. * Time constant - the number of minutes in a day
  63. */
  64. define('DAYMINS', 1440);
  65. /**
  66. * Time constant - the number of minutes in an hour
  67. */
  68. define('HOURMINS', 60);
  69. /// Parameter constants - every call to optional_param(), required_param() ///
  70. /// or clean_param() should have a specified type of parameter. //////////////
  71. /**
  72. * PARAM_RAW specifies a parameter that is not cleaned/processed in any way;
  73. * originally was 0, but changed because we need to detect unknown
  74. * parameter types and swiched order in clean_param().
  75. */
  76. define('PARAM_RAW', 666);
  77. /**
  78. * PARAM_CLEAN - obsoleted, please try to use more specific type of parameter.
  79. * It was one of the first types, that is why it is abused so much ;-)
  80. */
  81. define('PARAM_CLEAN', 0x0001);
  82. /**
  83. * PARAM_INT - integers only, use when expecting only numbers.
  84. */
  85. define('PARAM_INT', 0x0002);
  86. /**
  87. * PARAM_INTEGER - an alias for PARAM_INT
  88. */
  89. define('PARAM_INTEGER', 0x0002);
  90. /**
  91. * PARAM_NUMBER - a real/floating point number.
  92. */
  93. define('PARAM_NUMBER', 0x000a);
  94. /**
  95. * PARAM_ALPHA - contains only english letters.
  96. */
  97. define('PARAM_ALPHA', 0x0004);
  98. /**
  99. * PARAM_ACTION - an alias for PARAM_ALPHA, use for various actions in formas and urls
  100. * @TODO: should we alias it to PARAM_ALPHANUM ?
  101. */
  102. define('PARAM_ACTION', 0x0004);
  103. /**
  104. * PARAM_FORMAT - an alias for PARAM_ALPHA, use for names of plugins, formats, etc.
  105. * @TODO: should we alias it to PARAM_ALPHANUM ?
  106. */
  107. define('PARAM_FORMAT', 0x0004);
  108. /**
  109. * PARAM_NOTAGS - all html tags are stripped from the text. Do not abuse this type.
  110. */
  111. define('PARAM_NOTAGS', 0x0008);
  112. /**
  113. * PARAM_MULTILANG - alias of PARAM_TEXT.
  114. */
  115. define('PARAM_MULTILANG', 0x0009);
  116. /**
  117. * PARAM_TEXT - general plain text compatible with multilang filter, no other html tags.
  118. */
  119. define('PARAM_TEXT', 0x0009);
  120. /**
  121. * PARAM_FILE - safe file name, all dangerous chars are stripped, protects against XSS, SQL injections and directory traversals
  122. */
  123. define('PARAM_FILE', 0x0010);
  124. /**
  125. * PARAM_TAG - one tag (interests, blogs, etc.) - mostly international alphanumeric with spaces
  126. */
  127. define('PARAM_TAG', 0x0011);
  128. /**
  129. * PARAM_TAGLIST - list of tags separated by commas (interests, blogs, etc.)
  130. */
  131. define('PARAM_TAGLIST', 0x0012);
  132. /**
  133. * PARAM_PATH - safe relative path name, all dangerous chars are stripped, protects against XSS, SQL injections and directory traversals
  134. * note: the leading slash is not removed, window drive letter is not allowed
  135. */
  136. define('PARAM_PATH', 0x0020);
  137. /**
  138. * PARAM_HOST - expected fully qualified domain name (FQDN) or an IPv4 dotted quad (IP address)
  139. */
  140. define('PARAM_HOST', 0x0040);
  141. /**
  142. * PARAM_URL - expected properly formatted URL. Please note that domain part is required, http://localhost/ is not acceppted but http://localhost.localdomain/ is ok.
  143. */
  144. define('PARAM_URL', 0x0080);
  145. /**
  146. * PARAM_LOCALURL - expected properly formatted URL as well as one that refers to the local server itself. (NOT orthogonal to the others! Implies PARAM_URL!)
  147. */
  148. define('PARAM_LOCALURL', 0x0180);
  149. /**
  150. * PARAM_CLEANFILE - safe file name, all dangerous and regional chars are removed,
  151. * use when you want to store a new file submitted by students
  152. */
  153. define('PARAM_CLEANFILE',0x0200);
  154. /**
  155. * PARAM_ALPHANUM - expected numbers and letters only.
  156. */
  157. define('PARAM_ALPHANUM', 0x0400);
  158. /**
  159. * PARAM_BOOL - converts input into 0 or 1, use for switches in forms and urls.
  160. */
  161. define('PARAM_BOOL', 0x0800);
  162. /**
  163. * PARAM_CLEANHTML - cleans submitted HTML code and removes slashes
  164. * note: do not forget to addslashes() before storing into database!
  165. */
  166. define('PARAM_CLEANHTML',0x1000);
  167. /**
  168. * PARAM_ALPHAEXT the same contents as PARAM_ALPHA plus the chars in quotes: "/-_" allowed,
  169. * suitable for include() and require()
  170. * @TODO: should we rename this function to PARAM_SAFEDIRS??
  171. */
  172. define('PARAM_ALPHAEXT', 0x2000);
  173. /**
  174. * PARAM_SAFEDIR - safe directory name, suitable for include() and require()
  175. */
  176. define('PARAM_SAFEDIR', 0x4000);
  177. /**
  178. * PARAM_SEQUENCE - expects a sequence of numbers like 8 to 1,5,6,4,6,8,9. Numbers and comma only.
  179. */
  180. define('PARAM_SEQUENCE', 0x8000);
  181. /**
  182. * PARAM_PEM - Privacy Enhanced Mail format
  183. */
  184. define('PARAM_PEM', 0x10000);
  185. /**
  186. * PARAM_BASE64 - Base 64 encoded format
  187. */
  188. define('PARAM_BASE64', 0x20000);
  189. /// Page types ///
  190. /**
  191. * PAGE_COURSE_VIEW is a definition of a page type. For more information on the page class see moodle/lib/pagelib.php.
  192. */
  193. define('PAGE_COURSE_VIEW', 'course-view');
  194. /// Debug levels ///
  195. /** no warnings at all */
  196. define ('DEBUG_NONE', 0);
  197. /** E_ERROR | E_PARSE */
  198. define ('DEBUG_MINIMAL', 5);
  199. /** E_ERROR | E_PARSE | E_WARNING | E_NOTICE */
  200. define ('DEBUG_NORMAL', 15);
  201. /** E_ALL without E_STRICT for now, do show recoverable fatal errors */
  202. define ('DEBUG_ALL', 6143);
  203. /** DEBUG_ALL with extra Moodle debug messages - (DEBUG_ALL | 32768) */
  204. define ('DEBUG_DEVELOPER', 38911);
  205. /**
  206. * Blog access level constant declaration
  207. */
  208. define ('BLOG_USER_LEVEL', 1);
  209. define ('BLOG_GROUP_LEVEL', 2);
  210. define ('BLOG_COURSE_LEVEL', 3);
  211. define ('BLOG_SITE_LEVEL', 4);
  212. define ('BLOG_GLOBAL_LEVEL', 5);
  213. /**
  214. * Tag constanst
  215. */
  216. //To prevent problems with multibytes strings, this should not exceed the
  217. //length of "varchar(255) / 3 (bytes / utf-8 character) = 85".
  218. define('TAG_MAX_LENGTH', 50);
  219. /**
  220. * Password policy constants
  221. */
  222. define ('PASSWORD_LOWER', 'abcdefghijklmnopqrstuvwxyz');
  223. define ('PASSWORD_UPPER', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
  224. define ('PASSWORD_DIGITS', '0123456789');
  225. define ('PASSWORD_NONALPHANUM', '.,;:!?_-+/*@#&$');
  226. if (!defined('SORT_LOCALE_STRING')) { // PHP < 4.4.0 - TODO: remove in 2.0
  227. define('SORT_LOCALE_STRING', SORT_STRING);
  228. }
  229. /// PARAMETER HANDLING ////////////////////////////////////////////////////
  230. /**
  231. * Returns a particular value for the named variable, taken from
  232. * POST or GET. If the parameter doesn't exist then an error is
  233. * thrown because we require this variable.
  234. *
  235. * This function should be used to initialise all required values
  236. * in a script that are based on parameters. Usually it will be
  237. * used like this:
  238. * $id = required_param('id');
  239. *
  240. * @param string $parname the name of the page parameter we want
  241. * @param int $type expected type of parameter
  242. * @return mixed
  243. */
  244. function required_param($parname, $type=PARAM_CLEAN) {
  245. // detect_unchecked_vars addition
  246. global $CFG;
  247. if (!empty($CFG->detect_unchecked_vars)) {
  248. global $UNCHECKED_VARS;
  249. unset ($UNCHECKED_VARS->vars[$parname]);
  250. }
  251. if (isset($_POST[$parname])) { // POST has precedence
  252. $param = $_POST[$parname];
  253. } else if (isset($_GET[$parname])) {
  254. $param = $_GET[$parname];
  255. } else {
  256. error('A required parameter ('.$parname.') was missing');
  257. }
  258. return clean_param($param, $type);
  259. }
  260. /**
  261. * Returns a particular value for the named variable, taken from
  262. * POST or GET, otherwise returning a given default.
  263. *
  264. * This function should be used to initialise all optional values
  265. * in a script that are based on parameters. Usually it will be
  266. * used like this:
  267. * $name = optional_param('name', 'Fred');
  268. *
  269. * @param string $parname the name of the page parameter we want
  270. * @param mixed $default the default value to return if nothing is found
  271. * @param int $type expected type of parameter
  272. * @return mixed
  273. */
  274. function optional_param($parname, $default=NULL, $type=PARAM_CLEAN) {
  275. // detect_unchecked_vars addition
  276. global $CFG;
  277. if (!empty($CFG->detect_unchecked_vars)) {
  278. global $UNCHECKED_VARS;
  279. unset ($UNCHECKED_VARS->vars[$parname]);
  280. }
  281. if (isset($_POST[$parname])) { // POST has precedence
  282. $param = $_POST[$parname];
  283. } else if (isset($_GET[$parname])) {
  284. $param = $_GET[$parname];
  285. } else {
  286. return $default;
  287. }
  288. return clean_param($param, $type);
  289. }
  290. /**
  291. * Used by {@link optional_param()} and {@link required_param()} to
  292. * clean the variables and/or cast to specific types, based on
  293. * an options field.
  294. * <code>
  295. * $course->format = clean_param($course->format, PARAM_ALPHA);
  296. * $selectedgrade_item = clean_param($selectedgrade_item, PARAM_CLEAN);
  297. * </code>
  298. *
  299. * @uses $CFG
  300. * @uses PARAM_RAW
  301. * @uses PARAM_CLEAN
  302. * @uses PARAM_CLEANHTML
  303. * @uses PARAM_INT
  304. * @uses PARAM_NUMBER
  305. * @uses PARAM_ALPHA
  306. * @uses PARAM_ALPHANUM
  307. * @uses PARAM_ALPHAEXT
  308. * @uses PARAM_SEQUENCE
  309. * @uses PARAM_BOOL
  310. * @uses PARAM_NOTAGS
  311. * @uses PARAM_TEXT
  312. * @uses PARAM_SAFEDIR
  313. * @uses PARAM_CLEANFILE
  314. * @uses PARAM_FILE
  315. * @uses PARAM_PATH
  316. * @uses PARAM_HOST
  317. * @uses PARAM_URL
  318. * @uses PARAM_LOCALURL
  319. * @uses PARAM_PEM
  320. * @uses PARAM_BASE64
  321. * @uses PARAM_TAG
  322. * @uses PARAM_SEQUENCE
  323. * @param mixed $param the variable we are cleaning
  324. * @param int $type expected format of param after cleaning.
  325. * @return mixed
  326. */
  327. function clean_param($param, $type) {
  328. global $CFG;
  329. if (is_array($param)) { // Let's loop
  330. $newparam = array();
  331. foreach ($param as $key => $value) {
  332. $newparam[$key] = clean_param($value, $type);
  333. }
  334. return $newparam;
  335. }
  336. switch ($type) {
  337. case PARAM_RAW: // no cleaning at all
  338. return $param;
  339. case PARAM_CLEAN: // General HTML cleaning, try to use more specific type if possible
  340. if (is_numeric($param)) {
  341. return $param;
  342. }
  343. $param = stripslashes($param); // Needed for kses to work fine
  344. $param = clean_text($param); // Sweep for scripts, etc
  345. return addslashes($param); // Restore original request parameter slashes
  346. case PARAM_CLEANHTML: // prepare html fragment for display, do not store it into db!!
  347. $param = stripslashes($param); // Remove any slashes
  348. $param = clean_text($param); // Sweep for scripts, etc
  349. return trim($param);
  350. case PARAM_INT:
  351. return (int)$param; // Convert to integer
  352. case PARAM_NUMBER:
  353. return (float)$param; // Convert to integer
  354. case PARAM_ALPHA: // Remove everything not a-z
  355. return eregi_replace('[^a-zA-Z]', '', $param);
  356. case PARAM_ALPHANUM: // Remove everything not a-zA-Z0-9
  357. return eregi_replace('[^A-Za-z0-9]', '', $param);
  358. case PARAM_ALPHAEXT: // Remove everything not a-zA-Z/_-
  359. return eregi_replace('[^a-zA-Z/_-]', '', $param);
  360. case PARAM_SEQUENCE: // Remove everything not 0-9,
  361. return eregi_replace('[^0-9,]', '', $param);
  362. case PARAM_BOOL: // Convert to 1 or 0
  363. $tempstr = strtolower($param);
  364. if ($tempstr == 'on' or $tempstr == 'yes' ) {
  365. $param = 1;
  366. } else if ($tempstr == 'off' or $tempstr == 'no') {
  367. $param = 0;
  368. } else {
  369. $param = empty($param) ? 0 : 1;
  370. }
  371. return $param;
  372. case PARAM_NOTAGS: // Strip all tags
  373. return strip_tags($param);
  374. case PARAM_TEXT: // leave only tags needed for multilang
  375. return clean_param(strip_tags($param, '<lang><span>'), PARAM_CLEAN);
  376. case PARAM_SAFEDIR: // Remove everything not a-zA-Z0-9_-
  377. return eregi_replace('[^a-zA-Z0-9_-]', '', $param);
  378. case PARAM_CLEANFILE: // allow only safe characters
  379. return clean_filename($param);
  380. case PARAM_FILE: // Strip all suspicious characters from filename
  381. $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':\\/]', '', $param);
  382. $param = ereg_replace('\.\.+', '', $param);
  383. if($param == '.') {
  384. $param = '';
  385. }
  386. return $param;
  387. case PARAM_PATH: // Strip all suspicious characters from file path
  388. $param = str_replace('\\\'', '\'', $param);
  389. $param = str_replace('\\"', '"', $param);
  390. $param = str_replace('\\', '/', $param);
  391. $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':]', '', $param);
  392. $param = ereg_replace('\.\.+', '', $param);
  393. $param = ereg_replace('//+', '/', $param);
  394. return ereg_replace('/(\./)+', '/', $param);
  395. case PARAM_HOST: // allow FQDN or IPv4 dotted quad
  396. $param = preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars
  397. // match ipv4 dotted quad
  398. if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){
  399. // confirm values are ok
  400. if ( $match[0] > 255
  401. || $match[1] > 255
  402. || $match[3] > 255
  403. || $match[4] > 255 ) {
  404. // hmmm, what kind of dotted quad is this?
  405. $param = '';
  406. }
  407. } elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers
  408. && !preg_match('/^[\.-]/', $param) // no leading dots/hyphens
  409. && !preg_match('/[\.-]$/', $param) // no trailing dots/hyphens
  410. ) {
  411. // all is ok - $param is respected
  412. } else {
  413. // all is not ok...
  414. $param='';
  415. }
  416. return $param;
  417. case PARAM_URL: // allow safe ftp, http, mailto urls
  418. include_once($CFG->dirroot . '/lib/validateurlsyntax.php');
  419. if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p?f?q?r?')) {
  420. // all is ok, param is respected
  421. } else {
  422. $param =''; // not really ok
  423. }
  424. return $param;
  425. case PARAM_LOCALURL: // allow http absolute, root relative and relative URLs within wwwroot
  426. $param = clean_param($param, PARAM_URL);
  427. if (!empty($param)) {
  428. if (preg_match(':^/:', $param)) {
  429. // root-relative, ok!
  430. } elseif (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i',$param)) {
  431. // absolute, and matches our wwwroot
  432. } else {
  433. // relative - let's make sure there are no tricks
  434. if (validateUrlSyntax($param, 's-u-P-a-p-f+q?r?')) {
  435. // looks ok.
  436. } else {
  437. $param = '';
  438. }
  439. }
  440. }
  441. return $param;
  442. case PARAM_PEM:
  443. $param = trim($param);
  444. // PEM formatted strings may contain letters/numbers and the symbols
  445. // forward slash: /
  446. // plus sign: +
  447. // equal sign: =
  448. // , surrounded by BEGIN and END CERTIFICATE prefix and suffixes
  449. if (preg_match('/^-----BEGIN CERTIFICATE-----([\s\w\/\+=]+)-----END CERTIFICATE-----$/', trim($param), $matches)) {
  450. list($wholething, $body) = $matches;
  451. unset($wholething, $matches);
  452. $b64 = clean_param($body, PARAM_BASE64);
  453. if (!empty($b64)) {
  454. return "-----BEGIN CERTIFICATE-----\n$b64\n-----END CERTIFICATE-----\n";
  455. } else {
  456. return '';
  457. }
  458. }
  459. return '';
  460. case PARAM_BASE64:
  461. if (!empty($param)) {
  462. // PEM formatted strings may contain letters/numbers and the symbols
  463. // forward slash: /
  464. // plus sign: +
  465. // equal sign: =
  466. if (0 >= preg_match('/^([\s\w\/\+=]+)$/', trim($param))) {
  467. return '';
  468. }
  469. $lines = preg_split('/[\s]+/', $param, -1, PREG_SPLIT_NO_EMPTY);
  470. // Each line of base64 encoded data must be 64 characters in
  471. // length, except for the last line which may be less than (or
  472. // equal to) 64 characters long.
  473. for ($i=0, $j=count($lines); $i < $j; $i++) {
  474. if ($i + 1 == $j) {
  475. if (64 < strlen($lines[$i])) {
  476. return '';
  477. }
  478. continue;
  479. }
  480. if (64 != strlen($lines[$i])) {
  481. return '';
  482. }
  483. }
  484. return implode("\n",$lines);
  485. } else {
  486. return '';
  487. }
  488. case PARAM_TAG:
  489. //as long as magic_quotes_gpc is used, a backslash will be a
  490. //problem, so remove *all* backslash.
  491. $param = str_replace('\\', '', $param);
  492. //convert many whitespace chars into one
  493. $param = preg_replace('/\s+/', ' ', $param);
  494. $textlib = textlib_get_instance();
  495. $param = $textlib->substr(trim($param), 0, TAG_MAX_LENGTH);
  496. return $param;
  497. case PARAM_TAGLIST:
  498. $tags = explode(',', $param);
  499. $result = array();
  500. foreach ($tags as $tag) {
  501. $res = clean_param($tag, PARAM_TAG);
  502. if ($res != '') {
  503. $result[] = $res;
  504. }
  505. }
  506. if ($result) {
  507. return implode(',', $result);
  508. } else {
  509. return '';
  510. }
  511. default: // throw error, switched parameters in optional_param or another serious problem
  512. error("Unknown parameter type: $type");
  513. }
  514. }
  515. /**
  516. * Return true if given value is integer or string with integer value
  517. *
  518. * @param mixed $value String or Int
  519. * @return bool true if number, false if not
  520. */
  521. function is_number($value) {
  522. if (is_int($value)) {
  523. return true;
  524. } else if (is_string($value)) {
  525. return ((string)(int)$value) === $value;
  526. } else {
  527. return false;
  528. }
  529. }
  530. /**
  531. * This function is useful for testing whether something you got back from
  532. * the HTML editor actually contains anything. Sometimes the HTML editor
  533. * appear to be empty, but actually you get back a <br> tag or something.
  534. *
  535. * @param string $string a string containing HTML.
  536. * @return boolean does the string contain any actual content - that is text,
  537. * images, objcts, etc.
  538. */
  539. function html_is_blank($string) {
  540. return trim(strip_tags($string, '<img><object><applet><input><select><textarea><hr>')) == '';
  541. }
  542. /**
  543. * Set a key in global configuration
  544. *
  545. * Set a key/value pair in both this session's {@link $CFG} global variable
  546. * and in the 'config' database table for future sessions.
  547. *
  548. * Can also be used to update keys for plugin-scoped configs in config_plugin table.
  549. * In that case it doesn't affect $CFG.
  550. *
  551. * A NULL value will delete the entry.
  552. *
  553. * @param string $name the key to set
  554. * @param string $value the value to set (without magic quotes)
  555. * @param string $plugin (optional) the plugin scope
  556. * @uses $CFG
  557. * @return bool
  558. */
  559. function set_config($name, $value, $plugin=NULL) {
  560. /// No need for get_config because they are usually always available in $CFG
  561. global $CFG;
  562. if (empty($plugin)) {
  563. if (!array_key_exists($name, $CFG->config_php_settings)) {
  564. // So it's defined for this invocation at least
  565. if (is_null($value)) {
  566. unset($CFG->$name);
  567. } else {
  568. $CFG->$name = (string)$value; // settings from db are always strings
  569. }
  570. }
  571. if (get_field('config', 'name', 'name', $name)) {
  572. if ($value===null) {
  573. return delete_records('config', 'name', $name);
  574. } else {
  575. return set_field('config', 'value', addslashes($value), 'name', $name);
  576. }
  577. } else {
  578. if ($value===null) {
  579. return true;
  580. }
  581. $config = new object();
  582. $config->name = $name;
  583. $config->value = addslashes($value);
  584. return insert_record('config', $config);
  585. }
  586. } else { // plugin scope
  587. if ($id = get_field('config_plugins', 'id', 'name', $name, 'plugin', $plugin)) {
  588. if ($value===null) {
  589. return delete_records('config_plugins', 'name', $name, 'plugin', $plugin);
  590. } else {
  591. return set_field('config_plugins', 'value', addslashes($value), 'id', $id);
  592. }
  593. } else {
  594. if ($value===null) {
  595. return true;
  596. }
  597. $config = new object();
  598. $config->plugin = addslashes($plugin);
  599. $config->name = $name;
  600. $config->value = addslashes($value);
  601. return insert_record('config_plugins', $config);
  602. }
  603. }
  604. }
  605. /**
  606. * Get configuration values from the global config table
  607. * or the config_plugins table.
  608. *
  609. * If called with no parameters it will do the right thing
  610. * generating $CFG safely from the database without overwriting
  611. * existing values.
  612. *
  613. * If called with 2 parameters it will return a $string single
  614. * value or false of the value is not found.
  615. *
  616. * @param string $plugin
  617. * @param string $name
  618. * @uses $CFG
  619. * @return hash-like object or single value
  620. *
  621. */
  622. function get_config($plugin=NULL, $name=NULL) {
  623. global $CFG;
  624. if (!empty($name)) { // the user is asking for a specific value
  625. if (!empty($plugin)) {
  626. return get_field('config_plugins', 'value', 'plugin' , $plugin, 'name', $name);
  627. } else {
  628. return get_field('config', 'value', 'name', $name);
  629. }
  630. }
  631. // the user is after a recordset
  632. if (!empty($plugin)) {
  633. if ($configs=get_records('config_plugins', 'plugin', $plugin, '', 'name,value')) {
  634. $configs = (array)$configs;
  635. $localcfg = array();
  636. foreach ($configs as $config) {
  637. $localcfg[$config->name] = $config->value;
  638. }
  639. return (object)$localcfg;
  640. } else {
  641. return false;
  642. }
  643. } else {
  644. // this was originally in setup.php
  645. if ($configs = get_records('config')) {
  646. $localcfg = (array)$CFG;
  647. foreach ($configs as $config) {
  648. if (!isset($localcfg[$config->name])) {
  649. $localcfg[$config->name] = $config->value;
  650. }
  651. // do not complain anymore if config.php overrides settings from db
  652. }
  653. $localcfg = (object)$localcfg;
  654. return $localcfg;
  655. } else {
  656. // preserve $CFG if DB returns nothing or error
  657. return $CFG;
  658. }
  659. }
  660. }
  661. /**
  662. * Removes a key from global configuration
  663. *
  664. * @param string $name the key to set
  665. * @param string $plugin (optional) the plugin scope
  666. * @uses $CFG
  667. * @return bool
  668. */
  669. function unset_config($name, $plugin=NULL) {
  670. global $CFG;
  671. unset($CFG->$name);
  672. if (empty($plugin)) {
  673. return delete_records('config', 'name', $name);
  674. } else {
  675. return delete_records('config_plugins', 'name', $name, 'plugin', $plugin);
  676. }
  677. }
  678. /**
  679. * Get volatile flags
  680. *
  681. * @param string $type
  682. * @param int $changedsince
  683. * @return records array
  684. *
  685. */
  686. function get_cache_flags($type, $changedsince=NULL) {
  687. $type = addslashes($type);
  688. $sqlwhere = 'flagtype=\'' . $type . '\' AND expiry >= ' . time();
  689. if ($changedsince !== NULL) {
  690. $changedsince = (int)$changedsince;
  691. $sqlwhere .= ' AND timemodified > ' . $changedsince;
  692. }
  693. $cf = array();
  694. if ($flags=get_records_select('cache_flags', $sqlwhere, '', 'name,value')) {
  695. foreach ($flags as $flag) {
  696. $cf[$flag->name] = $flag->value;
  697. }
  698. }
  699. return $cf;
  700. }
  701. /**
  702. * Use this funciton to get a list of users from a config setting of type admin_setting_users_with_capability.
  703. * @param string $value the value of the config setting.
  704. * @param string $capability the capability - must match the one passed to the admin_setting_users_with_capability constructor.
  705. * @return array of user objects.
  706. */
  707. function get_users_from_config($value, $capability) {
  708. global $CFG;
  709. if ($value == '$@ALL@$') {
  710. $users = get_users_by_capability(get_context_instance(CONTEXT_SYSTEM), $capability);
  711. } else if ($value) {
  712. $usernames = explode(',', $value);
  713. $users = get_records_select('user', "username IN ('" . implode("','", $usernames) . "') AND mnethostid = " . $CFG->mnet_localhost_id);
  714. } else {
  715. $users = array();
  716. }
  717. return $users;
  718. }
  719. /**
  720. * Get volatile flags
  721. *
  722. * @param string $type
  723. * @param string $name
  724. * @param int $changedsince
  725. * @return records array
  726. *
  727. */
  728. function get_cache_flag($type, $name, $changedsince=NULL) {
  729. $type = addslashes($type);
  730. $name = addslashes($name);
  731. $sqlwhere = 'flagtype=\'' . $type . '\' AND name=\'' . $name . '\' AND expiry >= ' . time();
  732. if ($changedsince !== NULL) {
  733. $changedsince = (int)$changedsince;
  734. $sqlwhere .= ' AND timemodified > ' . $changedsince;
  735. }
  736. return get_field_select('cache_flags', 'value', $sqlwhere);
  737. }
  738. /**
  739. * Set a volatile flag
  740. *
  741. * @param string $type the "type" namespace for the key
  742. * @param string $name the key to set
  743. * @param string $value the value to set (without magic quotes) - NULL will remove the flag
  744. * @param int $expiry (optional) epoch indicating expiry - defaults to now()+ 24hs
  745. * @return bool
  746. */
  747. function set_cache_flag($type, $name, $value, $expiry=NULL) {
  748. $timemodified = time();
  749. if ($expiry===NULL || $expiry < $timemodified) {
  750. $expiry = $timemodified + 24 * 60 * 60;
  751. } else {
  752. $expiry = (int)$expiry;
  753. }
  754. if ($value === NULL) {
  755. return unset_cache_flag($type,$name);
  756. }
  757. $type = addslashes($type);
  758. $name = addslashes($name);
  759. if ($f = get_record('cache_flags', 'name', $name, 'flagtype', $type)) { // this is a potentail problem in DEBUG_DEVELOPER
  760. if ($f->value == $value and $f->expiry == $expiry and $f->timemodified == $timemodified) {
  761. return true; //no need to update; helps rcache too
  762. }
  763. $f->value = addslashes($value);
  764. $f->expiry = $expiry;
  765. $f->timemodified = $timemodified;
  766. return update_record('cache_flags', $f);
  767. } else {
  768. $f = new object();
  769. $f->flagtype = $type;
  770. $f->name = $name;
  771. $f->value = addslashes($value);
  772. $f->expiry = $expiry;
  773. $f->timemodified = $timemodified;
  774. return (bool)insert_record('cache_flags', $f);
  775. }
  776. }
  777. /**
  778. * Removes a single volatile flag
  779. *
  780. * @param string $type the "type" namespace for the key
  781. * @param string $name the key to set
  782. * @uses $CFG
  783. * @return bool
  784. */
  785. function unset_cache_flag($type, $name) {
  786. return delete_records('cache_flags',
  787. 'name', addslashes($name),
  788. 'flagtype', addslashes($type));
  789. }
  790. /**
  791. * Garbage-collect volatile flags
  792. *
  793. */
  794. function gc_cache_flags() {
  795. return delete_records_select('cache_flags', 'expiry < ' . time());
  796. }
  797. /**
  798. * Refresh current $USER session global variable with all their current preferences.
  799. * @uses $USER
  800. */
  801. function reload_user_preferences() {
  802. global $USER;
  803. //reset preference
  804. $USER->preference = array();
  805. if (!isloggedin() or isguestuser()) {
  806. // no permanent storage for not-logged-in user and guest
  807. } else if ($preferences = get_records('user_preferences', 'userid', $USER->id)) {
  808. foreach ($preferences as $preference) {
  809. $USER->preference[$preference->name] = $preference->value;
  810. }
  811. }
  812. return true;
  813. }
  814. /**
  815. * Sets a preference for the current user
  816. * Optionally, can set a preference for a different user object
  817. * @uses $USER
  818. * @todo Add a better description and include usage examples. Add inline links to $USER and user functions in above line.
  819. * @param string $name The key to set as preference for the specified user
  820. * @param string $value The value to set forthe $name key in the specified user's record
  821. * @param int $otheruserid A moodle user ID
  822. * @return bool
  823. */
  824. function set_user_preference($name, $value, $otheruserid=NULL) {
  825. global $USER;
  826. if (!isset($USER->preference)) {
  827. reload_user_preferences();
  828. }
  829. if (empty($name)) {
  830. return false;
  831. }
  832. $nostore = false;
  833. if (empty($otheruserid)){
  834. if (!isloggedin() or isguestuser()) {
  835. $nostore = true;
  836. }
  837. $userid = $USER->id;
  838. } else {
  839. if (isguestuser($otheruserid)) {
  840. $nostore = true;
  841. }
  842. $userid = $otheruserid;
  843. }
  844. $return = true;
  845. if ($nostore) {
  846. // no permanent storage for not-logged-in user and guest
  847. } else if ($preference = get_record('user_preferences', 'userid', $userid, 'name', addslashes($name))) {
  848. if ($preference->value === $value) {
  849. return true;
  850. }
  851. if (!set_field('user_preferences', 'value', addslashes((string)$value), 'id', $preference->id)) {
  852. $return = false;
  853. }
  854. } else {
  855. $preference = new object();
  856. $preference->userid = $userid;
  857. $preference->name = addslashes($name);
  858. $preference->value = addslashes((string)$value);
  859. if (!insert_record('user_preferences', $preference)) {
  860. $return = false;
  861. }
  862. }
  863. // update value in USER session if needed
  864. if ($userid == $USER->id) {
  865. $USER->preference[$name] = (string)$value;
  866. }
  867. return $return;
  868. }
  869. /**
  870. * Unsets a preference completely by deleting it from the database
  871. * Optionally, can set a preference for a different user id
  872. * @uses $USER
  873. * @param string $name The key to unset as preference for the specified user
  874. * @param int $otheruserid A moodle user ID
  875. */
  876. function unset_user_preference($name, $otheruserid=NULL) {
  877. global $USER;
  878. if (!isset($USER->preference)) {
  879. reload_user_preferences();
  880. }
  881. if (empty($otheruserid)){
  882. $userid = $USER->id;
  883. } else {
  884. $userid = $otheruserid;
  885. }
  886. //Delete the preference from $USER if needed
  887. if ($userid == $USER->id) {
  888. unset($USER->preference[$name]);
  889. }
  890. //Then from DB
  891. return delete_records('user_preferences', 'userid', $userid, 'name', addslashes($name));
  892. }
  893. /**
  894. * Sets a whole array of preferences for the current user
  895. * @param array $prefarray An array of key/value pairs to be set
  896. * @param int $otheruserid A moodle user ID
  897. * @return bool
  898. */
  899. function set_user_preferences($prefarray, $otheruserid=NULL) {
  900. if (!is_array($prefarray) or empty($prefarray)) {
  901. return false;
  902. }
  903. $return = true;
  904. foreach ($prefarray as $name => $value) {
  905. // The order is important; test for return is done first
  906. $return = (set_user_preference($name, $value, $otheruserid) && $return);
  907. }
  908. return $return;
  909. }
  910. /**
  911. * If no arguments are supplied this function will return
  912. * all of the current user preferences as an array.
  913. * If a name is specified then this function
  914. * attempts to return that particular preference value. If
  915. * none is found, then the optional value $default is returned,
  916. * otherwise NULL.
  917. * @param string $name Name of the key to use in finding a preference value
  918. * @param string $default Value to be returned if the $name key is not set in the user preferences
  919. * @param int $otheruserid A moodle user ID
  920. * @uses $USER
  921. * @return string
  922. */
  923. function get_user_preferences($name=NULL, $default=NULL, $otheruserid=NULL) {
  924. global $USER;
  925. if (!isset($USER->preference)) {
  926. reload_user_preferences();
  927. }
  928. if (empty($otheruserid)){
  929. $userid = $USER->id;
  930. } else {
  931. $userid = $otheruserid;
  932. }
  933. if ($userid == $USER->id) {
  934. $preference = $USER->preference;
  935. } else {
  936. $preference = array();
  937. if ($prefdata = get_records('user_preferences', 'userid', $userid)) {
  938. foreach ($prefdata as $pref) {
  939. $preference[$pref->name] = $pref->value;
  940. }
  941. }
  942. }
  943. if (empty($name)) {
  944. return $preference; // All values
  945. } else if (array_key_exists($name, $preference)) {
  946. return $preference[$name]; // The single value
  947. } else {
  948. return $default; // Default value (or NULL)
  949. }
  950. }
  951. /// FUNCTIONS FOR HANDLING TIME ////////////////////////////////////////////
  952. /**
  953. * Given date parts in user time produce a GMT timestamp.
  954. *
  955. * @param int $year The year part to create timestamp of
  956. * @param int $month The month part to create timestamp of
  957. * @param int $day The day part to create timestamp of
  958. * @param int $hour The hour part to create timestamp of
  959. * @param int $minute The minute part to create timestamp of
  960. * @param int $second The second part to create timestamp of
  961. * @param float $timezone ?
  962. * @param bool $applydst ?
  963. * @return int timestamp
  964. * @todo Finish documenting this function
  965. */
  966. function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) {
  967. $strtimezone = NULL;
  968. if (!is_numeric($timezone)) {
  969. $strtimezone = $timezone;
  970. }
  971. $timezone = get_user_timezone_offset($timezone);
  972. if (abs($timezone) > 13) {
  973. $time = mktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year);
  974. } else {
  975. $time = gmmktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year);
  976. $time = usertime($time, $timezone);
  977. if($applydst) {
  978. $time -= dst_offset_on($time, $strtimezone);
  979. }
  980. }
  981. return $time;
  982. }
  983. /**
  984. * Given an amount of time in seconds, returns string
  985. * formatted nicely as weeks, days, hours etc as needed
  986. *
  987. * @uses MINSECS
  988. * @uses HOURSECS
  989. * @uses DAYSECS
  990. * @uses YEARSECS
  991. * @param int $totalsecs ?
  992. * @param array $str ?
  993. * @return string
  994. */
  995. function format_time($totalsecs, $str=NULL) {
  996. $totalsecs = abs($totalsecs);
  997. if (!$str) { // Create the str structure the slow way
  998. $str->day = get_string('day');
  999. $str->days = get_string('days');
  1000. $str->hour = get_string('hour');
  1001. $str->hours = get_string('hours');
  1002. $str->min = get_string('min');
  1003. $str->mins = get_string('mins');
  1004. $str->sec = get_string('sec');
  1005. $str->secs = get_string('secs');
  1006. $str->year = get_string('year');
  1007. $str->years = get_string('years');
  1008. }
  1009. $years = floor($totalsecs/YEARSECS);
  1010. $remainder = $totalsecs - ($years*YEARSECS);
  1011. $days = floor($remainder/DAYSECS);
  1012. $remainder = $totalsecs - ($days*DAYSECS);
  1013. $hours = floor($remainder/HOURSECS);
  1014. $remainder = $remainder - ($hours*HOURSECS);
  1015. $mins = floor($remainder/MINSECS);
  1016. $secs = $remainder - ($mins*MINSECS);
  1017. $ss = ($secs == 1) ? $str->sec : $str->secs;
  1018. $sm = ($mins == 1) ? $str->min : $str->mins;
  1019. $sh = ($hours == 1) ? $str->hour : $str->hours;
  1020. $sd = ($days == 1) ? $str->day : $str->days;
  1021. $sy = ($years == 1) ? $str->year : $str->years;
  1022. $oyears = '';
  1023. $odays = '';
  1024. $ohours = '';
  1025. $omins = '';
  1026. $osecs = '';
  1027. if ($years) $oyears = $years .' '. $sy;
  1028. if ($days) $odays = $days .' '. $sd;
  1029. if ($hours) $ohours = $hours .' '. $sh;
  1030. if ($mins) $omins = $mins .' '. $sm;
  1031. if ($secs) $osecs = $secs .' '. $ss;
  1032. if ($years) return trim($oyears .' '. $odays);
  1033. if ($days) return trim($odays .' '. $ohours);
  1034. if ($hours) return trim($ohours .' '. $omins);
  1035. if ($mins) return trim($omins .' '. $osecs);
  1036. if ($secs) return $osecs;
  1037. return get_string('now');
  1038. }
  1039. /**
  1040. * Returns a formatted string that represents a date in user time
  1041. * <b>WARNING: note that the format is for strftime(), not date().</b>
  1042. * Because of a bug in most Windows time libraries, we can't use
  1043. * the nicer %e, so we have to use %d which has leading zeroes.
  1044. * A lot of the fuss in the function is just getting rid of these leading
  1045. * zeroes as efficiently as possible.
  1046. *
  1047. * If parameter fixday = true (default), then take off leading
  1048. * zero from %d, else mantain it.
  1049. *
  1050. * @uses HOURSECS
  1051. * @param int $date timestamp in GMT
  1052. * @param string $format strftime format
  1053. * @param float $timezone
  1054. * @param bool $fixday If true (default) then the leading
  1055. * zero from %d is removed. If false then the leading zero is mantained.
  1056. * @return string
  1057. */
  1058. function userdate($date, $format='', $timezone=99, $fixday = true) {
  1059. global $CFG;
  1060. $strtimezone = NULL;
  1061. if (!is_numeric($timezone)) {
  1062. $strtimezone = $timezone;
  1063. }
  1064. if (empty($format)) {
  1065. $format = get_string('strftimedaydatetime');
  1066. }
  1067. if (!empty($CFG->nofixday)) { // Config.php can force %d not to be fixed.
  1068. $fixday = false;
  1069. } else if ($fixday) {
  1070. $formatnoday = str_replace('%d', 'DD', $format);
  1071. $fixday = ($formatnoday != $format);
  1072. }
  1073. $date += dst_offset_on($date, $strtimezone);
  1074. $timezone = get_user_timezone_offset($timezone);
  1075. if (abs($timezone) > 13) { /// Server time
  1076. if ($fixday) {
  1077. $datestring = strftime($formatnoday, $date);
  1078. $daystring = str_replace(' 0', '', strftime(' %d', $date));
  1079. $datestring = str_replace('DD', $daystring, $datestring);
  1080. } else {
  1081. $datestring = strftime($format, $date);
  1082. }
  1083. } else {
  1084. $date += (int)($timezone * 3600);
  1085. if ($fixday) {
  1086. $datestring = gmstrftime($formatnoday, $date);
  1087. $daystring = str_replace(' 0', '', gmstrftime(' %d', $date));
  1088. $datestring = str_replace('DD', $daystring, $datestring);
  1089. } else {
  1090. $datestring = gmstrftime($format, $date);
  1091. }
  1092. }
  1093. /// If we are running under Windows convert from windows encoding to UTF-8
  1094. /// (because it's impossible to specify UTF-8 to fetch locale info in Win32)
  1095. if ($CFG->ostype == 'WINDOWS') {
  1096. if ($localewincharset = get_string('localewincharset')) {
  1097. $textlib = textlib_get_instance();
  1098. $datestring = $textlib->convert($datestring, $localewincharset, 'utf-8');
  1099. }
  1100. }
  1101. return $datestring;
  1102. }
  1103. /**
  1104. * Given a $time timestamp in GMT (seconds since epoch),
  1105. * returns an array that represents the date in user time
  1106. *
  1107. * @uses HOURSECS
  1108. * @param int $time Timestamp in GMT
  1109. * @param float $timezone ?
  1110. * @return array An array that represents the date in user time
  1111. * @todo Finish documenting this function
  1112. */
  1113. function usergetdate($time, $timezone=99) {
  1114. $strtimezone = NULL;
  1115. if (!is_numeric($timezone)) {
  1116. $strtimezone = $timezone;
  1117. }
  1118. $timezone = get_user_timezone_offset($timezone);
  1119. if (abs($timezone) > 13) { // Server time
  1120. return getdate($time);
  1121. }
  1122. // There is no gmgetdate so we use gmdate instead
  1123. $time += dst_offset_on($time, $strtimezone);
  1124. $time += intval((float)$timezone * HOURSECS);
  1125. $datestring = gmstrftime('%S_%M_%H_%d_%m_%Y_%w_%j_%A_%B', $time);
  1126. list(
  1127. $getdate['seconds'],
  1128. $getdate['minutes'],
  1129. $getdate['hours'],
  1130. $getdate['mday'],
  1131. $getdate['mon'],
  1132. $getdate['year'],
  1133. $getdate['wday'],
  1134. $getdate['yday'],
  1135. $getdate['weekday'],
  1136. $getdate['month']
  1137. ) = explode('_', $datestring);
  1138. return $getdate;
  1139. }
  1140. /**
  1141. * Given a GMT timestamp (seconds since epoch), offsets it by
  1142. * the timezone. eg 3pm in India is 3pm GMT - 7 * 3600 seconds
  1143. *
  1144. * @uses HOURSECS
  1145. * @param int $date Timestamp in GMT
  1146. * @param float $timezone
  1147. * @return int
  1148. */
  1149. function usertime($date, $timezone=99) {
  1150. $timezone = get_user_timezone_offset($timezone);
  1151. if (abs($timezone) > 13) {
  1152. return $date;
  1153. }
  1154. return $date - (int)($timezone * HOURSECS);
  1155. }
  1156. /**
  1157. * Given a time, return the GMT timestamp of the most recent midnight
  1158. * for the current user.
  1159. *
  1160. * @param int $date Timestamp in GMT
  1161. * @param float $timezone ?
  1162. * @return ?
  1163. */
  1164. function usergetmidnight($date, $timezone=99) {
  1165. $userdate = usergetdate($date, $timezone);
  1166. // Time of midnight of this user's day, in GMT
  1167. return make_timestamp($userdate['year'], $userdate['mon'], $userdate['mday'], 0, 0, 0, $timezone);
  1168. }
  1169. /**
  1170. * Returns a string that prints the user's timezone
  1171. *
  1172. * @param float $timezone The user's timezone
  1173. * @return string
  1174. */
  1175. function usertimezone($timezone=99) {
  1176. $tz = get_user_timezone($timezone);
  1177. if (!is_float($tz)) {
  1178. return $tz;
  1179. }
  1180. if(abs($tz) > 13) { // Server time
  1181. return get_string('serverlocaltime');
  1182. }
  1183. if($tz == intval($tz)) {
  1184. // Don't show .0 for whole hours
  1185. $tz = intval($tz);
  1186. }
  1187. if($tz == 0) {
  1188. return 'UTC';
  1189. }
  1190. else if($tz > 0) {
  1191. return 'UTC+'.$tz;
  1192. }
  1193. else {
  1194. return 'UTC'.$tz;
  1195. }
  1196. }
  1197. /**
  1198. * Returns a float which represents the user's timezone difference from GMT in hours
  1199. * Checks various settings and picks the most dominant of those which have a value
  1200. *
  1201. * @uses $CFG
  1202. * @uses $USER
  1203. * @param float $tz If this value is provided and not equal to 99, it will be returned as is and no other settings will be checked
  1204. * @return int
  1205. */
  1206. function get_user_timezone_offset($tz = 99) {
  1207. global $USER, $CFG;
  1208. $tz = get_user_timezone($tz);
  1209. if (is_float($tz)) {
  1210. return $tz;
  1211. } else {
  1212. $tzrecord = get_timezone_record($tz);
  1213. if (empty($tzrecord)) {
  1214. return 99.0;
  1215. }
  1216. return (float)$tzrecord->gmtoff / HOURMINS;
  1217. }
  1218. }
  1219. /**
  1220. * Returns an int which represents the systems's timezone difference from GMT in seconds
  1221. * @param mixed $tz timezone
  1222. * @return int if found, false is timezone 99 or error
  1223. */
  1224. function get_timezone_offset($tz) {
  1225. global $CFG;
  1226. if ($tz == 99) {
  1227. return false;
  1228. }
  1229. if (is_numeric($tz)) {
  1230. return intval($tz * 60*60);
  1231. }
  1232. if (!$tzrecord = get_timezone_record($tz)) {
  1233. return false;
  1234. }
  1235. return intval($tzrecord->gmtoff * 60);
  1236. }
  1237. /**
  1238. * Returns a float or a string which denotes the user's timezone
  1239. * A float value means that a simple offset from GMT is used, while a string (it will be the name of a timezone in the database)
  1240. * means that for this timezone there are also DST rules to be taken into account
  1241. * Checks various settings and picks the most dominant of those which have a value
  1242. *
  1243. * @uses $USER
  1244. * @uses $CFG
  1245. * @param float $tz If this value is provided and not equal to 99, it will be returned as is and no other settings will be checked
  1246. * @return mixed
  1247. */
  1248. function get_user_timezone($tz = 99) {
  1249. global $USER, $CFG;
  1250. $timezones = array(
  1251. $tz,
  1252. isset($CFG->forcetimezone) ? $CFG->forcetimezone : 99,
  1253. isset($USER->timezone) ? $USER->timezone : 99,
  1254. isset($CFG->timezone) ? $CFG->timezone : 99,
  1255. );
  1256. $tz = 99;
  1257. while(($tz == '' || $tz == 99 || $tz == NULL) && $next = each($timezones)) {
  1258. $tz = $next['value'];
  1259. }
  1260. return is_numeric($tz) ? (float) $tz : $tz;
  1261. }
  1262. /**
  1263. * ?
  1264. *
  1265. * @uses $CFG
  1266. * @uses $db
  1267. * @param string $timezonename ?
  1268. * @return object
  1269. */
  1270. function get_timezone_record($timezonename) {
  1271. global $CFG, $db;
  1272. static $cache = NULL;
  1273. if ($cache === NULL) {
  1274. $cache = array();
  1275. }
  1276. if (isset($cache[$timezonename])) {
  1277. return $cache[$timezonename];
  1278. }
  1279. return $cache[$timezonename] = get_record_sql('SELECT * FROM '.$CFG->prefix.'timezone
  1280. WHERE name = '.$db->qstr($timezonename).' ORDER BY year DESC', true);
  1281. }
  1282. /**
  1283. * ?
  1284. *
  1285. * @uses $CFG
  1286. * @uses $USER
  1287. * @param ? $fromyear ?
  1288. * @param ? $to_year ?
  1289. * @return bool
  1290. */
  1291. function calculate_user_dst_table($from_year = NULL, $to_year = NULL, $strtimezone = NULL) {
  1292. global $CFG, $SESSION;
  1293. $usertz = get_user_timezone($strtimezone);
  1294. if (is_float($usertz)) {
  1295. // Trivial timezone, no DST
  1296. return false;
  1297. }
  1298. if (!empty($SESSION->dst_offsettz) && $SESSION->dst_offsettz != $usertz) {
  1299. // We have precalculated values, but the user's effective TZ has changed in the meantime, so reset
  1300. unset($SESSION->dst_offsets);
  1301. unset($SESSION->dst_range);
  1302. }
  1303. if (!empty($SESSION->dst_offsets) && empty($from_year) && empty($to_year)) {
  1304. // Repeat calls which do not request specific year ranges stop here, we have already calculated the table
  1305. // This will be the return path most of the time, pretty light computationally
  1306. return true;
  1307. }
  1308. // Reaching here means we either need to extend our table or create it from scratch
  1309. // Remember which TZ we calculated these changes for
  1310. $SESSION->dst_offsettz = $usertz;
  1311. if(empty($SESSION->dst_offsets)) {
  1312. // If we 're creating from scratch, put the two guard elements in there
  1313. $SESSION->dst_offsets = array(1 => NULL, 0 => NULL);
  1314. }
  1315. if(empty($SESSION->dst_range)) {
  1316. // If creating from scratch
  1317. $from = max((empty($from_year) ? intval(date('Y')) - 3 : $from_year), 1971);
  1318. $to = min((empty($to_year) ? intval(date('Y')) + 3 : $to_year), 2035);
  1319. // Fill in the array with the extra years we need to process
  1320. $yearstoprocess = array();
  1321. for($i = $from; $i <= $to; ++$i) {
  1322. $yearstoprocess[] = $i;
  1323. }
  1324. // Take note of which years we have processed for future calls
  1325. $SESSION->dst_range = array($from, $to);
  1326. }
  1327. else {
  1328. // If needing to extend the table, do the same
  1329. $yearstoprocess = array();
  1330. $from = max((empty($from_year) ? $SESSION->dst_range[0] : $from_year), 1971);
  1331. $to = min((empty($to_year) ? $SESSION->dst_range[1] : $to_year), 2035);
  1332. if($from < $SESSION->dst_range[0]) {
  1333. // Take note of which years we need to process and then note that we have processed them for future calls
  1334. for($i = $from; $i < $SESSION->dst_range[0]; ++$i) {
  1335. $yearstoprocess[] = $i;
  1336. }
  1337. $SESSION->dst_range[0] = $from;
  1338. }
  1339. if($to > $SESSION->dst_range[1]) {
  1340. // Take note of which years we need to process and then note that we have processed them for future calls
  1341. for($i = $SESSION->dst_range[1] + 1; $i <= $to; ++$i) {
  1342. $yearstoprocess[] = $i;
  1343. }
  1344. $SESSION->dst_range[1] = $to;
  1345. }
  1346. }
  1347. if(empty($yearstoprocess)) {
  1348. // This means that there was a call requesting a SMALLER range than we have already calculated
  1349. return true;
  1350. }
  1351. // From now on, we know that the array has at least the two guard elements, and $yearstoprocess has the years we need
  1352. // Also, the array is sorted in descending timestamp order!
  1353. // Get DB data
  1354. static $presets_cache = array();
  1355. if (!isset($presets_cache[$usertz])) {
  1356. $presets_cache[$usertz] = get_records('timezone', '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');
  1357. }
  1358. if(empty($presets_cache[$usertz])) {
  1359. return false;
  1360. }
  1361. // Remove ending guard (first elemen…

Large files files are truncated, but you can click here to view the full file