module_choice.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. <?php
  2. // This file is part of the Sloodle project (www.sloodle.org)
  3. /**
  4. * This file defines a choice module for Sloodle.
  5. *
  6. * @package sloodle
  7. * @copyright Copyright (c) 2008 Sloodle (various contributors)
  8. * @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
  9. *
  10. * @contributor Peter R. Bloomfield
  11. */
  12. /** The Sloodle module base. */
  13. require_once(SLOODLE_LIBROOT.'/modules/module_base.php');
  14. /** General Sloodle functions. */
  15. require_once(SLOODLE_LIBROOT.'/general.php');
  16. /** Include the standard Moodle choice module library. */
  17. require_once($CFG->dirroot.'/mod/choice/lib.php');
  18. /**
  19. * The Sloodle choice module class.
  20. * @package sloodle
  21. */
  22. class SloodleModuleChoice extends SloodleModule
  23. {
  24. // DATA //
  25. /**
  26. * Internal for Moodle only - course module instance.
  27. * Corresponds to one record from the Moodle 'course_modules' table.
  28. * @var object
  29. * @access private
  30. */
  31. var $cm = null;
  32. /**
  33. * Internal only - Moodle choice module instance database object.
  34. * Corresponds to one record from the Moodle 'choice' table.
  35. * @var object
  36. * @access private
  37. */
  38. var $moodle_choice_instance = null;
  39. /**
  40. * The number of (non-admin) users on the course who have not yet answered this choice.
  41. * @var int
  42. * @access private
  43. */
  44. var $numunanswered = 0;
  45. /**
  46. * The options available for this choice, as an associative array of IDs to {@link SloodleChoiceOption} objects.
  47. * @var array
  48. * @access public
  49. */
  50. var $options = array();
  51. // FUNCTIONS //
  52. /**
  53. * Constructor
  54. */
  55. function SloodleModuleChoice(&$_session)
  56. {
  57. $constructor = get_parent_class($this);
  58. parent::$constructor($_session);
  59. }
  60. /**
  61. * Loads data from the database.
  62. * Note: even if the function fails, it may still have overwritten some or all existing data in the object.
  63. * @param mixed $id The site-wide unique identifier for all modules. Type depends on VLE. On Moodle, it is an integer course module identifier ('id' field of 'course_modules' table)
  64. * @return bool True if successful, or false otherwise
  65. */
  66. function load($id)
  67. {
  68. // Make sure the ID is valid
  69. $id = (int)$id;
  70. if ($id <= 0) return false;
  71. // Fetch the course module data
  72. if (!($this->cm = get_coursemodule_from_id('choice', $id))) {
  73. sloodle_debug("Failed to load course module instance #$id.<br/>");
  74. return false;
  75. }
  76. // Make sure the module is visible
  77. if ($this->cm->visible == 0) {
  78. sloodle_debug("Error: course module instance #$id not visible.<br/>");
  79. return false;
  80. }
  81. // Load from the primary table: choice instance
  82. if (!($this->moodle_choice_instance = sloodle_get_record('choice', 'id', $this->cm->instance))) {
  83. sloodle_debug("Failed to load choice with instance ID #{$cm->instance}.<br/>");
  84. return false;
  85. }
  86. // Fetch options
  87. $this->options = array();
  88. if ($options = sloodle_get_records('choice_options', 'choiceid', $this->moodle_choice_instance->id)) {
  89. // Get response data (this uses the standard choice function, in "moodle/mod/choice/lib.php")
  90. $allresponses = choice_get_response_data($this->moodle_choice_instance, $this->cm, 0);
  91. foreach ($options as $opt) {
  92. // Create our option object and add our data
  93. $this->options[$opt->id] = new SloodleChoiceOption();
  94. $this->options[$opt->id]->id = $opt->id;
  95. $this->options[$opt->id]->text = $opt->text;
  96. $this->options[$opt->id]->maxselections = $opt->maxanswers;
  97. $this->options[$opt->id]->timemodified = (int)$opt->timemodified;
  98. // Count the number of selections made
  99. $numsels = 0;
  100. if (isset($allresponses[$opt->id])) $numsels = count($allresponses[$opt->id]);
  101. $this->options[$opt->id]->numselections = $numsels;
  102. }
  103. }
  104. //$users = get_course_users($this->cm->course); // Deprecated since 1.7. Hope this works instead:
  105. $context = get_context_instance(CONTEXT_COURSE, $this->cm->course);
  106. $users = get_users_by_capability($context, 'mod/sloodle:courseparticipate', 'u.id', '','','',array(), false);
  107. // Determine how many people on the course have not yet answered
  108. if (!is_array($users)) $users = array();
  109. $num_users = count($users);
  110. $numanswers = (int)sloodle_count_records('choice_answers', 'choiceid', $this->moodle_choice_instance->id);
  111. $this->numunanswered = max(0, $num_users - $numanswers);
  112. return true;
  113. }
  114. /**
  115. * Selects an option in this choice on behalf of the specified user.
  116. * Logs the user in to the VLE if necessary.
  117. * If a general error occurs, FALSE will be returned.
  118. * Otherwise, an integer {@link http://slisweb.sjsu.edu/sl/index.php/Sloodle_status_codes status code} will be returned.
  119. * The following status codes are typical responses:
  120. * * 10011 = added new choice selection
  121. * * 10012 = updated existing choice selection
  122. * * 10013 = user previously selected same option
  123. * * -10011 = User already made a selection, and re-selection is not allowed
  124. * * -10012 = max number of selections for this option already made
  125. * * -10013 = choice is not yet open
  126. * * -10014 = choice is already closed
  127. *
  128. * @param mixed $optionid The unique site-wide identifier of the option to be selected
  129. * @param mixed $user A SloodleUser identifying the current user; if omitted, the current {@link SloodleSession} will be used.
  130. * @return integer|false
  131. */
  132. function select_option($optionid, $user = null)
  133. {
  134. // Fetch a user if necessary
  135. if ($user === null) {
  136. // Make sure we have a session user
  137. if (!isset($this->_session->user)) return false;
  138. $user = $this->_session->user;
  139. }
  140. // Make sure we have a user loaded and logged-in
  141. if (!$user->is_user_loaded()) return false;
  142. if (!$user->login()) return false;
  143. // Make sure the user is permitted to select from this choice
  144. if (!has_capability('mod/choice:choose', get_context_instance(CONTEXT_MODULE, $this->cm->id))) return -331;
  145. // Make sure the choice is open
  146. if ($this->is_early()) return -10013;
  147. if ($this->is_late()) return -10014;
  148. // Has the user already made a selection for this choice?
  149. $update_selection = false;
  150. $previous_selection = sloodle_get_record('choice_answers', 'choiceid', $this->moodle_choice_instance->id, 'userid', $user->get_user_id());
  151. if ($previous_selection) {
  152. // Was it a selection of the same option?
  153. if ($previous_selection->optionid == $optionid) {
  154. // Yes - that's fine. Nothing to do.
  155. return 10013;
  156. }
  157. // No - are re-selections allowed?
  158. if (!$this->allow_update()) {
  159. // No - stop here
  160. return -10011;
  161. }
  162. $update_selection = true;
  163. }
  164. // Fetch the option record
  165. $option = sloodle_get_record('choice_options', 'id', $optionid, 'choiceid', $this->moodle_choice_instance->id);
  166. if (!$option) return false;
  167. // Make sure the maximum selections for the given option have not yet been made
  168. if (!empty($this->moodle_choice_instance->limitanswers)) {
  169. $numselections = sloodle_count_records('choice_answers', 'optionid', $optionid);
  170. if (!$numselections) return false;
  171. if ($numselections >= $option->maxanswers) return -10012;
  172. }
  173. // If necessary, delete the existing selection
  174. if ($update_selection) sloodle_delete_records('choice_answers', 'choiceid', $this->moodle_choice_instance->id, 'userid', $user->get_user_id());
  175. // Select the new option
  176. $selection = new stdClass();
  177. $selection->choiceid = $this->moodle_choice_instance->id;
  178. $selection->userid = $user->get_user_id();
  179. $selection->optionid = $optionid;
  180. $selection->timemodified = time();
  181. if (!sloodle_insert_record('choice_answers', $selection)) return false;
  182. // Success!
  183. if ($update_selection) return 10012;
  184. return 10011;
  185. }
  186. // ACCESSORS //
  187. /**
  188. * Gets the name of this module instance.
  189. * @return string The name of this controller
  190. */
  191. function get_name()
  192. {
  193. return $this->moodle_choice_instance->name;
  194. }
  195. /**
  196. * Gets the intro description of this module instance, if available.
  197. * @return string The intro description of this controller
  198. */
  199. function get_intro()
  200. {
  201. // Seems to have changed somewhere before Moodle 2.3
  202. return isset($this->moodle_choice_instance->text) ? $this->moodle_choice_instance->text : $this->moodle_choice_instance->intro;
  203. }
  204. /**
  205. * Gets the identifier of the course this controller belongs to.
  206. * @return mixed Course identifier. Type depends on VLE. (In Moodle, it will be an integer).
  207. */
  208. function get_course_id()
  209. {
  210. return (int)$this->moodle_choice_instance->course;
  211. }
  212. /**
  213. * Gets the time at which this instance was created, or 0 if unknown.
  214. * @return int Timestamp
  215. */
  216. function get_creation_time()
  217. {
  218. return 0;
  219. }
  220. /**
  221. * Gets the time at which this instance was last modified, or 0 if unknown.
  222. * @return int Timestamp
  223. */
  224. function get_modification_time()
  225. {
  226. return (int)$this->moodle_choice_instance->timemodified;
  227. }
  228. /**
  229. * Gets the short type name of this instance.
  230. * @return string
  231. */
  232. function get_type()
  233. {
  234. return 'choice';
  235. }
  236. /**
  237. * Gets the full type name of this instance, according to the current language pack, if available.
  238. * Note: should be overridden by sub-classes.
  239. * @return string Full type name if possible, or the short name otherwise.
  240. */
  241. function get_type_full()
  242. {
  243. return get_string('modulename', 'choice');
  244. }
  245. /**
  246. * Gets the time at which this choice opens.
  247. * @return int Timestamp. 0 if choice has no opening time.
  248. */
  249. function get_opening_time()
  250. {
  251. return (int)$this->moodle_choice_instance->timeopen;
  252. }
  253. /**
  254. * Gets the time at which this choice closes.
  255. * @return int Timestamp. 0 if choice has no closing time.
  256. */
  257. function get_closing_time()
  258. {
  259. return (int)$this->moodle_choice_instance->timeclose;
  260. }
  261. /**
  262. * Determines if the choice is currently open.
  263. * @param int $timestamp The time to test. Uses the current time if none is given.
  264. * @return bool
  265. */
  266. function is_open($timestamp = null)
  267. {
  268. // Use the current time if necessary
  269. if ($timestamp === null) $timestamp = time();
  270. // Check against the opening and closing times
  271. $open = $this->get_opening_time();
  272. $close = $this->get_closing_time();
  273. if ($open > 0 && $open > $timestamp) return false;
  274. if ($close > 0 && $close < $timestamp) return false;
  275. return true;
  276. }
  277. /**
  278. * Determines if the choice has not opened yet.
  279. * @param int $timestamp The time to test. Uses the current time if none is given.
  280. * @return bool
  281. */
  282. function is_early($timestamp = null)
  283. {
  284. // Use the current time if necessary
  285. if ($timestamp === null) $timestamp = time();
  286. // Check against the opening time
  287. $open = $this->get_opening_time();
  288. if ($open == 0) return false; // No opening time - can never be early
  289. return ($open > $timestamp);
  290. }
  291. /**
  292. * Determines if the choice has already closed.
  293. * @param int $timestamp The time to test. Uses the current time if none is given.
  294. * @return bool
  295. */
  296. function is_late($timestamp = null)
  297. {
  298. // Use the current time if necessary
  299. if ($timestamp === null) $timestamp = time();
  300. // Check against the closing time
  301. $close = $this->get_closing_time();
  302. if ($close == 0) return false; // No opening time - can never be early
  303. return ($close < $timestamp);
  304. }
  305. /**
  306. * Checks if users are allowed to re-select their answer in this choice.
  307. * @return bool
  308. */
  309. function allow_update()
  310. {
  311. return !empty($this->moodle_choice_instance->allowupdate);
  312. }
  313. /**
  314. * Checks if results are to be shown.
  315. * (Some choices only allow results after the choice is closed).
  316. * @return bool
  317. */
  318. function can_show_results()
  319. {
  320. if ($this->moodle_choice_instance->showresults == CHOICE_SHOWRESULTS_ALWAYS) return true;
  321. if ($this->moodle_choice_instance->showresults == CHOICE_SHOWRESULTS_AFTER_CLOSE && $this->is_late()) return true;
  322. return false;
  323. }
  324. /**
  325. * Gets the number of people who have not yet answered the choice.
  326. * Counts all users on the course, including students and teachers.
  327. * @return int
  328. */
  329. function get_num_unanswered()
  330. {
  331. return $this->numunanswered;
  332. }
  333. }
  334. /**
  335. * Class to represent a single available option for a choice.
  336. * @package sloodle
  337. */
  338. class SloodleChoiceOption
  339. {
  340. /**
  341. * The ID of the option (should be unique across the site).
  342. * @var mixed
  343. * @access public
  344. */
  345. var $id = 0;
  346. /**
  347. * The text of this option.
  348. * @var string
  349. * @access public
  350. */
  351. var $text = '';
  352. /**
  353. * Number of selections so far of this option.
  354. * @var int
  355. * @access public
  356. */
  357. var $numselections = 0;
  358. /**
  359. * Maximum allowed number of selections for this option.
  360. * Note: will be -1 if there is no limit.
  361. * @var int
  362. * @access public
  363. */
  364. var $maxselections = -1;
  365. /**
  366. * Timestamp of when this option was last modified.
  367. * $var int
  368. * @access public
  369. */
  370. var $timemodified = 0;
  371. }
  372. ?>