[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 3 // This file is part of Moodle - http://moodle.org/ 4 // 5 // Moodle is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // Moodle is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 17 18 /** 19 * @package mod_choice 20 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 */ 23 24 defined('MOODLE_INTERNAL') || die(); 25 26 /** @global int $CHOICE_COLUMN_HEIGHT */ 27 global $CHOICE_COLUMN_HEIGHT; 28 $CHOICE_COLUMN_HEIGHT = 300; 29 30 /** @global int $CHOICE_COLUMN_WIDTH */ 31 global $CHOICE_COLUMN_WIDTH; 32 $CHOICE_COLUMN_WIDTH = 300; 33 34 define('CHOICE_PUBLISH_ANONYMOUS', '0'); 35 define('CHOICE_PUBLISH_NAMES', '1'); 36 37 define('CHOICE_SHOWRESULTS_NOT', '0'); 38 define('CHOICE_SHOWRESULTS_AFTER_ANSWER', '1'); 39 define('CHOICE_SHOWRESULTS_AFTER_CLOSE', '2'); 40 define('CHOICE_SHOWRESULTS_ALWAYS', '3'); 41 42 define('CHOICE_DISPLAY_HORIZONTAL', '0'); 43 define('CHOICE_DISPLAY_VERTICAL', '1'); 44 45 /** @global array $CHOICE_PUBLISH */ 46 global $CHOICE_PUBLISH; 47 $CHOICE_PUBLISH = array (CHOICE_PUBLISH_ANONYMOUS => get_string('publishanonymous', 'choice'), 48 CHOICE_PUBLISH_NAMES => get_string('publishnames', 'choice')); 49 50 /** @global array $CHOICE_SHOWRESULTS */ 51 global $CHOICE_SHOWRESULTS; 52 $CHOICE_SHOWRESULTS = array (CHOICE_SHOWRESULTS_NOT => get_string('publishnot', 'choice'), 53 CHOICE_SHOWRESULTS_AFTER_ANSWER => get_string('publishafteranswer', 'choice'), 54 CHOICE_SHOWRESULTS_AFTER_CLOSE => get_string('publishafterclose', 'choice'), 55 CHOICE_SHOWRESULTS_ALWAYS => get_string('publishalways', 'choice')); 56 57 /** @global array $CHOICE_DISPLAY */ 58 global $CHOICE_DISPLAY; 59 $CHOICE_DISPLAY = array (CHOICE_DISPLAY_HORIZONTAL => get_string('displayhorizontal', 'choice'), 60 CHOICE_DISPLAY_VERTICAL => get_string('displayvertical','choice')); 61 62 /// Standard functions ///////////////////////////////////////////////////////// 63 64 /** 65 * @global object 66 * @param object $course 67 * @param object $user 68 * @param object $mod 69 * @param object $choice 70 * @return object|null 71 */ 72 function choice_user_outline($course, $user, $mod, $choice) { 73 global $DB; 74 if ($answer = $DB->get_record('choice_answers', array('choiceid' => $choice->id, 'userid' => $user->id))) { 75 $result = new stdClass(); 76 $result->info = "'".format_string(choice_get_option_text($choice, $answer->optionid))."'"; 77 $result->time = $answer->timemodified; 78 return $result; 79 } 80 return NULL; 81 } 82 83 /** 84 * Callback for the "Complete" report - prints the activity summary for the given user 85 * 86 * @param object $course 87 * @param object $user 88 * @param object $mod 89 * @param object $choice 90 */ 91 function choice_user_complete($course, $user, $mod, $choice) { 92 global $DB; 93 if ($answers = $DB->get_records('choice_answers', array("choiceid" => $choice->id, "userid" => $user->id))) { 94 $info = []; 95 foreach ($answers as $answer) { 96 $info[] = "'" . format_string(choice_get_option_text($choice, $answer->optionid)) . "'"; 97 } 98 core_collator::asort($info); 99 echo get_string("answered", "choice") . ": ". join(', ', $info) . ". " . 100 get_string("updated", '', userdate($answer->timemodified)); 101 } else { 102 print_string("notanswered", "choice"); 103 } 104 } 105 106 /** 107 * Given an object containing all the necessary data, 108 * (defined by the form in mod_form.php) this function 109 * will create a new instance and return the id number 110 * of the new instance. 111 * 112 * @global object 113 * @param object $choice 114 * @return int 115 */ 116 function choice_add_instance($choice) { 117 global $DB, $CFG; 118 require_once($CFG->dirroot.'/mod/choice/locallib.php'); 119 120 $choice->timemodified = time(); 121 122 //insert answers 123 $choice->id = $DB->insert_record("choice", $choice); 124 foreach ($choice->option as $key => $value) { 125 $value = trim($value); 126 if (isset($value) && $value <> '') { 127 $option = new stdClass(); 128 $option->text = $value; 129 $option->choiceid = $choice->id; 130 if (isset($choice->limit[$key])) { 131 $option->maxanswers = $choice->limit[$key]; 132 } 133 $option->timemodified = time(); 134 $DB->insert_record("choice_options", $option); 135 } 136 } 137 138 // Add calendar events if necessary. 139 choice_set_events($choice); 140 141 return $choice->id; 142 } 143 144 /** 145 * Given an object containing all the necessary data, 146 * (defined by the form in mod_form.php) this function 147 * will update an existing instance with new data. 148 * 149 * @global object 150 * @param object $choice 151 * @return bool 152 */ 153 function choice_update_instance($choice) { 154 global $DB, $CFG; 155 require_once($CFG->dirroot.'/mod/choice/locallib.php'); 156 157 $choice->id = $choice->instance; 158 $choice->timemodified = time(); 159 160 //update, delete or insert answers 161 foreach ($choice->option as $key => $value) { 162 $value = trim($value); 163 $option = new stdClass(); 164 $option->text = $value; 165 $option->choiceid = $choice->id; 166 if (isset($choice->limit[$key])) { 167 $option->maxanswers = $choice->limit[$key]; 168 } 169 $option->timemodified = time(); 170 if (isset($choice->optionid[$key]) && !empty($choice->optionid[$key])){//existing choice record 171 $option->id=$choice->optionid[$key]; 172 if (isset($value) && $value <> '') { 173 $DB->update_record("choice_options", $option); 174 } else { 175 // Remove the empty (unused) option. 176 $DB->delete_records("choice_options", array("id" => $option->id)); 177 // Delete any answers associated with this option. 178 $DB->delete_records("choice_answers", array("choiceid" => $choice->id, "optionid" => $option->id)); 179 } 180 } else { 181 if (isset($value) && $value <> '') { 182 $DB->insert_record("choice_options", $option); 183 } 184 } 185 } 186 187 // Add calendar events if necessary. 188 choice_set_events($choice); 189 190 return $DB->update_record('choice', $choice); 191 192 } 193 194 /** 195 * @global object 196 * @param object $choice 197 * @param object $user 198 * @param object $coursemodule 199 * @param array $allresponses 200 * @return array 201 */ 202 function choice_prepare_options($choice, $user, $coursemodule, $allresponses) { 203 global $DB; 204 205 $cdisplay = array('options'=>array()); 206 207 $cdisplay['limitanswers'] = true; 208 $context = context_module::instance($coursemodule->id); 209 210 foreach ($choice->option as $optionid => $text) { 211 if (isset($text)) { //make sure there are no dud entries in the db with blank text values. 212 $option = new stdClass; 213 $option->attributes = new stdClass; 214 $option->attributes->value = $optionid; 215 $option->text = format_string($text); 216 $option->maxanswers = $choice->maxanswers[$optionid]; 217 $option->displaylayout = $choice->display; 218 219 if (isset($allresponses[$optionid])) { 220 $option->countanswers = count($allresponses[$optionid]); 221 } else { 222 $option->countanswers = 0; 223 } 224 if ($DB->record_exists('choice_answers', array('choiceid' => $choice->id, 'userid' => $user->id, 'optionid' => $optionid))) { 225 $option->attributes->checked = true; 226 } 227 if ( $choice->limitanswers && ($option->countanswers >= $option->maxanswers) && empty($option->attributes->checked)) { 228 $option->attributes->disabled = true; 229 } 230 $cdisplay['options'][] = $option; 231 } 232 } 233 234 $cdisplay['hascapability'] = is_enrolled($context, NULL, 'mod/choice:choose'); //only enrolled users are allowed to make a choice 235 236 if ($choice->allowupdate && $DB->record_exists('choice_answers', array('choiceid'=> $choice->id, 'userid'=> $user->id))) { 237 $cdisplay['allowupdate'] = true; 238 } 239 240 if ($choice->showpreview && $choice->timeopen > time()) { 241 $cdisplay['previewonly'] = true; 242 } 243 244 return $cdisplay; 245 } 246 247 /** 248 * Process user submitted answers for a choice, 249 * and either updating them or saving new answers. 250 * 251 * @param int $formanswer users submitted answers. 252 * @param object $choice the selected choice. 253 * @param int $userid user identifier. 254 * @param object $course current course. 255 * @param object $cm course context. 256 * @return void 257 */ 258 function choice_user_submit_response($formanswer, $choice, $userid, $course, $cm) { 259 global $DB, $CFG; 260 require_once($CFG->libdir.'/completionlib.php'); 261 262 $continueurl = new moodle_url('/mod/choice/view.php', array('id' => $cm->id)); 263 264 if (empty($formanswer)) { 265 print_error('atleastoneoption', 'choice', $continueurl); 266 } 267 268 if (is_array($formanswer)) { 269 if (!$choice->allowmultiple) { 270 print_error('multiplenotallowederror', 'choice', $continueurl); 271 } 272 $formanswers = $formanswer; 273 } else { 274 $formanswers = array($formanswer); 275 } 276 277 $options = $DB->get_records('choice_options', array('choiceid' => $choice->id), '', 'id'); 278 foreach ($formanswers as $key => $val) { 279 if (!isset($options[$val])) { 280 print_error('cannotsubmit', 'choice', $continueurl); 281 } 282 } 283 // Start lock to prevent synchronous access to the same data 284 // before it's updated, if using limits. 285 if ($choice->limitanswers) { 286 $timeout = 10; 287 $locktype = 'mod_choice_choice_user_submit_response'; 288 // Limiting access to this choice. 289 $resouce = 'choiceid:' . $choice->id; 290 $lockfactory = \core\lock\lock_config::get_lock_factory($locktype); 291 292 // Opening the lock. 293 $choicelock = $lockfactory->get_lock($resouce, $timeout); 294 if (!$choicelock) { 295 print_error('cannotsubmit', 'choice', $continueurl); 296 } 297 } 298 299 $current = $DB->get_records('choice_answers', array('choiceid' => $choice->id, 'userid' => $userid)); 300 $context = context_module::instance($cm->id); 301 302 $choicesexceeded = false; 303 $countanswers = array(); 304 foreach ($formanswers as $val) { 305 $countanswers[$val] = 0; 306 } 307 if($choice->limitanswers) { 308 // Find out whether groups are being used and enabled 309 if (groups_get_activity_groupmode($cm) > 0) { 310 $currentgroup = groups_get_activity_group($cm); 311 } else { 312 $currentgroup = 0; 313 } 314 315 list ($insql, $params) = $DB->get_in_or_equal($formanswers, SQL_PARAMS_NAMED); 316 317 if($currentgroup) { 318 // If groups are being used, retrieve responses only for users in 319 // current group 320 global $CFG; 321 322 $params['groupid'] = $currentgroup; 323 $sql = "SELECT ca.* 324 FROM {choice_answers} ca 325 INNER JOIN {groups_members} gm ON ca.userid=gm.userid 326 WHERE optionid $insql 327 AND gm.groupid= :groupid"; 328 } else { 329 // Groups are not used, retrieve all answers for this option ID 330 $sql = "SELECT ca.* 331 FROM {choice_answers} ca 332 WHERE optionid $insql"; 333 } 334 335 $answers = $DB->get_records_sql($sql, $params); 336 if ($answers) { 337 foreach ($answers as $a) { //only return enrolled users. 338 if (is_enrolled($context, $a->userid, 'mod/choice:choose')) { 339 $countanswers[$a->optionid]++; 340 } 341 } 342 } 343 foreach ($countanswers as $opt => $count) { 344 if ($count >= $choice->maxanswers[$opt]) { 345 $choicesexceeded = true; 346 break; 347 } 348 } 349 } 350 351 // Check the user hasn't exceeded the maximum selections for the choice(s) they have selected. 352 if (!($choice->limitanswers && $choicesexceeded)) { 353 $answersnapshots = array(); 354 if ($current) { 355 // Update an existing answer. 356 $existingchoices = array(); 357 foreach ($current as $c) { 358 if (in_array($c->optionid, $formanswers)) { 359 $existingchoices[] = $c->optionid; 360 $DB->set_field('choice_answers', 'timemodified', time(), array('id' => $c->id)); 361 $answersnapshots[] = $c; 362 } else { 363 $DB->delete_records('choice_answers', array('id' => $c->id)); 364 } 365 } 366 367 // Add new ones. 368 foreach ($formanswers as $f) { 369 if (!in_array($f, $existingchoices)) { 370 $newanswer = new stdClass(); 371 $newanswer->optionid = $f; 372 $newanswer->choiceid = $choice->id; 373 $newanswer->userid = $userid; 374 $newanswer->timemodified = time(); 375 $newanswer->id = $DB->insert_record("choice_answers", $newanswer); 376 $answersnapshots[] = $newanswer; 377 } 378 } 379 380 // Initialised as true, meaning we updated the answer. 381 $answerupdated = true; 382 } else { 383 // Add new answer. 384 foreach ($formanswers as $answer) { 385 $newanswer = new stdClass(); 386 $newanswer->choiceid = $choice->id; 387 $newanswer->userid = $userid; 388 $newanswer->optionid = $answer; 389 $newanswer->timemodified = time(); 390 $newanswer->id = $DB->insert_record("choice_answers", $newanswer); 391 $answersnapshots[] = $newanswer; 392 } 393 394 // Update completion state 395 $completion = new completion_info($course); 396 if ($completion->is_enabled($cm) && $choice->completionsubmit) { 397 $completion->update_state($cm, COMPLETION_COMPLETE); 398 } 399 400 // Initalised as false, meaning we submitted a new answer. 401 $answerupdated = false; 402 } 403 } else { 404 // Check to see if current choice already selected - if not display error. 405 $currentids = array_keys($current); 406 407 if (array_diff($currentids, $formanswers) || array_diff($formanswers, $currentids) ) { 408 // Release lock before error. 409 $choicelock->release(); 410 print_error('choicefull', 'choice', $continueurl); 411 } 412 } 413 414 // Release lock. 415 if (isset($choicelock)) { 416 $choicelock->release(); 417 } 418 419 // Now record completed event. 420 if (isset($answerupdated)) { 421 $eventdata = array(); 422 $eventdata['context'] = $context; 423 $eventdata['objectid'] = $choice->id; 424 $eventdata['userid'] = $userid; 425 $eventdata['courseid'] = $course->id; 426 $eventdata['other'] = array(); 427 $eventdata['other']['choiceid'] = $choice->id; 428 429 if ($answerupdated) { 430 $eventdata['other']['optionid'] = $formanswer; 431 $event = \mod_choice\event\answer_updated::create($eventdata); 432 } else { 433 $eventdata['other']['optionid'] = $formanswers; 434 $event = \mod_choice\event\answer_submitted::create($eventdata); 435 } 436 $event->add_record_snapshot('course', $course); 437 $event->add_record_snapshot('course_modules', $cm); 438 $event->add_record_snapshot('choice', $choice); 439 foreach ($answersnapshots as $record) { 440 $event->add_record_snapshot('choice_answers', $record); 441 } 442 $event->trigger(); 443 } 444 } 445 446 /** 447 * @param array $user 448 * @param object $cm 449 * @return void Output is echo'd 450 */ 451 function choice_show_reportlink($user, $cm) { 452 $userschosen = array(); 453 foreach($user as $optionid => $userlist) { 454 if ($optionid) { 455 $userschosen = array_merge($userschosen, array_keys($userlist)); 456 } 457 } 458 $responsecount = count(array_unique($userschosen)); 459 460 echo '<div class="reportlink">'; 461 echo "<a href=\"report.php?id=$cm->id\">".get_string("viewallresponses", "choice", $responsecount)."</a>"; 462 echo '</div>'; 463 } 464 465 /** 466 * @global object 467 * @param object $choice 468 * @param object $course 469 * @param object $coursemodule 470 * @param array $allresponses 471 472 * * @param bool $allresponses 473 * @return object 474 */ 475 function prepare_choice_show_results($choice, $course, $cm, $allresponses) { 476 global $OUTPUT; 477 478 $display = clone($choice); 479 $display->coursemoduleid = $cm->id; 480 $display->courseid = $course->id; 481 482 //overwrite options value; 483 $display->options = array(); 484 $allusers = []; 485 foreach ($choice->option as $optionid => $optiontext) { 486 $display->options[$optionid] = new stdClass; 487 $display->options[$optionid]->text = $optiontext; 488 $display->options[$optionid]->maxanswer = $choice->maxanswers[$optionid]; 489 490 if (array_key_exists($optionid, $allresponses)) { 491 $display->options[$optionid]->user = $allresponses[$optionid]; 492 $allusers = array_merge($allusers, array_keys($allresponses[$optionid])); 493 } 494 } 495 unset($display->option); 496 unset($display->maxanswers); 497 498 $display->numberofuser = count(array_unique($allusers)); 499 $context = context_module::instance($cm->id); 500 $display->viewresponsecapability = has_capability('mod/choice:readresponses', $context); 501 $display->deleterepsonsecapability = has_capability('mod/choice:deleteresponses',$context); 502 $display->fullnamecapability = has_capability('moodle/site:viewfullnames', $context); 503 504 if (empty($allresponses)) { 505 echo $OUTPUT->heading(get_string("nousersyet"), 3, null); 506 return false; 507 } 508 509 return $display; 510 } 511 512 /** 513 * @global object 514 * @param array $attemptids 515 * @param object $choice Choice main table row 516 * @param object $cm Course-module object 517 * @param object $course Course object 518 * @return bool 519 */ 520 function choice_delete_responses($attemptids, $choice, $cm, $course) { 521 global $DB, $CFG, $USER; 522 require_once($CFG->libdir.'/completionlib.php'); 523 524 if(!is_array($attemptids) || empty($attemptids)) { 525 return false; 526 } 527 528 foreach($attemptids as $num => $attemptid) { 529 if(empty($attemptid)) { 530 unset($attemptids[$num]); 531 } 532 } 533 534 $context = context_module::instance($cm->id); 535 $completion = new completion_info($course); 536 foreach($attemptids as $attemptid) { 537 if ($todelete = $DB->get_record('choice_answers', array('choiceid' => $choice->id, 'id' => $attemptid))) { 538 // Trigger the event answer deleted. 539 $eventdata = array(); 540 $eventdata['objectid'] = $todelete->id; 541 $eventdata['context'] = $context; 542 $eventdata['userid'] = $USER->id; 543 $eventdata['courseid'] = $course->id; 544 $eventdata['relateduserid'] = $todelete->userid; 545 $eventdata['other'] = array(); 546 $eventdata['other']['choiceid'] = $choice->id; 547 $eventdata['other']['optionid'] = $todelete->optionid; 548 $event = \mod_choice\event\answer_deleted::create($eventdata); 549 $event->add_record_snapshot('course', $course); 550 $event->add_record_snapshot('course_modules', $cm); 551 $event->add_record_snapshot('choice', $choice); 552 $event->add_record_snapshot('choice_answers', $todelete); 553 $event->trigger(); 554 555 $DB->delete_records('choice_answers', array('choiceid' => $choice->id, 'id' => $attemptid)); 556 } 557 } 558 559 // Update completion state. 560 if ($completion->is_enabled($cm) && $choice->completionsubmit) { 561 $completion->update_state($cm, COMPLETION_INCOMPLETE); 562 } 563 564 return true; 565 } 566 567 568 /** 569 * Given an ID of an instance of this module, 570 * this function will permanently delete the instance 571 * and any data that depends on it. 572 * 573 * @global object 574 * @param int $id 575 * @return bool 576 */ 577 function choice_delete_instance($id) { 578 global $DB; 579 580 if (! $choice = $DB->get_record("choice", array("id"=>"$id"))) { 581 return false; 582 } 583 584 $result = true; 585 586 if (! $DB->delete_records("choice_answers", array("choiceid"=>"$choice->id"))) { 587 $result = false; 588 } 589 590 if (! $DB->delete_records("choice_options", array("choiceid"=>"$choice->id"))) { 591 $result = false; 592 } 593 594 if (! $DB->delete_records("choice", array("id"=>"$choice->id"))) { 595 $result = false; 596 } 597 // Remove old calendar events. 598 if (! $DB->delete_records('event', array('modulename' => 'choice', 'instance' => $choice->id))) { 599 $result = false; 600 } 601 602 return $result; 603 } 604 605 /** 606 * Returns text string which is the answer that matches the id 607 * 608 * @global object 609 * @param object $choice 610 * @param int $id 611 * @return string 612 */ 613 function choice_get_option_text($choice, $id) { 614 global $DB; 615 616 if ($result = $DB->get_record("choice_options", array("id" => $id))) { 617 return $result->text; 618 } else { 619 return get_string("notanswered", "choice"); 620 } 621 } 622 623 /** 624 * Gets a full choice record 625 * 626 * @global object 627 * @param int $choiceid 628 * @return object|bool The choice or false 629 */ 630 function choice_get_choice($choiceid) { 631 global $DB; 632 633 if ($choice = $DB->get_record("choice", array("id" => $choiceid))) { 634 if ($options = $DB->get_records("choice_options", array("choiceid" => $choiceid), "id")) { 635 foreach ($options as $option) { 636 $choice->option[$option->id] = $option->text; 637 $choice->maxanswers[$option->id] = $option->maxanswers; 638 } 639 return $choice; 640 } 641 } 642 return false; 643 } 644 645 /** 646 * List the actions that correspond to a view of this module. 647 * This is used by the participation report. 648 * 649 * Note: This is not used by new logging system. Event with 650 * crud = 'r' and edulevel = LEVEL_PARTICIPATING will 651 * be considered as view action. 652 * 653 * @return array 654 */ 655 function choice_get_view_actions() { 656 return array('view','view all','report'); 657 } 658 659 /** 660 * List the actions that correspond to a post of this module. 661 * This is used by the participation report. 662 * 663 * Note: This is not used by new logging system. Event with 664 * crud = ('c' || 'u' || 'd') and edulevel = LEVEL_PARTICIPATING 665 * will be considered as post action. 666 * 667 * @return array 668 */ 669 function choice_get_post_actions() { 670 return array('choose','choose again'); 671 } 672 673 674 /** 675 * Implementation of the function for printing the form elements that control 676 * whether the course reset functionality affects the choice. 677 * 678 * @param object $mform form passed by reference 679 */ 680 function choice_reset_course_form_definition(&$mform) { 681 $mform->addElement('header', 'choiceheader', get_string('modulenameplural', 'choice')); 682 $mform->addElement('advcheckbox', 'reset_choice', get_string('removeresponses','choice')); 683 } 684 685 /** 686 * Course reset form defaults. 687 * 688 * @return array 689 */ 690 function choice_reset_course_form_defaults($course) { 691 return array('reset_choice'=>1); 692 } 693 694 /** 695 * Actual implementation of the reset course functionality, delete all the 696 * choice responses for course $data->courseid. 697 * 698 * @global object 699 * @global object 700 * @param object $data the data submitted from the reset course. 701 * @return array status array 702 */ 703 function choice_reset_userdata($data) { 704 global $CFG, $DB; 705 706 $componentstr = get_string('modulenameplural', 'choice'); 707 $status = array(); 708 709 if (!empty($data->reset_choice)) { 710 $choicessql = "SELECT ch.id 711 FROM {choice} ch 712 WHERE ch.course=?"; 713 714 $DB->delete_records_select('choice_answers', "choiceid IN ($choicessql)", array($data->courseid)); 715 $status[] = array('component'=>$componentstr, 'item'=>get_string('removeresponses', 'choice'), 'error'=>false); 716 } 717 718 /// updating dates - shift may be negative too 719 if ($data->timeshift) { 720 shift_course_mod_dates('choice', array('timeopen', 'timeclose'), $data->timeshift, $data->courseid); 721 $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false); 722 } 723 724 return $status; 725 } 726 727 /** 728 * @global object 729 * @global object 730 * @global object 731 * @uses CONTEXT_MODULE 732 * @param object $choice 733 * @param object $cm 734 * @param int $groupmode 735 * @param bool $onlyactive Whether to get response data for active users only. 736 * @return array 737 */ 738 function choice_get_response_data($choice, $cm, $groupmode, $onlyactive) { 739 global $CFG, $USER, $DB; 740 741 $context = context_module::instance($cm->id); 742 743 /// Get the current group 744 if ($groupmode > 0) { 745 $currentgroup = groups_get_activity_group($cm); 746 } else { 747 $currentgroup = 0; 748 } 749 750 /// Initialise the returned array, which is a matrix: $allresponses[responseid][userid] = responseobject 751 $allresponses = array(); 752 753 /// First get all the users who have access here 754 /// To start with we assume they are all "unanswered" then move them later 755 $allresponses[0] = get_enrolled_users($context, 'mod/choice:choose', $currentgroup, 756 user_picture::fields('u', array('idnumber')), null, 0, 0, $onlyactive); 757 758 /// Get all the recorded responses for this choice 759 $rawresponses = $DB->get_records('choice_answers', array('choiceid' => $choice->id)); 760 761 /// Use the responses to move users into the correct column 762 763 if ($rawresponses) { 764 $answeredusers = array(); 765 foreach ($rawresponses as $response) { 766 if (isset($allresponses[0][$response->userid])) { // This person is enrolled and in correct group 767 $allresponses[0][$response->userid]->timemodified = $response->timemodified; 768 $allresponses[$response->optionid][$response->userid] = clone($allresponses[0][$response->userid]); 769 $allresponses[$response->optionid][$response->userid]->answerid = $response->id; 770 $answeredusers[] = $response->userid; 771 } 772 } 773 foreach ($answeredusers as $answereduser) { 774 unset($allresponses[0][$answereduser]); 775 } 776 } 777 return $allresponses; 778 } 779 780 /** 781 * Returns all other caps used in module 782 * 783 * @return array 784 */ 785 function choice_get_extra_capabilities() { 786 return array('moodle/site:accessallgroups'); 787 } 788 789 /** 790 * @uses FEATURE_GROUPS 791 * @uses FEATURE_GROUPINGS 792 * @uses FEATURE_MOD_INTRO 793 * @uses FEATURE_COMPLETION_TRACKS_VIEWS 794 * @uses FEATURE_GRADE_HAS_GRADE 795 * @uses FEATURE_GRADE_OUTCOMES 796 * @param string $feature FEATURE_xx constant for requested feature 797 * @return mixed True if module supports feature, null if doesn't know 798 */ 799 function choice_supports($feature) { 800 switch($feature) { 801 case FEATURE_GROUPS: return true; 802 case FEATURE_GROUPINGS: return true; 803 case FEATURE_MOD_INTRO: return true; 804 case FEATURE_COMPLETION_TRACKS_VIEWS: return true; 805 case FEATURE_COMPLETION_HAS_RULES: return true; 806 case FEATURE_GRADE_HAS_GRADE: return false; 807 case FEATURE_GRADE_OUTCOMES: return false; 808 case FEATURE_BACKUP_MOODLE2: return true; 809 case FEATURE_SHOW_DESCRIPTION: return true; 810 811 default: return null; 812 } 813 } 814 815 /** 816 * Adds module specific settings to the settings block 817 * 818 * @param settings_navigation $settings The settings navigation object 819 * @param navigation_node $choicenode The node to add module settings to 820 */ 821 function choice_extend_settings_navigation(settings_navigation $settings, navigation_node $choicenode) { 822 global $PAGE; 823 824 if (has_capability('mod/choice:readresponses', $PAGE->cm->context)) { 825 826 $groupmode = groups_get_activity_groupmode($PAGE->cm); 827 if ($groupmode) { 828 groups_get_activity_group($PAGE->cm, true); 829 } 830 831 $choice = choice_get_choice($PAGE->cm->instance); 832 833 // Check if we want to include responses from inactive users. 834 $onlyactive = $choice->includeinactive ? false : true; 835 836 // Big function, approx 6 SQL calls per user. 837 $allresponses = choice_get_response_data($choice, $PAGE->cm, $groupmode, $onlyactive); 838 839 $allusers = []; 840 foreach($allresponses as $optionid => $userlist) { 841 if ($optionid) { 842 $allusers = array_merge($allusers, array_keys($userlist)); 843 } 844 } 845 $responsecount = count(array_unique($allusers)); 846 $choicenode->add(get_string("viewallresponses", "choice", $responsecount), new moodle_url('/mod/choice/report.php', array('id'=>$PAGE->cm->id))); 847 } 848 } 849 850 /** 851 * Obtains the automatic completion state for this choice based on any conditions 852 * in forum settings. 853 * 854 * @param object $course Course 855 * @param object $cm Course-module 856 * @param int $userid User ID 857 * @param bool $type Type of comparison (or/and; can be used as return value if no conditions) 858 * @return bool True if completed, false if not, $type if conditions not set. 859 */ 860 function choice_get_completion_state($course, $cm, $userid, $type) { 861 global $CFG,$DB; 862 863 // Get choice details 864 $choice = $DB->get_record('choice', array('id'=>$cm->instance), '*', 865 MUST_EXIST); 866 867 // If completion option is enabled, evaluate it and return true/false 868 if($choice->completionsubmit) { 869 return $DB->record_exists('choice_answers', array( 870 'choiceid'=>$choice->id, 'userid'=>$userid)); 871 } else { 872 // Completion option is not enabled so just return $type 873 return $type; 874 } 875 } 876 877 /** 878 * Return a list of page types 879 * @param string $pagetype current page type 880 * @param stdClass $parentcontext Block's parent context 881 * @param stdClass $currentcontext Current context of block 882 */ 883 function choice_page_type_list($pagetype, $parentcontext, $currentcontext) { 884 $module_pagetype = array('mod-choice-*'=>get_string('page-mod-choice-x', 'choice')); 885 return $module_pagetype; 886 } 887 888 /** 889 * Prints choice summaries on MyMoodle Page 890 * 891 * Prints choice name, due date and attempt information on 892 * choice activities that have a deadline that has not already passed 893 * and it is available for completing. 894 * @uses CONTEXT_MODULE 895 * @param array $courses An array of course objects to get choice instances from. 896 * @param array $htmlarray Store overview output array( course ID => 'choice' => HTML output ) 897 */ 898 function choice_print_overview($courses, &$htmlarray) { 899 global $USER, $DB, $OUTPUT; 900 901 if (empty($courses) || !is_array($courses) || count($courses) == 0) { 902 return; 903 } 904 if (!$choices = get_all_instances_in_courses('choice', $courses)) { 905 return; 906 } 907 908 $now = time(); 909 foreach ($choices as $choice) { 910 if ($choice->timeclose != 0 // If this choice is scheduled. 911 and $choice->timeclose >= $now // And the deadline has not passed. 912 and ($choice->timeopen == 0 or $choice->timeopen <= $now)) { // And the choice is available. 913 914 // Visibility. 915 $class = (!$choice->visible) ? 'dimmed' : ''; 916 917 // Link to activity. 918 $url = new moodle_url('/mod/choice/view.php', array('id' => $choice->coursemodule)); 919 $url = html_writer::link($url, format_string($choice->name), array('class' => $class)); 920 $str = $OUTPUT->box(get_string('choiceactivityname', 'choice', $url), 'name'); 921 922 // Deadline. 923 $str .= $OUTPUT->box(get_string('choicecloseson', 'choice', userdate($choice->timeclose)), 'info'); 924 925 // Display relevant info based on permissions. 926 if (has_capability('mod/choice:readresponses', context_module::instance($choice->coursemodule))) { 927 $attempts = $DB->count_records_sql('SELECT COUNT(DISTINCT userid) FROM {choice_answers} WHERE choiceid = ?', 928 [$choice->id]); 929 $url = new moodle_url('/mod/choice/report.php', ['id' => $choice->coursemodule]); 930 $str .= $OUTPUT->box(html_writer::link($url, get_string('viewallresponses', 'choice', $attempts)), 'info'); 931 932 } else if (has_capability('mod/choice:choose', context_module::instance($choice->coursemodule))) { 933 // See if the user has submitted anything. 934 $answers = $DB->count_records('choice_answers', array('choiceid' => $choice->id, 'userid' => $USER->id)); 935 if ($answers > 0) { 936 // User has already selected an answer, nothing to show. 937 $str = ''; 938 } else { 939 // User has not made a selection yet. 940 $str .= $OUTPUT->box(get_string('notanswered', 'choice'), 'info'); 941 } 942 } else { 943 // Does not have permission to do anything on this choice activity. 944 $str = ''; 945 } 946 947 // Make sure we have something to display. 948 if (!empty($str)) { 949 // Generate the containing div. 950 $str = $OUTPUT->box($str, 'choice overview'); 951 952 if (empty($htmlarray[$choice->course]['choice'])) { 953 $htmlarray[$choice->course]['choice'] = $str; 954 } else { 955 $htmlarray[$choice->course]['choice'] .= $str; 956 } 957 } 958 } 959 } 960 return; 961 } 962 963 964 /** 965 * Get my responses on a given choice. 966 * 967 * @param stdClass $choice Choice record 968 * @return array of choice answers records 969 * @since Moodle 3.0 970 */ 971 function choice_get_my_response($choice) { 972 global $DB, $USER; 973 return $DB->get_records('choice_answers', array('choiceid' => $choice->id, 'userid' => $USER->id)); 974 } 975 976 977 /** 978 * Get all the responses on a given choice. 979 * 980 * @param stdClass $choice Choice record 981 * @return array of choice answers records 982 * @since Moodle 3.0 983 */ 984 function choice_get_all_responses($choice) { 985 global $DB; 986 return $DB->get_records('choice_answers', array('choiceid' => $choice->id)); 987 } 988 989 990 /** 991 * Return true if we are allowd to view the choice results. 992 * 993 * @param stdClass $choice Choice record 994 * @param rows|null $current my choice responses 995 * @param bool|null $choiceopen if the choice is open 996 * @return bool true if we can view the results, false otherwise. 997 * @since Moodle 3.0 998 */ 999 function choice_can_view_results($choice, $current = null, $choiceopen = null) { 1000 1001 if (is_null($choiceopen)) { 1002 $timenow = time(); 1003 if ($choice->timeclose != 0 && $timenow > $choice->timeclose) { 1004 $choiceopen = false; 1005 } else { 1006 $choiceopen = true; 1007 } 1008 } 1009 if (empty($current)) { 1010 $current = choice_get_my_response($choice); 1011 } 1012 1013 if ($choice->showresults == CHOICE_SHOWRESULTS_ALWAYS or 1014 ($choice->showresults == CHOICE_SHOWRESULTS_AFTER_ANSWER and !empty($current)) or 1015 ($choice->showresults == CHOICE_SHOWRESULTS_AFTER_CLOSE and !$choiceopen)) { 1016 return true; 1017 } 1018 return false; 1019 } 1020 1021 /** 1022 * Mark the activity completed (if required) and trigger the course_module_viewed event. 1023 * 1024 * @param stdClass $choice choice object 1025 * @param stdClass $course course object 1026 * @param stdClass $cm course module object 1027 * @param stdClass $context context object 1028 * @since Moodle 3.0 1029 */ 1030 function choice_view($choice, $course, $cm, $context) { 1031 1032 // Trigger course_module_viewed event. 1033 $params = array( 1034 'context' => $context, 1035 'objectid' => $choice->id 1036 ); 1037 1038 $event = \mod_choice\event\course_module_viewed::create($params); 1039 $event->add_record_snapshot('course_modules', $cm); 1040 $event->add_record_snapshot('course', $course); 1041 $event->add_record_snapshot('choice', $choice); 1042 $event->trigger(); 1043 1044 // Completion. 1045 $completion = new completion_info($course); 1046 $completion->set_module_viewed($cm); 1047 } 1048 1049 /** 1050 * Check if a choice is available for the current user. 1051 * 1052 * @param stdClass $choice choice record 1053 * @return array status (available or not and possible warnings) 1054 */ 1055 function choice_get_availability_status($choice) { 1056 $available = true; 1057 $warnings = array(); 1058 1059 $timenow = time(); 1060 1061 if (!empty($choice->timeopen) && ($choice->timeopen > $timenow)) { 1062 $available = false; 1063 $warnings['notopenyet'] = userdate($choice->timeopen); 1064 } else if (!empty($choice->timeclose) && ($timenow > $choice->timeclose)) { 1065 $available = false; 1066 $warnings['expired'] = userdate($choice->timeclose); 1067 } 1068 if (!$choice->allowupdate && choice_get_my_response($choice)) { 1069 $available = false; 1070 $warnings['choicesaved'] = ''; 1071 } 1072 1073 // Choice is available. 1074 return array($available, $warnings); 1075 } 1076 1077 /** 1078 * This standard function will check all instances of this module 1079 * and make sure there are up-to-date events created for each of them. 1080 * If courseid = 0, then every chat event in the site is checked, else 1081 * only chat events belonging to the course specified are checked. 1082 * This function is used, in its new format, by restore_refresh_events() 1083 * 1084 * @param int $courseid 1085 * @return bool 1086 */ 1087 function choice_refresh_events($courseid = 0) { 1088 global $DB, $CFG; 1089 require_once($CFG->dirroot.'/mod/choice/locallib.php'); 1090 1091 if ($courseid) { 1092 if (! $choices = $DB->get_records("choice", array("course" => $courseid))) { 1093 return true; 1094 } 1095 } else { 1096 if (! $choices = $DB->get_records("choice")) { 1097 return true; 1098 } 1099 } 1100 1101 foreach ($choices as $choice) { 1102 choice_set_events($choice); 1103 } 1104 return true; 1105 } 1106
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |