PageRenderTime 64ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/sloodle/lib/general.php

http://sloodle.googlecode.com/
PHP | 1123 lines | 560 code | 127 blank | 436 comment | 184 complexity | 288458d456f3001d17449c45e161f439 MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. /**
  3. * Sloodle general library.
  4. *
  5. * Provides various utility functionality for general Sloodle purposes.
  6. *
  7. * @package sloodle
  8. * @copyright Copyright (c) 2007-8 Sloodle (various contributors)
  9. * @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
  10. *
  11. * @contributor Edmund Edgar
  12. * @contributor Peter R. Bloomfield
  13. *
  14. */
  15. // This library expects that the Sloodle config file has already been included
  16. // (along with the Moodle libraries)
  17. /** Include our email functionality. */
  18. require_once(SLOODLE_LIBROOT.'/mail.php');
  19. /**
  20. * Force the user to login, but reject guest logins.
  21. * This function exists to workaround some Moodle 1.8 bugs.
  22. * @return void
  23. */
  24. function sloodle_require_login_no_guest()
  25. {
  26. global $CFG, $SESSION, $FULLME;
  27. // Attempt a direct login initially
  28. require_login(0, false);
  29. // Has the user been logged-in as a guest?
  30. if (isguestuser()) {
  31. // Make sure we can come back here after login
  32. $SESSION->wantsurl = $FULLME;
  33. // Redirect to the appropriate login page
  34. if (empty($CFG->loginhttps)) {
  35. redirect($CFG->wwwroot .'/login/index.php');
  36. } else {
  37. $wwwroot = str_replace('http:','https:', $CFG->wwwroot);
  38. redirect($wwwroot .'/login/index.php');
  39. }
  40. exit();
  41. }
  42. }
  43. /**
  44. * Sets a Sloodle configuration value.
  45. * This data will be stored in Moodle's "config" table, so it will persist even after Sloodle is uninstalled.
  46. * After being set, it will be available (read-only) as a named member of Moodle's $CFG variable.
  47. * <b>NOTE:</b> in Sloodle debug mode, this function will terminate the script with an error if the name is not prefixed with "sloodle_".
  48. * @param string $name The name of the value to be stored (should be prefixed with "sloodle_")
  49. * @param string $value The string representation of the value to be stored
  50. * @return bool True on success, or false on failure (may fail if database query encountered an error)
  51. * @see sloodle_get_config()
  52. */
  53. function sloodle_set_config($name, $value)
  54. {
  55. // If in debug mode, ensure the name is prefixed appropriately for Sloodle
  56. if (defined('SLOODLE_DEBUG') && SLOODLE_DEBUG) {
  57. if (substr_count($name, 'sloodle_') < 1) {
  58. exit ("ERROR: sloodle_set_config(..) called with invalid value name \"$name\". Expected \"sloodle_\" prefix.");
  59. }
  60. }
  61. // Use the standard Moodle config function, ignoring the 3rd parameter ("plugin", which defaults to NULL)
  62. return set_config(strtolower($name), $value);
  63. }
  64. /**
  65. * Gets a Sloodle configuration value from Moodle's "config" table.
  66. * This function does not necessarily need to be used.
  67. * All configuration data is available as named members of Moodle's $CFG global variable.
  68. * <b>NOTE:</b> in Sloodle debug mode, this function will terminate the script with an error if the name is not prefixed with "sloodle_".
  69. * @param string $name The name of the value to be stored (should be prefixed with "sloodle_")
  70. * @return mixed A string containing the configuration value, or false if the query failed (e.g. if the named value didn't exist)
  71. * @see sloodle_set_config()
  72. */
  73. function sloodle_get_config($name)
  74. {
  75. // If in debug mode, ensure the name is prefixed appropriately for Sloodle
  76. if (defined('SLOODLE_DEBUG') && SLOODLE_DEBUG) {
  77. if (substr_count($name, 'sloodle_') < 1) {
  78. exit ("ERROR: sloodle_get_config(..) called with invalid value name \"$name\". Expected \"sloodle_\" prefix.");
  79. }
  80. }
  81. // Use the Moodle config function, ignoring the plugin parameter
  82. $val = get_config(NULL, strtolower($name));
  83. // Older Moodle versions return a database record object instead of the value itself
  84. // Workaround:
  85. if (is_object($val)) return $val->value;
  86. return $val;
  87. }
  88. /**
  89. * Determines whether or not auto-registration is allowed for the site.
  90. * @return bool True if auto-reg is allowed on the site, or false otherwise.
  91. */
  92. function sloodle_autoreg_enabled_site()
  93. {
  94. return (bool)sloodle_get_config('sloodle_allow_autoreg');
  95. }
  96. /**
  97. * Determines whether or not auto-enrolment is allowed for the site.
  98. * @return bool True if auto-enrolment is allowed on the site, or false otherwise.
  99. */
  100. function sloodle_autoenrol_enabled_site()
  101. {
  102. return (bool)sloodle_get_config('sloodle_allow_autoenrol');
  103. }
  104. /**
  105. * Sends an XMLRPC message into Second Life.
  106. * @param string $channel A string containing a UUID identifying the XMLRPC channel in SL to be used
  107. * @param int $intval An integer value to be sent in the message
  108. * @param string $strval A string value to be sent in the message
  109. * @return bool True if successful, or false if an error occurs
  110. */
  111. function sloodle_send_xmlrpc_message($channel,$intval,$strval)
  112. {
  113. // Include our XMLRPC library
  114. require_once(SLOODLE_DIRROOT.'/lib/xmlrpc.inc');
  115. // Instantiate a new client object for communicating with Second Life
  116. $client = new xmlrpc_client("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi");
  117. // Construct the content of the RPC
  118. $content = '<?xml version="1.0"?><methodCall><methodName>llRemoteData</methodName><params><param><value><struct><member><name>Channel</name><value><string>'.$channel.'</string></value></member><member><name>IntValue</name><value><int>'.$intval.'</int></value></member><member><name>StringValue</name><value><string>'.$strval.'</string></value></member></struct></value></param></params></methodCall>';
  119. // Attempt to send the data via http
  120. $response = $client->send(
  121. $content,
  122. 60,
  123. 'http'
  124. );
  125. //var_dump($response); // Debug output
  126. // Make sure we got a response value
  127. if (!isset($response->val) || empty($response->val) || is_null($response->val)) {
  128. // Report an error if we are in debug mode
  129. if (defined('SLOODLE_DEBUG') && SLOODLE_DEBUG) {
  130. print '<p align="left">Not getting the expected XMLRPC response. Is Second Life broken again?<br/>';
  131. if (isset($response->errstr)) print "XMLRPC Error - ".$response->errstr;
  132. print '</p>';
  133. }
  134. return FALSE;
  135. }
  136. // Check the contents of the response value
  137. //if (defined('SLOODLE_DEBUG') && SLOODLE_DEBUG) {
  138. // print_r($response->val);
  139. //}
  140. //TODO: Check the details of the response to see if this was successful or not...
  141. return TRUE;
  142. }
  143. /**
  144. * Old logging function
  145. * @todo <b>May require update?</b>
  146. */
  147. function sloodle_add_to_log($courseid = null, $module = null, $action = null, $url = null, $cmid = null, $info = null)
  148. {
  149. global $CFG;
  150. // TODO: Make sure we set this in the calling function, then remove this bit
  151. if ($courseid == null) {
  152. $courseid = optional_param('sloodle_courseid',0,PARAM_RAW);
  153. }
  154. // if no action is specified, use the object name
  155. if ($action == null) {
  156. $action = $_SERVER['X-SecondLife-Object-Name'];
  157. }
  158. $region = $_SERVER['X-SecondLife-Region'];
  159. if ($info == null) {
  160. $info = $region;
  161. }
  162. $slurl = '';
  163. if (preg_match('/^(.*)\(.*?\)$/',$region,$matches)) { // strip the coordinates, eg. Cicero (123,123)
  164. $region = $matches[1];
  165. }
  166. $xyz = $_SERVER['X-SecondLife-Local-Position'];
  167. if (preg_match('/^\((.*?),(.*?),(.*?)\)$/',$xyz,$matches)) {
  168. $xyz = $matches[1].'/'.$matches[2].'/'.$matches[3];
  169. }
  170. return add_to_log($courseid, null, $action, $CFG->wwwroot.'/mod/sloodle/toslurl.php?region='.urlencode($region).'&xyz='.$xyz, $userid, $info );
  171. //return add_to_log($courseid, null, "ok", "ok", $userid, "ok");
  172. }
  173. /**
  174. * Determines whether or not Sloodle is installed.
  175. * Queries Moodle's modules table for a Sloodle entry.
  176. * <b>NOTE:</b> does not check for the presence of the Sloodle files.
  177. * @return bool True if Sloodle is installed, or false otherwise.
  178. */
  179. function sloodle_is_installed()
  180. {
  181. // Is there a Sloodle entry in the modules table?
  182. return record_exists('modules', 'name', 'sloodle');
  183. }
  184. /**
  185. * Generates a random login security token.
  186. * Uses mixed-case letters and numbers to generate a random 16-character string.
  187. * @return string
  188. * @see sloodle_random_web_password()
  189. */
  190. function sloodle_random_security_token()
  191. {
  192. // Define the characters we can use in our token, and get the length of it
  193. $str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  194. $strlen = strlen($str) - 1;
  195. // Prepare the token variable
  196. $token = '';
  197. // Loop once for each output character
  198. for($length = 0; $length < 16; $length++) {
  199. // Shuffle the string, then pick and store a random character
  200. $str = str_shuffle($str);
  201. $char = mt_rand(0, $strlen);
  202. $token .= $str[$char];
  203. }
  204. return $token;
  205. }
  206. /**
  207. * Generates a random web password
  208. * Uses mixed-case letters and numbers to generate a random 8-character string.
  209. * @return string
  210. * @see sloodle_random_security_token()
  211. */
  212. function sloodle_random_web_password()
  213. {
  214. // Define the characters we can use in our token, and get the length of it
  215. $str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  216. $strlen = strlen($str) - 1;
  217. // Prepare the password string
  218. $pwd = '';
  219. // Loop once for each output character
  220. for($length = 0; $length < 8; $length++) {
  221. // Shuffle the string, then pick and store a random character
  222. $str = str_shuffle($str);
  223. $char = mt_rand(0, $strlen);
  224. $pwd .= $str[$char];
  225. }
  226. return $pwd;
  227. }
  228. /**
  229. * Generates a random prim password (7 to 9 digit number).
  230. * @return string The password as a string
  231. */
  232. function sloodle_random_prim_password()
  233. {
  234. return (string)mt_rand(1000000, 999999999);
  235. }
  236. /**
  237. * Converts a string vector to an array vector.
  238. * String vector should be of format "<x,y,z>".
  239. * Converts to associative array with members 'x', 'y', and 'z'.
  240. * Returns false if input parameter was not of correct format.
  241. * @param string $vector A string vector of format "<x,y,z>".
  242. * @return mixed
  243. * @see sloodle_array_to_vector()
  244. * @see sloodle_round_vector()
  245. */
  246. function sloodle_vector_to_array($vector)
  247. {
  248. if (preg_match('/<(.*?),(.*?),(.*?)>/',$vector,$vectorbits)) {
  249. $arr = array();
  250. $arr['x'] = $vectorbits[1];
  251. $arr['y'] = $vectorbits[2];
  252. $arr['z'] = $vectorbits[3];
  253. return $arr;
  254. }
  255. return false;
  256. }
  257. /**
  258. * Converts an array vector to a string vector.
  259. * Array vector should be associative, containing elements 'x', 'y', and 'z'.
  260. * Converts to a string vector of format "<x,y,z>".
  261. * @return string
  262. * @see sloodle_vector_to_array()
  263. * @see sloodle_round_vector()
  264. */
  265. function sloodle_array_to_vector($arr)
  266. {
  267. $ret = '<'.$arr['x'].','.$arr['y'].','.$arr['z'].'>';
  268. return $ret;
  269. }
  270. /**
  271. * Obtains the identified course module instance database record.
  272. * @param int $id The integer ID of a course module instance
  273. * @return mixed A database record if successful, or false if it could not be found
  274. */
  275. function sloodle_get_course_module_instance($id)
  276. {
  277. return get_record('course_modules', 'id', $id);
  278. }
  279. /**
  280. * Determines whether or not the specified course module instance is visible.
  281. * Checks that the instance itself and the course section are both valid.
  282. * @param int $id The integer ID of a course module instance.
  283. * @return bool True if visible, or false if invisible or not found
  284. */
  285. function sloodle_is_course_module_instance_visible($id)
  286. {
  287. // Get the course module instance record, whether directly from the parameter, or from the database
  288. if (is_object($id)) {
  289. $course_module_instance = $id;
  290. } else if (is_int($id)) {
  291. if (!($course_module_instance = get_record('course_modules', 'id', $id))) return FALSE;
  292. } else return FALSE;
  293. // Make sure the instance itself is visible
  294. if ((int)$course_module_instance->visible == 0) return FALSE;
  295. // Find out which section it is in, and if that section is valid
  296. if (!($section = get_record('course_sections', 'id', $course_module_instance->section))) return FALSE;
  297. if ((int)$section->visible == 0) return FALSE;
  298. // Looks like the module is visible
  299. return TRUE;
  300. }
  301. /**
  302. * Determines if the specified course module instance is of the named type.
  303. * For example, this can check if a particular instance is a "forum" or a "chat".
  304. * @param int $id The integer ID of a course module instance
  305. * @param string $module_name Module type to check (must be the exact name of an installed module, e.g. 'sloodle' or 'quiz')
  306. * @return bool True if the module is of the specified type, or false otherwise
  307. */
  308. function sloodle_check_course_module_instance_type($id, $module_name)
  309. {
  310. // Get the record for the module type
  311. if (!($module_record = get_record('modules', 'name', $module_name))) return FALSE;
  312. // Get the course module instance record, whether directly from the parameter, or from the database
  313. if (is_object($id)) {
  314. $course_module_instance = $id;
  315. } else if (is_int($id)) {
  316. if (!($course_module_instance = get_record('course_modules', 'id', $id))) return FALSE;
  317. } else return FALSE;
  318. // Check the type of the instance
  319. return ($course_module_instance->module == $module_record->id);
  320. }
  321. /**
  322. * Obtains the ID number of the specified module (type not instance).
  323. * @param string $name The name of the module type to check, e.g. 'sloodle' or 'forum'
  324. * @return mixed Integer containing module ID, or false if it is not installed
  325. */
  326. function sloodle_get_module_id($name)
  327. {
  328. // Ensure the name is a non-empty string
  329. if (!is_string($name) || empty($name)) return FALSE;
  330. // Obtain the module record
  331. if (!($module_record = get_record('modules', 'name', $module_name))) return FALSE;
  332. return $module_record->id;
  333. }
  334. /**
  335. * Checks if the specified position is in the current (site-wide) loginzone.
  336. * @param mixed $pos A string vector or an associated array vector
  337. * @return bool True if position is in LoginZone, or false if not
  338. * @see sloodle_login_zone_coordinates()
  339. * @todo Update or remove... no longer valid
  340. */
  341. function sloodle_position_is_in_login_zone($pos)
  342. {
  343. // Get a position array from the parameter
  344. $posarr = NULL;
  345. if (is_array($pos) && count($pos) == 3) {
  346. $posarr = $pos;
  347. } else if (is_string($pos)) {
  348. $posarr = sloodle_vector_to_array($pos);
  349. } else {
  350. return FALSE;
  351. }
  352. // Fetch the loginzone boundaries
  353. list($maxarr,$minarr) = sloodle_login_zone_coordinates();
  354. // Make sure the position is not past the maximum bounds
  355. if ( ($posarr['x'] > $maxarr['x']) || ($posarr['y'] > $maxarr['y']) || ($posarr['z'] > $maxarr['z']) ) {
  356. return FALSE;
  357. }
  358. // Make sure the position is not past the minimum bounds
  359. if ( ($posarr['x'] < $minarr['x']) || ($posarr['y'] < $minarr['y']) || ($posarr['z'] < $minarr['z']) ) {
  360. return FALSE;
  361. }
  362. return TRUE;
  363. }
  364. /**
  365. * Generates teleport coordinates for a user who has already finished the LoginZone process.
  366. * @param string $pos A string vector giving the position of the LoginZone
  367. * @param string $size A string vector giving the size of the LoginZone
  368. * @return array, bool An associative array vector containing a teleport location, or false if the operation fails.
  369. */
  370. function sloodle_finished_login_coordinates($pos, $size)
  371. {
  372. // Make sure the parameters are valid types
  373. if (!is_string($pos) || !is_string($size)) {
  374. return FALSE;
  375. }
  376. // Convert both to arrays
  377. $posarr = sloodle_vector_to_array($pos);
  378. $sizearr = sloodle_vector_to_array($size);
  379. // Calculate a position just below the loginzone
  380. $coord = array();
  381. $coord['x'] = round($posarr['x'],0);
  382. $coord['y'] = round($posarr['y'],0);
  383. $coord['z'] = round(($posarr['z']-(($sizearr['z'])/2)-2),0);
  384. return $coord;
  385. }
  386. /**
  387. * Generates a random position within a cuboid zone of the specified size.
  388. * (Note: leaves a 2 metre margin round the outside)
  389. * @param array $size Associative array giving the size of the zone
  390. * @return array An associative vector array
  391. */
  392. function sloodle_random_position_in_zone($size)
  393. {
  394. // Construct the half-size array
  395. $halfsize = array('x'=>($size['x'] / 2.0) - 2.0, 'y'=>($size['y'] / 2.0) - 2.0, 'z'=>($size['z'] / 2.0) - 2.0);
  396. $pos = array();
  397. $pos['x'] = mt_rand(0.0, $size['x'] - 4.0) - $halfsize['x'];
  398. $pos['y'] = mt_rand(0.0, $size['y'] - 4.0) - $halfsize['y'];
  399. $pos['z'] = mt_rand(0.0, $size['z'] - 4.0) - $halfsize['z'];
  400. return $pos;
  401. }
  402. // Round the specified 3d vector to integer values
  403. // $pos should be a vector string "<x,y,z>" or an associative array {x,y,z}
  404. // Return is the same as the type passed-in
  405. // If the input type is unrecognised, it simply returns it back out unchanged
  406. /**
  407. * Rounds the specified 3d vector integer values.
  408. * Can handle/return a string vector, or an array vector.
  409. * (Output type matches input type).
  410. * @param mixed $pos Either a string vector or an array vector
  411. * @return mixed
  412. */
  413. function sloodle_round_vector($pos)
  414. {
  415. // We will work with an array, but allow for conversion to/from string
  416. $arrayvec = $pos;
  417. $returnstring = FALSE;
  418. // Is it a string?
  419. if (is_string($pos)) {
  420. $arrayvec = sloodle_vector_to_array($pos);
  421. $returnstring = TRUE;
  422. } else if (!is_array($pos)) {
  423. return $pos;
  424. }
  425. // Construct an output array
  426. $output = array();
  427. foreach ($arrayvec as $key => $val) {
  428. $output[$key] = round($val, 0);
  429. }
  430. // If we need to convert it back to a string, then do so
  431. if ($returnstring) {
  432. return sloodle_array_to_vector($output);
  433. }
  434. return $output;
  435. }
  436. /**
  437. * Calculates the maximum and minimum bounds of the specified LoginZone
  438. * Returns the bounds as a numeric array of two associate array vectors: ($max, $min).
  439. * (Or returns false if no LoginZone position/size could be found in the Moodle configuration table).
  440. * @param string $pos A string vector giving the position of the LoginZone
  441. * @param string $size A string vector giving the size of the LoginZone
  442. * @return array
  443. */
  444. function sloodle_login_zone_bounds($pos, $size)
  445. {
  446. // Make sure the parameters are valid types
  447. if (($pos == FALSE) || ($size == FALSE)) {
  448. return FALSE;
  449. }
  450. // Convert both to arrays
  451. $posarr = sloodle_vector_to_array($pos);
  452. $sizearr = sloodle_vector_to_array($size);
  453. // Calculate the bounds
  454. $max = array();
  455. $max['x'] = $posarr['x']+(($sizearr['x'])/2)-2;
  456. $max['y'] = $posarr['y']+(($sizearr['y'])/2)-2;
  457. $max['z'] = $posarr['z']+(($sizearr['z'])/2)-2;
  458. $min = array();
  459. $min['x'] = $posarr['x']-(($sizearr['x'])/2)+2;
  460. $min['y'] = $posarr['y']-(($sizearr['y'])/2)+2;
  461. $min['z'] = $posarr['z']-(($sizearr['z'])/2)+2;
  462. return array($max,$min);
  463. }
  464. /**
  465. * Checks if the given prim password is valid.
  466. * @param string $password The password string to check
  467. * @return bool True if it is valid, or false otherwise.
  468. */
  469. function sloodle_validate_prim_password($password)
  470. {
  471. // Check that it's a string
  472. if (!is_string($password)) return false;
  473. // Check the length
  474. $len = strlen($password);
  475. if ($len < 5 || $len > 9) return false;
  476. // Check that it's all numbers
  477. if (!ctype_digit($password)) return false;
  478. // Check that it doesn't start with a 0
  479. if ($password[0] == '0') return false;
  480. // It all seems fine
  481. return true;
  482. }
  483. /**
  484. * Checks if the given prim password is valid, and provides feedback.
  485. * An array is written to by reference, each element containing error codes.
  486. * Each error code is a word. The full text of the error message may be obtained
  487. * from the string file by looking for "primpass:errorcode".
  488. *
  489. * @param string $password The password to validate
  490. * @param array &$errors An array (passed by reference) which will contain any error messages
  491. * @return bool True if the prim password is valid, or false otherwise
  492. */
  493. function sloodle_validate_prim_password_verbose($password, &$errors)
  494. {
  495. // Initialise variables
  496. $errors = array();
  497. $result = true;
  498. // Check that it's a string
  499. if (!is_string($password)) {
  500. $errors[] = 'invalidtype';
  501. $result = false;
  502. }
  503. // Check the length
  504. $len = strlen($password);
  505. if ($len < 5) {
  506. $errors[] = 'tooshort';
  507. $result = false;
  508. }
  509. if ($len > 9) {
  510. $errors[] = 'toolong';
  511. $result = false;
  512. }
  513. // Check that it's all numbers
  514. if (!ctype_digit($password)) {
  515. $errors[] = 'numonly';
  516. $result = false;
  517. }
  518. // Check that it doesn't start with a 0
  519. if ($password[0] == '0') {
  520. $errors[] = 'leadingzero';
  521. $result = false;
  522. }
  523. return $result;
  524. }
  525. /**
  526. * Stores a pending login notification for an auto-registered user.
  527. * A cron job will process the pending notification queue.
  528. * @param string $destination Identifies the destination of the notification (for SL, this will be the object UUID. The send function will construct the email address)
  529. * @param string $avatar Identifier for the avatar being notified
  530. * @param string $username The username to notify the user of
  531. * @param string $password The (plaintext) password to notify the user of
  532. * @return bool True if successful, or false otherwise
  533. */
  534. function sloodle_login_notification($destination, $avatar, $username, $password)
  535. {
  536. // If another pending notification already exists for the same username, then delete it
  537. delete_records('sloodle_login_notifications', 'username', $username);
  538. // Add the new details
  539. $notification = new stdClass();
  540. $notification->destination = $destination;
  541. $notification->avatar = $avatar;
  542. $notification->username = $username;
  543. $notification->password = $password;
  544. return (bool)insert_record('sloodle_login_notifications', $notification);
  545. }
  546. /**
  547. * Send a login notification.
  548. * @param string $destination Identifies the destination of the notification (for SL, this will be the object UUID. The target email address will be constructed)
  549. * @param string $avatar Identifier for the avatar being notified
  550. * @param string $username The username to notify the user of
  551. * @param string $password The (plaintext) password to notify the user of
  552. * @return bool True if successful, or false otherwise
  553. */
  554. function sloodle_send_login_notification($destination, $avatar, $username, $password)
  555. {
  556. global $CFG;
  557. return sloodle_text_email_sl($destination, 'SLOODLE_LOGIN', "$avatar|{$CFG->wwwroot}|$username|$password");
  558. }
  559. /**
  560. * Processes pending login notifications, up to a certain limit.
  561. * Retrieves the requests one-at-a-time for processing.
  562. * This is slower, but ensures minimal damage if the process is terminated, e.g. due to server timeout.
  563. * @param int $limit The maximum number of pending requests to process.
  564. * @return void
  565. */
  566. function sloodle_process_login_notifications($limit = 25)
  567. {
  568. global $CFG;
  569. // Validate the limit
  570. $limit = (int)$limit;
  571. if ($limit < 1) return;
  572. // Go through each one
  573. for ($i = 0; $i < $limit; $i++) {
  574. // Obtain the first record
  575. $recs = get_records('sloodle_login_notifications', '', '', 'id', '*', 0, $limit);
  576. if (!$recs) return false;
  577. reset($recs);
  578. $rec = current($recs);
  579. // Determine the user ID of the person who requested this
  580. $userid = 0;
  581. if (!($sloodleuser = get_record('sloodle_users', 'uuid', $rec->avatar))) {
  582. // Failed to the user - get the guest user instead
  583. $guestdata = guest_user();
  584. $userid = $guestdata->id;
  585. } else {
  586. // Got the data - store the user ID
  587. $userid = $sloodleuser->userid;
  588. }
  589. // Send the notification
  590. if (sloodle_send_login_notification($rec->destination, $rec->avatar, $rec->username, $rec->password)) {
  591. // Log the notification
  592. add_to_log(SITEID, 'sloodle', 'view', '', 'Sent login details by email to avatar in-world', 0, $userid);
  593. } else {
  594. // Log the failed notification (but don't keep trying the same one)
  595. add_to_log(SITEID, 'sloodle', 'view failed', '', 'Failed to send login details by email to avatar in-world', 0, $userid);
  596. }
  597. // Delete the record from the data
  598. delete_records('sloodle_login_notifications', 'id', $rec->id);
  599. }
  600. }
  601. /**
  602. * Extracts a value from a name-value associative array if it is set.
  603. * (The array should associate name to value).
  604. * @param array $settings The array of names and values
  605. * @param string $name The name of the value to retrieve
  606. * @param mixed $default The default value to return if the specified value was not found
  607. * @return mixed The value from the input array, or the $default parameter
  608. */
  609. function sloodle_get_value($settings, $name, $default = null)
  610. {
  611. if (is_array($settings) && isset($settings[$name])) return $settings[$name];
  612. return $default;
  613. }
  614. /**
  615. * Outputs the standard form elements for access levels in object configuration.
  616. * Each part can be optionally hidden, and default values can be provided.
  617. * (Note: the server access level must be communicated from the object back to Moodle... rubbish implementation, but it works!)
  618. * @param array $current_config An associative array of setting names to values, containing defaults. (Ignored if null).
  619. * @param bool $show_use_object Determines whether or not the "Use Object" setting is shown
  620. * @param bool $show_control_object Determines whether or not the "Control Object" setting is shown
  621. * @param bool $show_server Determines whether or not the server access setting is shown
  622. * @return void
  623. */
  624. function sloodle_print_access_level_options($current_config, $show_use_object = true, $show_control_object = true, $show_server = true)
  625. {
  626. // Quick-escape: if everything is being suppressed, then do nothing
  627. if (!($show_use_object || $show_control_object || $show_server)) return;
  628. // Fetch default values from the configuration, if possible
  629. $sloodleobjectaccessleveluse = sloodle_get_value($current_config, 'sloodleobjectaccessleveluse', SLOODLE_OBJECT_ACCESS_LEVEL_PUBLIC);
  630. $sloodleobjectaccesslevelctrl = sloodle_get_value($current_config, 'sloodleobjectaccesslevelctrl', SLOODLE_OBJECT_ACCESS_LEVEL_OWNER);
  631. $sloodleserveraccesslevel = sloodle_get_value($current_config, 'sloodleserveraccesslevel', SLOODLE_SERVER_ACCESS_LEVEL_PUBLIC);
  632. // Define our object access level array
  633. $object_access_levels = array( SLOODLE_OBJECT_ACCESS_LEVEL_PUBLIC => get_string('accesslevel:public','sloodle'),
  634. SLOODLE_OBJECT_ACCESS_LEVEL_GROUP => get_string('accesslevel:group','sloodle'),
  635. SLOODLE_OBJECT_ACCESS_LEVEL_OWNER => get_string('accesslevel:owner','sloodle') );
  636. // Define our server access level array
  637. $server_access_levels = array( SLOODLE_SERVER_ACCESS_LEVEL_PUBLIC => get_string('accesslevel:public','sloodle'),
  638. SLOODLE_SERVER_ACCESS_LEVEL_COURSE => get_string('accesslevel:course','sloodle'),
  639. SLOODLE_SERVER_ACCESS_LEVEL_SITE => get_string('accesslevel:site','sloodle'),
  640. SLOODLE_SERVER_ACCESS_LEVEL_STAFF => get_string('accesslevel:staff','sloodle') );
  641. // Display box and a heading
  642. print_box_start('generalbox boxaligncenter');
  643. echo '<h3>'.get_string('accesslevel','sloodle').'</h3>';
  644. // Print the object settings
  645. if ($show_use_object || $show_control_object) {
  646. // Object access
  647. echo '<b>'.get_string('accesslevelobject','sloodle').'</b><br><i>'.get_string('accesslevelobject:desc','sloodle').'</i><br><br>';
  648. // Use object
  649. if ($show_use_object) {
  650. echo get_string('accesslevelobject:use','sloodle').': ';
  651. choose_from_menu($object_access_levels, 'sloodleobjectaccessleveluse', $sloodleobjectaccessleveluse, '');
  652. echo '<br><br>';
  653. }
  654. // Control object
  655. if ($show_control_object) {
  656. echo get_string('accesslevelobject:control','sloodle').': ';
  657. choose_from_menu($object_access_levels, 'sloodleobjectaccesslevelctrl', $sloodleobjectaccesslevelctrl, '');
  658. echo '<br><br>';
  659. }
  660. }
  661. // Print the server settings
  662. if ($show_server) {
  663. // Server access
  664. echo '<b>'.get_string('accesslevelserver','sloodle').'</b><br><i>'.get_string('accesslevelserver:desc','sloodle').'</i><br><br>';
  665. echo get_string('accesslevel','sloodle').': ';
  666. choose_from_menu($server_access_levels, 'sloodleserveraccesslevel', $sloodleserveraccesslevel, '');
  667. echo '<br>';
  668. }
  669. print_box_end();
  670. }
  671. function sloodle_access_level_option_choice($option, $current_config, $show, $prefix = '', $suffix = '') {
  672. $access_levels = array();
  673. if ($option == 'sloodleserveraccesslevel') {
  674. $access_levels = array( SLOODLE_SERVER_ACCESS_LEVEL_PUBLIC => get_string('accesslevel:public','sloodle'),
  675. SLOODLE_SERVER_ACCESS_LEVEL_COURSE => get_string('accesslevel:course','sloodle'),
  676. SLOODLE_SERVER_ACCESS_LEVEL_SITE => get_string('accesslevel:site','sloodle'),
  677. SLOODLE_SERVER_ACCESS_LEVEL_STAFF => get_string('accesslevel:staff','sloodle')
  678. );
  679. } else {
  680. $access_levels = array( SLOODLE_OBJECT_ACCESS_LEVEL_PUBLIC => get_string('accesslevel:public','sloodle'),
  681. SLOODLE_OBJECT_ACCESS_LEVEL_GROUP => get_string('accesslevel:group','sloodle'),
  682. SLOODLE_OBJECT_ACCESS_LEVEL_OWNER => get_string('accesslevel:owner','sloodle')
  683. );
  684. }
  685. $defaults = array(
  686. 'sloodleobjectaccessleveluse' => SLOODLE_OBJECT_ACCESS_LEVEL_PUBLIC,
  687. 'sloodleobjectaccesslevelctrl' => SLOODLE_OBJECT_ACCESS_LEVEL_OWNER,
  688. 'sloodleserveraccesslevel' => SLOODLE_SERVER_ACCESS_LEVEL_PUBLIC
  689. );
  690. // Fetch default values from the configuration, if possible
  691. $selected_value = sloodle_get_value($current_config, $option, $defaults[$option]);
  692. if ($show) {
  693. return choose_from_menu($access_levels, $prefix.$option.$suffix, $selected_value, '', '', 0, $return = true);
  694. } else {
  695. return '&nbsp;';
  696. }
  697. }
  698. /**
  699. * Returns a very approximate natural language description of a period of time (in minutes, hours, days, or weeks).
  700. * Can also be used to describe how long ago something happened, in which case anything less than 1 minute is treated as 'now'.
  701. * @param int $secs Number of seconds in period of time
  702. * @param bool $ago If true (not default), then the time will be described in past tense, e.g. "3 days ago", as opposed to simply "3 days".
  703. * @return string
  704. */
  705. function sloodle_describe_approx_time($secs, $ago = false)
  706. {
  707. // Make sure the time is a positive integer
  708. $secs = (int)$secs;
  709. if ($secs < 0) $secs *= -1;
  710. // Less than a minute
  711. if ($secs < 60) {
  712. // If we are describing a past time, then approximate to 'now'
  713. if ($ago) return ucwords(get_string('now', 'sloodle'));
  714. // Give the number of seconds
  715. if ($secs == 1) return '1 '. get_string('second', 'sloodle');
  716. return $secs.' '. get_string('seconds', 'sloodle');
  717. }
  718. // This variable will hold the time description
  719. $desc = '';
  720. // Roughly 1 minute
  721. if ($secs < 120) $desc = '1 '. get_string('minute', 'sloodle');
  722. // Several minutes (up to 1 hour)
  723. else if ($secs < 3600) $desc = ((string)(int)($secs / 60)).' '. get_string('minutes', 'sloodle');
  724. // Roughly 1 hour
  725. else if ($secs < 7200) $desc = '1 '. get_string('hour', 'sloodle');
  726. // Several hours (up to 1 day)
  727. else if ($secs < 86400) $desc = ((string)(int)($secs / 3600)).' '. get_string('hours', 'sloodle');
  728. // Roughly 1 day
  729. else if ($secs < 172800) $desc = '1 '. get_string('day', 'sloodle');
  730. // Several days (up to 1 week)
  731. else if ($secs < 604800) $desc = ((string)(int)($secs / 86400)).' '. get_string('days', 'sloodle');
  732. // Roughly 1 week
  733. else if ($secs < 1209600) $desc = '1 '. get_string('week', 'sloodle');
  734. // Several weeks (up to 2 months)
  735. else if ($secs < 5184000) $desc = ((string)(int)($secs / 604800)).' '. get_string('weeks', 'sloodle');
  736. // Several months (up to 11 months)
  737. else if ($secs < 29462400) $desc = ((string)(int)($secs / 2592000)).' '. get_string('months', 'sloodle');
  738. // 1 year
  739. else if ($secs < 63072000) $desc = '1 '. get_string('year', 'sloodle');
  740. // Several years
  741. else $desc = ((string)(int)($secs / 31536000)).' '. get_string('years', 'sloodle');
  742. // Add 'ago' if necessary
  743. if ($ago) return get_string('timeago', 'sloodle', $desc);
  744. return $desc;
  745. }
  746. /**
  747. * Gets the basic URL of the current web-page being accessed.
  748. * Includes the protocol, hostname, and script path/name.
  749. * @return string
  750. */
  751. function sloodle_get_web_path()
  752. {
  753. // Check for the protocol
  754. if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off') $protocol = "http";
  755. else $protocol = "https";
  756. // Get the host name (e.g. domain)
  757. $host = $_SERVER['SERVER_NAME'];
  758. // Finally, get the script path/name
  759. $file = $_SERVER['SCRIPT_NAME'];
  760. return $protocol.'://'.$host.$file;
  761. }
  762. /**
  763. * Gets an array of subdirectories within the given directory.
  764. * Ignores anything which starts with a .
  765. * @param string $dir The directory to search WITHOUT a trailing slash. (Note: cannot search the current directory or higher in the file hierarchy)
  766. * @param bool $relative If TRUE (default) the array of results will be relative to the input directory. Otherwise, they will include the input directory path.
  767. * @return array|false A numeric array of subdirectory names sorted alphabetically, or false if an error occurred (such as the input value not being a directory)
  768. */
  769. function sloodle_get_subdirectories($dir, $relative = true)
  770. {
  771. // Make sure we have a valid directory
  772. if (empty($dir)) return false;
  773. // Open the directory
  774. if (!is_dir($dir)) return false;
  775. if (!$dh = opendir($dir)) return false;
  776. // Go through each item
  777. $output = array();
  778. while (($file = readdir($dh)) !== false) {
  779. // Ignore anything starting with a . and anything which isn't a directory
  780. if (strpos($file, '.') == 0) continue;
  781. $filetype = @filetype($dir.'/'.$file);
  782. if (empty($filetype) || $filetype != 'dir') continue;
  783. // Store it
  784. if ($relative) $output[] = $file;
  785. else $output[] = $dir.'/'.$file;
  786. }
  787. closedir($dh);
  788. natcasesort($output);
  789. return $output;
  790. }
  791. /**
  792. * Gets an array of files within the given directory.
  793. * Ignores anything which starts with a .
  794. * @param string $dir The directory to search WITHOUT a trailing slash. (Note: cannot search the current directory or higher in the file hierarchy)
  795. * @param bool $relative If TRUE (default) the array of results will be relative to the input directory. Otherwise, they will include the input directory path.
  796. * @return array|false A numeric array of file names sorted alphabetically, or false if an error occurred (such as the input value not being a directory)
  797. */
  798. function sloodle_get_files($dir, $relative = true)
  799. {
  800. // Make sure we have a valid directory
  801. if (empty($dir)) return false;
  802. // Open the directory
  803. if (!is_dir($dir)) return false;
  804. if (!$dh = opendir($dir)) return false;
  805. // Go through each item
  806. $output = array();
  807. while (($file = readdir($dh)) !== false) {
  808. // Ignore anything starting with a . and anything which isn't a file
  809. if (strpos($file, '.') == 0) continue;
  810. $filetype = @filetype($dir.'/'.$file);
  811. if (empty($filetype) || $filetype != 'file') continue;
  812. // Store it
  813. if ($relative) $output[] = $file;
  814. else $output[] = $dir.'/'.$file;
  815. }
  816. closedir($dh);
  817. natcasesort($output);
  818. return $output;
  819. }
  820. /**
  821. * Extracts the object name and version number from an object identifier.
  822. * @param string $objid An object identifier, such as "chat-1.0"
  823. * @return array A numeric array of name then version number.
  824. */
  825. function sloodle_parse_object_identifier($objid)
  826. {
  827. // Find the last dash character, and split the string around it.
  828. $lastpos = strrpos($objid, '-');
  829. // Check for common problems
  830. if ($lastpos === false) return array($objid, ''); // No dash
  831. if ($lastpos == 0) return array('', substr($objid, 1)); // Dash at start
  832. if ($lastpos == (strlen($objid) - 1)) return array(substr($objid, 0, -1), ''); // Dash at end
  833. // Split up the values
  834. $name = substr($objid, 0, $lastpos);
  835. $version = substr($objid, $lastpos + 1, strlen($objid) - $lastpos - 1);
  836. return array($name, $version);
  837. }
  838. /**
  839. * Gets all object types and versions available in this installation.
  840. * Creates a 2-dimensional associative array.
  841. * The top level is the object name, and the second is the object version (both as strings).
  842. * The associated value is the path to the configuration form script, or boolean false
  843. * if the object has no configuration options.
  844. * @return array|false Returns a 2d associative array if successful, or false if an error occurs
  845. */
  846. function sloodle_get_installed_object_types()
  847. {
  848. // Fetch all sub-directories of the "mod" directory
  849. $MODPATH = SLOODLE_DIRROOT.'/mod';
  850. $dirs = sloodle_get_subdirectories($MODPATH, true);
  851. if (!$dirs) return false;
  852. // Go through each object to parse names and version numbers.
  853. // Object names should have format "name-version" (e.g. "chat-1.0").
  854. // We will skip anything that does not match this format.
  855. // We will also skip anything with a "noshow" file in the folder.
  856. $mods = array();
  857. foreach ($dirs as $d) {
  858. if (empty($d)) continue;
  859. // Parse the object identifier
  860. list($name, $version) = sloodle_parse_object_identifier($d);
  861. if (empty($name) || empty($version)) continue;
  862. // Check if there's a noshow file
  863. if (file_exists("{$MODPATH}/{$d}/noshow")) continue;
  864. // Check if this object has a configuration script
  865. $cfgscript = "$MODPATH/$d/object_config.php";
  866. if (file_exists($cfgscript)) {
  867. $mods[$name][$version] = $cfgscript;
  868. } else {
  869. $mods[$name][$version] = false;
  870. }
  871. }
  872. // Sort the array by name of the object
  873. ksort($mods);
  874. return $mods;
  875. }
  876. /**
  877. * Render a page viewing a particular feature, or a SLOODLE module.
  878. * Outputs error text in SLOODLE debug mode.
  879. * @param string $feature The name of a feature to view ("course", "user", "users"), or "module" to indicate that we are viewing some kind of module. Note: features should contain only alphanumric characters.
  880. * @return bool True if successful, or false if not.
  881. */
  882. function sloodle_view($feature)
  883. {
  884. global $CFG, $USER;
  885. // Make sure the parameter is safe -- nothing but alphanumeric characters.
  886. if (!ctype_alnum($feature)) {
  887. sloodle_debug('sloodle_view(..): Invalid characters in view feature, "'.$feature.'"');
  888. return false;
  889. }
  890. if (empty($feature)) {
  891. sloodle_debug('sloodle_view(..): No feature name specified.');
  892. return false;
  893. }
  894. $feature = trim($feature);
  895. // Has a module been requested?
  896. if (strcasecmp($feature, 'module') == 0) {
  897. // We should have an ID parameter, indicating which module has been requested
  898. $id = required_param('id', PARAM_INT);
  899. // Query the database for the SLOODLE module sub-type
  900. $instanceid = get_field('course_modules', 'instance', 'id', $id);
  901. if ($instanceid === false) error('Course module instance '.$id.' not found.');
  902. $type = get_field('sloodle', 'type', 'id', $instanceid);
  903. if ($type === false) error('SLOODLE module instance '.$instanceid.' not found.');
  904. // We will just use the type as a feature name now.
  905. // This means the following words are unavailable as module sub-types: course, user, users
  906. $feature = $type;
  907. }
  908. // Attempt to include the relevant viewing class
  909. $filename = SLOODLE_DIRROOT."/view/{$feature}.php";
  910. if (!file_exists($filename)) {
  911. error("SLOODLE file not found: view/{$feature}.php");
  912. exit();
  913. }
  914. require_once($filename);
  915. // Create and execute the viewing instance
  916. $classname = 'sloodle_view_'.$feature;
  917. if (!class_exists($classname)) {
  918. error("SLOODLE class missing: {$classname}");
  919. exit();
  920. }
  921. $viewer = new $classname();
  922. $viewer->view();
  923. return true;
  924. }
  925. /**
  926. * Returns the given string, 'cleaned' and ready for output to SL as UTF-8.
  927. * Removes tags and slash-characters.
  928. * @param string str The string to clean.
  929. * @return string
  930. */
  931. function sloodle_clean_for_output($str)
  932. {
  933. return strip_tags(stripcslashes(@html_entity_decode($str, ENT_QUOTES, 'UTF-8')));
  934. }
  935. /**
  936. * Returns the given string, 'cleaned' and ready for storage in the database.
  937. * Note: removes tags and slash-characters.
  938. * @param string str The string to clean.
  939. * @return string
  940. */
  941. function sloodle_clean_for_db($str)
  942. {
  943. return htmlentities($str, ENT_QUOTES, 'UTF-8');
  944. }
  945. /**
  946. * Converts a shorthand file size to a number of bytes, if necessary.
  947. * This follows PHP shorthand, with K for Kilobytes, M for Megabytes, and G for Gigabytes.
  948. * @param string size The shorthand size to conert
  949. * @return integer The size specified in bytes
  950. */
  951. function sloodle_convert_file_size_shorthand($size)
  952. {
  953. $size = trim($size);
  954. $num = (int)$size;
  955. $char = strtolower($size{strlen($size)-1});
  956. switch ($char)
  957. {
  958. case 'g': $num *= 1024;
  959. case 'm': $num *= 1024;
  960. case 'k': $num *= 1024;
  961. }
  962. return $num;
  963. }
  964. /**
  965. * Converts a file size to plain text.
  966. * For example, will convert "1024" to "1 kilobyte".
  967. * @param integer|string size If an integer, then it is the number of bytes. If a string, then it can be PHP shorthand, such as "1M" for 1 megabyte.
  968. * @return string A text string describing the specified size.
  969. */
  970. function sloodle_get_size_description($size)
  971. {
  972. // Make sure we have a number of bytes
  973. $bytes = 0;
  974. if (is_int($size)) $bytes = $size;
  975. else $bytes = sloodle_convert_file_size_shorthand($size);
  976. $desc = '';
  977. // Keep the number small by going with the largest possible units
  978. if ($bytes >= 1073741824) $desc = ($bytes / 1073741824)." GB";
  979. else if ($bytes >= 1048576) $desc = ($bytes / 1048576). " MB";
  980. else if ($bytes >= 1024) $desc = ($bytes / 1024). " KB";
  981. else $desc = $bytes . " bytes";
  982. return $desc;
  983. }
  984. /**
  985. * Gets the maximum size of a file (in bytes) that can be uploaded using POST.
  986. * @return integer
  987. */
  988. function sloodle_get_max_post_upload()
  989. {
  990. // Get the sizes of the relevant limits
  991. $upload_max_filesize = sloodle_convert_file_size_shorthand(ini_get('upload_max_filesize'));
  992. $post_max_size = sloodle_convert_file_size_shorthand(ini_get('post_max_size'));
  993. // Use the smaller limit
  994. return min($upload_max_filesize, $post_max_size);
  995. }
  996. ?>