diff --git a/mod/choice/lang/en/choice.php b/mod/choice/lang/en/choice.php index e241893933e54..99831875f17b8 100644 --- a/mod/choice/lang/en/choice.php +++ b/mod/choice/lang/en/choice.php @@ -27,6 +27,7 @@ $string['allowupdate'] = 'Allow choice to be updated'; $string['allowmultiple'] = 'Allow more than one choice to be selected'; $string['answered'] = 'Answered'; +$string['cannotsubmit'] = 'Sorry, there was a problem submitting your choice. Please try again.'; $string['completionsubmit'] = 'Show as complete when user makes a choice'; $string['displayhorizontal'] = 'Display horizontally'; $string['displaymode'] = 'Display mode for the options'; diff --git a/mod/choice/lib.php b/mod/choice/lib.php index a2d3a35b24358..ae9d5552bf182 100644 --- a/mod/choice/lib.php +++ b/mod/choice/lib.php @@ -236,12 +236,15 @@ function choice_prepare_options($choice, $user, $coursemodule, $allresponses) { } /** - * @global object - * @param int $formanswer - * @param object $choice - * @param int $userid - * @param object $course Course object - * @param object $cm + * Process user submitted answers for a choice, + * and either updating them or saving new answers. + * + * @param int $formanswer users submitted answers. + * @param object $choice the selected choice. + * @param int $userid user identifier. + * @param object $course current course. + * @param object $cm course context. + * @return void */ function choice_user_submit_response($formanswer, $choice, $userid, $course, $cm) { global $DB, $CFG; @@ -260,6 +263,20 @@ function choice_user_submit_response($formanswer, $choice, $userid, $course, $cm $formanswers = array($formanswer); } + // Start lock to prevent synchronous access to the same data + // before it's updated. + $timeout = 10; + $locktype = 'mod_choice_choice_user_submit_response'; + // Limiting access to this choice. + $resouce = 'choiceid:' . $choice->id; + $lockfactory = \core\lock\lock_config::get_lock_factory($locktype); + + // Opening the lock. + $choicelock = $lockfactory->get_lock($resouce, $timeout); + if (!$choicelock) { + print_error('cannotsubmit', 'choice'); + } + $current = $DB->get_records('choice_answers', array('choiceid' => $choice->id, 'userid' => $userid)); $context = context_module::instance($cm->id); @@ -305,7 +322,7 @@ function choice_user_submit_response($formanswer, $choice, $userid, $course, $cm } } foreach ($countanswers as $opt => $count) { - if ($count > $choice->maxanswers[$opt]) { + if ($count >= $choice->maxanswers[$opt]) { $choicesexceeded = true; break; } @@ -316,7 +333,7 @@ function choice_user_submit_response($formanswer, $choice, $userid, $course, $cm if (!($choice->limitanswers && $choicesexceeded)) { $answersnapshots = array(); if ($current) { - + // Update an existing answer. $existingchoices = array(); foreach ($current as $c) { if (in_array($c->optionid, $formanswers)) { @@ -341,25 +358,10 @@ function choice_user_submit_response($formanswer, $choice, $userid, $course, $cm } } - $eventdata = array(); - $eventdata['context'] = $context; - $eventdata['objectid'] = $choice->id; - $eventdata['userid'] = $userid; - $eventdata['courseid'] = $course->id; - $eventdata['other'] = array(); - $eventdata['other']['choiceid'] = $choice->id; - $eventdata['other']['optionid'] = $formanswer; - - $event = \mod_choice\event\answer_updated::create($eventdata); - $event->add_record_snapshot('course', $course); - $event->add_record_snapshot('course_modules', $cm); - $event->add_record_snapshot('choice', $choice); - foreach ($answersnapshots as $record) { - $event->add_record_snapshot('choice_answers', $record); - } - $event->trigger(); + // Initialised as true, meaning we updated the answer. + $answerupdated = true; } else { - + // Add new answer. foreach ($formanswers as $answer) { $newanswer = new stdClass(); $newanswer->choiceid = $choice->id; @@ -376,31 +378,48 @@ function choice_user_submit_response($formanswer, $choice, $userid, $course, $cm $completion->update_state($cm, COMPLETION_COMPLETE); } - $eventdata = array(); - $eventdata['context'] = $context; - $eventdata['objectid'] = $choice->id; - $eventdata['userid'] = $userid; - $eventdata['courseid'] = $course->id; - $eventdata['other'] = array(); - $eventdata['other']['choiceid'] = $choice->id; - $eventdata['other']['optionid'] = $formanswers; - - $event = \mod_choice\event\answer_submitted::create($eventdata); - $event->add_record_snapshot('course', $course); - $event->add_record_snapshot('course_modules', $cm); - $event->add_record_snapshot('choice', $choice); - foreach ($answersnapshots as $record) { - $event->add_record_snapshot('choice_answers', $record); - } - $event->trigger(); + // Initalised as false, meaning we submitted a new answer. + $answerupdated = false; } } else { // Check to see if current choice already selected - if not display error. $currentids = array_keys($current); + if (array_diff($currentids, $formanswers) || array_diff($formanswers, $currentids) ) { + // Release lock before error. + $choicelock->release(); print_error('choicefull', 'choice'); } } + + // Release lock. + $choicelock->release(); + + // Now record completed event. + if (isset($answerupdated)) { + $eventdata = array(); + $eventdata['context'] = $context; + $eventdata['objectid'] = $choice->id; + $eventdata['userid'] = $userid; + $eventdata['courseid'] = $course->id; + $eventdata['other'] = array(); + $eventdata['other']['choiceid'] = $choice->id; + + if ($answerupdated) { + $eventdata['other']['optionid'] = $formanswer; + $event = \mod_choice\event\answer_updated::create($eventdata); + } else { + $eventdata['other']['optionid'] = $formanswers; + $event = \mod_choice\event\answer_submitted::create($eventdata); + } + $event->add_record_snapshot('course', $course); + $event->add_record_snapshot('course_modules', $cm); + $event->add_record_snapshot('choice', $choice); + foreach ($answersnapshots as $record) { + $event->add_record_snapshot('choice_answers', $record); + } + $event->trigger(); + } } /**