MDL-5311 qtype_multichoice: add clear my choice option

This commit is contained in:
Simey Lameze
2019-04-29 14:04:51 +08:00
parent 14cdf51189
commit f6a778497e
5 changed files with 181 additions and 5 deletions
@@ -0,0 +1 @@
define(["jquery","core/custom_interaction_events"],function(a,b){var c={CHOICE_ELEMENT:".answer input",CLEAR_CHOICE_ELEMENT:'div[class="qtype_multichoice_clearchoice"]'},d=function(a){a.find('input[type="radio"]').prop("checked",!0)},e=function(a,b){return a.find('div[id="'+b+'"]')},f=function(a){a.addClass("hidden")},g=function(a){a.removeClass("hidden")},h=function(a,h){var i=e(a,h);a.on(b.events.activate,c.CLEAR_CHOICE_ELEMENT,function(){d(i),f(i)}),a.on(b.events.activate,c.CHOICE_ELEMENT,function(){g(i)})},i=function(b,c){b=a("#"+b),h(b,c)};return{init:i}});
@@ -0,0 +1,105 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Manages 'Clear my choice' functionality actions.
*
* @module qtype_multichoice/clearchoice
* @copyright 2019 Simey Lameze <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.7
*/
define(['jquery', 'core/custom_interaction_events'], function($, CustomEvents) {
var SELECTORS = {
CHOICE_ELEMENT: '.answer input',
CLEAR_CHOICE_ELEMENT: 'div[class="qtype_multichoice_clearchoice"]'
};
/**
* Mark clear choice radio as checked.
*
* @param {Object} clearChoiceContainer The clear choice option container.
*/
var checkClearChoiceRadio = function(clearChoiceContainer) {
clearChoiceContainer.find('input[type="radio"]').prop('checked', true);
};
/**
* Get the clear choice div container.
*
* @param {Object} root The question root element.
* @param {string} fieldPrefix The question outer div prefix.
* @returns {Object} The clear choice div container.
*/
var getClearChoiceElement = function(root, fieldPrefix) {
return root.find('div[id="' + fieldPrefix + '"]');
};
/**
* Hide clear choice option.
*
* @param {Object} clearChoiceContainer The clear choice option container.
*/
var hideClearChoiceOption = function(clearChoiceContainer) {
clearChoiceContainer.addClass('hidden');
};
/**
* Shows clear choice option.
*
* @param {Object} clearChoiceContainer The clear choice option container.
*/
var showClearChoiceOption = function(clearChoiceContainer) {
clearChoiceContainer.removeClass('hidden');
};
/**
* Register event listeners for the clear choice module.
*
* @param {Object} root The question outer div prefix.
* @param {string} fieldPrefix The "Clear choice" div prefix.
*/
var registerEventListeners = function(root, fieldPrefix) {
var clearChoiceContainer = getClearChoiceElement(root, fieldPrefix);
root.on(CustomEvents.events.activate, SELECTORS.CLEAR_CHOICE_ELEMENT, function() {
// Mark the clear choice radio element as checked.
checkClearChoiceRadio(clearChoiceContainer);
// Now that the hidden radio has been checked, hide the clear choice option.
hideClearChoiceOption(clearChoiceContainer);
});
root.on(CustomEvents.events.activate, SELECTORS.CHOICE_ELEMENT, function() {
// If the event has been triggered by any other choice, show the clear choice option.
showClearChoiceOption(clearChoiceContainer);
});
};
/**
* Initialise clear choice module.
* @param {string} root The question outer div prefix.
* @param {string} fieldPrefix The "Clear choice" div prefix.
*/
var init = function(root, fieldPrefix) {
root = $('#' + root);
registerEventListeners(root, fieldPrefix);
};
return {
init: init
};
});
@@ -37,6 +37,7 @@ $string['answersingleno'] = 'Multiple answers allowed';
$string['answersingleyes'] = 'One answer only';
$string['choiceno'] = 'Choice {$a}';
$string['choices'] = 'Available choices';
$string['clearchoice'] = 'Clear my choice';
$string['clozeaid'] = 'Enter missing word';
$string['correctansweris'] = 'The correct answer is: {$a}';
$string['correctanswersare'] = 'The correct answers are: {$a}';
+10 -5
View File
@@ -176,8 +176,7 @@ class qtype_multichoice_single_question extends qtype_multichoice_base {
}
public function summarise_response(array $response) {
if (!array_key_exists('answer', $response) ||
!array_key_exists($response['answer'], $this->order)) {
if (!$this->is_complete_response($response)) {
return null;
}
$ansid = $this->order[$response['answer']];
@@ -186,8 +185,7 @@ class qtype_multichoice_single_question extends qtype_multichoice_base {
}
public function classify_response(array $response) {
if (!array_key_exists('answer', $response) ||
!array_key_exists($response['answer'], $this->order)) {
if (!$this->is_complete_response($response)) {
return array($this->id => question_classified_response::no_response());
}
$choiceid = $this->order[$response['answer']];
@@ -230,11 +228,18 @@ class qtype_multichoice_single_question extends qtype_multichoice_base {
}
public function is_same_response(array $prevresponse, array $newresponse) {
if (!$this->is_complete_response($prevresponse)) {
$prevresponse = [];
}
if (!$this->is_complete_response($newresponse)) {
$newresponse = [];
}
return question_utils::arrays_same_at_key($prevresponse, $newresponse, 'answer');
}
public function is_complete_response(array $response) {
return array_key_exists('answer', $response) && $response['answer'] !== '';
return array_key_exists('answer', $response) && $response['answer'] !== ''
&& (string) $response['answer'] !== '-1';
}
public function is_gradable_response(array $response) {
+64
View File
@@ -35,6 +35,17 @@ defined('MOODLE_INTERNAL') || die();
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class qtype_multichoice_renderer_base extends qtype_with_combined_feedback_renderer {
/**
* Method to generating the bits of output after question choices.
*
* @param question_attempt $qa The question attempt object.
* @param question_display_options $options controls what should and should not be displayed.
*
* @return string HTML output.
*/
protected abstract function after_choices(question_attempt $qa, question_display_options $options);
protected abstract function get_input_type();
protected abstract function get_input_name(question_attempt $qa, $value);
@@ -136,6 +147,8 @@ abstract class qtype_multichoice_renderer_base extends qtype_with_combined_feedb
}
$result .= html_writer::end_tag('div'); // Answer.
$result .= $this->after_choices($qa, $options);
$result .= html_writer::end_tag('div'); // Ablock.
if ($qa->get_state() == question_state::$invalid) {
@@ -252,6 +265,53 @@ class qtype_multichoice_single_renderer extends qtype_multichoice_renderer_base
}
return $this->correct_choices($right);
}
public function after_choices(question_attempt $qa, question_display_options $options) {
// Only load the clear choice feature if it's not read only.
if ($options->readonly) {
return '';
}
$question = $qa->get_question();
$response = $question->get_response($qa);
$hascheckedchoice = false;
foreach ($question->get_order($qa) as $value => $ansid) {
if ($question->is_choice_selected($response, $value)) {
$hascheckedchoice = true;
break;
}
}
$clearchoiceid = $this->get_input_id($qa, -1);
$clearchoicefieldname = $qa->get_qt_field_name('clearchoice');
$clearchoiceradioattrs = [
'type' => $this->get_input_type(),
'name' => $qa->get_qt_field_name('answer'),
'id' => $clearchoiceid,
'value' => -1
];
$cssclass = 'qtype_multichoice_clearchoice';
// When no choice selected during rendering, then hide the clear choice option.
if (!$hascheckedchoice && $response == -1) {
$cssclass .= ' hidden';
$clearchoiceradioattrs['checked'] = 'checked';
}
// Adds an hidden radio that will be checked to give the impression the choice has been cleared.
$clearchoiceradio = html_writer::empty_tag('input', $clearchoiceradioattrs);
$clearchoiceradio .= html_writer::tag('label', get_string('clearchoice', 'qtype_multichoice'),
['for' => $clearchoiceid, 'role' => 'button', 'tabindex' => 0]);
// Now wrap the radio and label inside a div.
$result = html_writer::tag('div', $clearchoiceradio, ['id' => $clearchoicefieldname, 'class' => $cssclass]);
// Load required clearchoice AMD module.
$this->page->requires->js_call_amd('qtype_multichoice/clearchoice', 'init',
[$qa->get_outer_question_div_unique_id(), $clearchoicefieldname]);
return $result;
}
}
/**
@@ -262,6 +322,10 @@ class qtype_multichoice_single_renderer extends qtype_multichoice_renderer_base
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_multichoice_multi_renderer extends qtype_multichoice_renderer_base {
protected function after_choices(question_attempt $qa, question_display_options $options) {
return '';
}
protected function get_input_type() {
return 'checkbox';
}