From e0dcb76a4174515f2cd88d2557b840bf34697110 Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Mon, 28 Feb 2022 13:30:54 +1100 Subject: [PATCH 1/2] MDL-71305 core_question: Remove duplicate strings --- lang/en/question.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lang/en/question.php b/lang/en/question.php index 81227317af6..3d9ffe93509 100644 --- a/lang/en/question.php +++ b/lang/en/question.php @@ -354,8 +354,6 @@ $string['defaultmark'] = 'Default mark'; $string['errorsavingflags'] = 'Error saving the flag state.'; $string['feedback'] = 'Feedback'; $string['fillincorrect'] = 'Fill in correct responses'; -$string['flagged'] = 'Flagged'; -$string['flagthisquestion'] = 'Flag this question'; $string['generalfeedback'] = 'General feedback'; $string['generalfeedback_help'] = 'General feedback is shown to the student after they have completed the question. Unlike specific feedback, which depends on the question type and what response the student gave, the same general feedback text is shown to all students. @@ -388,7 +386,6 @@ $string['markoutofmax'] = 'Mark {$a->mark} out of {$a->max}'; $string['marks'] = 'Marks'; $string['noresponse'] = '[No response]'; $string['notanswered'] = 'Not answered'; -$string['notflagged'] = 'Not flagged'; $string['notgraded'] = 'Not graded'; $string['notshown'] = 'Not shown'; $string['notyetanswered'] = 'Not yet answered'; @@ -451,7 +448,6 @@ $string['restoremultipletopcats'] = 'The backup file contains more than one top- $string['rightanswer'] = 'Right answer'; $string['rightanswer_help'] = 'An automatically generated summary of the correct response. This can be limited, so you may wish to consider explaining the correct solution in the general feedback for the question, and turning this option off.'; $string['saved'] = 'Saved: {$a}'; -$string['saveflags'] = 'Save the state of the flags'; $string['settingsformultipletries'] = 'Multiple tries'; $string['showhidden'] = 'Also show old questions'; $string['showmarkandmax'] = 'Show mark and max'; From 8c1587e612ba2e8d5bbe24d295095b267b3344ad Mon Sep 17 00:00:00 2001 From: Shamim Rezaie Date: Sun, 27 Feb 2022 03:38:45 +1100 Subject: [PATCH 2/2] MDL-71305 core_question: Use toggle button for flag question element --- question/engine/lib.php | 12 ++---- question/engine/renderer.php | 27 ++++++------- question/flags.js | 73 ++++++++++++++++++++++++------------ 3 files changed, 66 insertions(+), 46 deletions(-) diff --git a/question/engine/lib.php b/question/engine/lib.php index 62990f180bd..ace64193025 100644 --- a/question/engine/lib.php +++ b/question/engine/lib.php @@ -747,26 +747,22 @@ abstract class question_flags { 'requires' => array('base', 'dom', 'event-delegate', 'io-base'), ); $actionurl = $CFG->wwwroot . '/question/toggleflag.php'; - $flagtext = array( - 0 => get_string('clickflag', 'question'), - 1 => get_string('clickunflag', 'question') - ); $flagattributes = array( 0 => array( 'src' => $OUTPUT->image_url('i/unflagged') . '', 'title' => get_string('clicktoflag', 'question'), - 'alt' => get_string('notflagged', 'question'), - // 'text' => get_string('clickflag', 'question'), + 'alt' => get_string('flagged', 'question'), // Label on toggle should not change. + 'text' => get_string('clickflag', 'question'), ), 1 => array( 'src' => $OUTPUT->image_url('i/flagged') . '', 'title' => get_string('clicktounflag', 'question'), 'alt' => get_string('flagged', 'question'), - // 'text' => get_string('clickunflag', 'question'), + 'text' => get_string('clickunflag', 'question'), ), ); $PAGE->requires->js_init_call('M.core_question_flags.init', - array($actionurl, $flagattributes, $flagtext), false, $module); + array($actionurl, $flagattributes), false, $module); $done = true; } } diff --git a/question/engine/renderer.php b/question/engine/renderer.php index 0a3c83655ac..0f2d77af878 100644 --- a/question/engine/renderer.php +++ b/question/engine/renderer.php @@ -263,8 +263,6 @@ class core_question_renderer extends plugin_renderer_base { * @param int $flagsoption the option that says whether flags should be displayed. */ protected function question_flag(question_attempt $qa, $flagsoption) { - global $CFG; - $divattributes = array('class' => 'questionflag'); switch ($flagsoption) { @@ -290,17 +288,14 @@ class core_question_renderer extends plugin_renderer_base { $flagcontent = html_writer::empty_tag('input', array('type' => 'hidden', 'name' => $id, 'value' => 0)) . - html_writer::empty_tag('input', $checkboxattributes) . html_writer::empty_tag('input', array('type' => 'hidden', 'value' => $postdata, 'class' => 'questionflagpostdata')) . + html_writer::empty_tag('input', $checkboxattributes) . html_writer::tag('label', $this->get_flag_html($qa->is_flagged(), $id . 'img'), array('id' => $id . 'label', 'for' => $id . 'checkbox')) . "\n"; $divattributes = array( 'class' => 'questionflag editable', - 'aria-atomic' => 'true', - 'aria-relevant' => 'text', - 'aria-live' => 'assertive', ); break; @@ -322,18 +317,16 @@ class core_question_renderer extends plugin_renderer_base { protected function get_flag_html($flagged, $id = '') { if ($flagged) { $icon = 'i/flagged'; - $alt = get_string('flagged', 'question'); $label = get_string('clickunflag', 'question'); } else { $icon = 'i/unflagged'; - $alt = get_string('notflagged', 'question'); $label = get_string('clickflag', 'question'); } - $attributes = array( + $attributes = [ 'src' => $this->image_url($icon), - 'alt' => $alt, + 'alt' => '', 'class' => 'questionflagimage', - ); + ]; if ($id) { $attributes['id'] = $id; } @@ -343,10 +336,14 @@ class core_question_renderer extends plugin_renderer_base { return $img; } - protected function edit_question_link(question_attempt $qa, - question_display_options $options) { - global $CFG; - + /** + * Generate the display of the edit question link. + * + * @param question_attempt $qa The question attempt to display. + * @param question_display_options $options controls what should and should not be displayed. + * @return string + */ + protected function edit_question_link(question_attempt $qa, question_display_options $options) { if (empty($options->editquestionparams)) { return ''; } diff --git a/question/flags.js b/question/flags.js index aa8dac16cc5..3720d2409b1 100644 --- a/question/flags.js +++ b/question/flags.js @@ -28,13 +28,11 @@ M.core_question_flags = { flagattributes: null, actionurl: null, - flagtext: null, listeners: [], - init: function(Y, actionurl, flagattributes, flagtext) { + init: function(Y, actionurl, flagattributes) { M.core_question_flags.flagattributes = flagattributes; M.core_question_flags.actionurl = actionurl; - M.core_question_flags.flagtext = flagtext; Y.all('div.questionflag').each(function(flagdiv, i) { var checkbox = flagdiv.one('input[type=checkbox]'); @@ -47,38 +45,67 @@ M.core_question_flags = { input.set('name', checkbox.get('name')); input.set('value', checkbox.get('checked') ? 1 : 0); - // Create an image input to replace the img tag. - var image = Y.Node.create(''); - var flagtext = Y.Node.create('.'); - M.core_question_flags.update_flag(input, image, flagtext); + var ariaPressed = checkbox.get('checked') ? 'true' : 'false'; + var toggle = Y.Node.create('' + + '.' + + ''); + M.core_question_flags.update_flag(input, toggle); checkbox.remove(); flagdiv.one('label').remove(); flagdiv.append(input); - flagdiv.append(image); - flagdiv.append(flagtext); + flagdiv.append(toggle); }); Y.delegate('click', function(e) { - var input = this.one('input.questionflagvalue'); - input.set('value', 1 - input.get('value')); - M.core_question_flags.update_flag(input, this.one('input.questionflagimage'), - this.one('span.questionflagtext')); - var postdata = this.one('input.questionflagpostdata').get('value') + - input.get('value'); - e.halt(); - Y.io(M.core_question_flags.actionurl , {method: 'POST', 'data': postdata}); - M.core_question_flags.fire_listeners(postdata); + M.core_question_flags.process(this); }, document.body, 'div.questionflag'); + Y.delegate('key', function(e) { + e.halt(); + if (e.keyCode == 13) { + M.core_question_flags.process(this); + } + }, document.body, 'down:enter, space', 'div.questionflag'); + Y.delegate('key', function(e) { + e.halt(); + M.core_question_flags.process(this); + }, document.body, 'up:space', 'div.questionflag'); }, - update_flag: function(input, image, flagtext) { + update_flag: function(input, toggle) { var value = input.get('value'); - image.setAttrs(M.core_question_flags.flagattributes[value]); - flagtext.replaceChild(flagtext.create(M.core_question_flags.flagtext[value]), - flagtext.get('firstChild')); - flagtext.set('title', M.core_question_flags.flagattributes[value].title); + toggle.setContent( + '' + + M.core_question_flags.flagattributes[value].text + ); + toggle.set('aria-pressed', parseInt(value) ? 'true' : 'false'); + toggle.set('aria-label', M.core_question_flags.flagattributes[value].alt); + if (M.core_question_flags.flagattributes[value].title != M.core_question_flags.flagattributes[value].text) { + toggle.set('title', M.core_question_flags.flagattributes[value].title); + } else { + toggle.removeAttribute('title'); + } + }, + + /** + * Process the change of flag status. + * + * @param {Y.Node} target The root element + */ + process: function(target) { + var input = target.one('input.questionflagvalue'); + input.set('value', 1 - input.get('value')); + M.core_question_flags.update_flag(input, target.one('[aria-pressed]')); + var postdata = target.one('input.questionflagpostdata').get('value') + + input.get('value'); + + Y.io(M.core_question_flags.actionurl, {method: 'POST', 'data': postdata}); + M.core_question_flags.fire_listeners(postdata); }, add_listener: function(listener) {