mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 16:32:18 +02:00
MDL-79863 qtype_ordering: qtype/ordering fix display of correct/wrong items for question review
This commit is contained in:
parent
12472692db
commit
e5e7f24f35
@ -1,4 +1,4 @@
|
||||
<?php // $Id: qtype_TEMPLATE.php,v 1.2 2006/08/25 21:40:44 Serafim Panov Exp $
|
||||
<?php
|
||||
/**
|
||||
* The language strings for the QTYPENAME question type.
|
||||
*
|
||||
@ -27,16 +27,19 @@ $string['ordering_contiguous'] = 'Contiguous';
|
||||
$string['ordering_logicalpossibilities'] = 'Logical possibilities';
|
||||
$string['ordering_addmoreanswers'] = 'Blank for {no} more answers';
|
||||
|
||||
$string['ordering_choiceno'] = 'Answer {$a}';
|
||||
$string['ordering_choiceno'] = 'Answer {$a}';
|
||||
$string['ordering_choices'] = 'Available answers';
|
||||
$string['ordering_answer'] = 'Answer';
|
||||
|
||||
$string['ordering_itemsforstudent'] = 'How many items the student will see';
|
||||
|
||||
$string['correctfeedback'] = 'For any correct answer';
|
||||
$string['incorrectfeedback'] = 'For any incorrect answer';
|
||||
$string['correctorder'] = 'The correct order for these items is as follows:';
|
||||
$string['noresponsedetails'] = 'Sorry, no details of the response to this question are available.';
|
||||
|
||||
$string['correctfeedback'] = 'Correct answer';
|
||||
$string['incorrectfeedback'] = 'Incorrect answer';
|
||||
$string['overallfeedback'] = 'Overall Feedback';
|
||||
$string['partiallycorrectfeedback'] = 'For any partially correct answer';
|
||||
$string['partiallycorrectfeedback'] = 'Partially correct answer ({$a})';
|
||||
$string['ordering_notenoughanswers'] = 'not enough answers';
|
||||
|
||||
$string['ordering_comment'] = 'Drag and drop the items into the correct order.';
|
||||
|
@ -32,18 +32,32 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_ordering_question extends question_graded_automatically {
|
||||
|
||||
public $answers;
|
||||
public $options;
|
||||
public $rightanswer;
|
||||
public $truefeedback;
|
||||
public $falsefeedback;
|
||||
public $trueanswerid;
|
||||
public $falseanswerid;
|
||||
|
||||
public function get_response_fieldname() {
|
||||
return 'response_'.$this->id;
|
||||
}
|
||||
|
||||
public function format_questiontext($qa) {
|
||||
$text = parent::format_questiontext($qa);
|
||||
return stripslashes($text);
|
||||
}
|
||||
|
||||
public function get_expected_data() {
|
||||
return array('answer' => PARAM_INTEGER);
|
||||
$name = $this->get_response_fieldname();
|
||||
return array($name => PARAM_TEXT);
|
||||
}
|
||||
|
||||
public function get_correct_response() {
|
||||
return array('answer' => (int) $this->rightanswer);
|
||||
if ($this->rightanswer===null) {
|
||||
$this->rightanswer = $this->get_ordering_answers();
|
||||
$this->rightanswer = array_keys($this->rightanswer);
|
||||
$this->rightanswer = implode(',', $this->rightanswer);
|
||||
}
|
||||
return array('answer' => $this->rightanswer);
|
||||
}
|
||||
|
||||
public function summarise_response(array $response) {
|
||||
@ -59,10 +73,56 @@ class qtype_ordering_question extends question_graded_automatically {
|
||||
}
|
||||
}
|
||||
|
||||
public function get_ordering_options() {
|
||||
global $DB;
|
||||
if ($this->options===null) {
|
||||
$this->options = $DB->get_record('question_ordering', array('question' => $this->id));
|
||||
if ($this->options==false) {
|
||||
$this->options = (object)array(
|
||||
'question' => $this->id,
|
||||
'logical' => 0, // require all answers
|
||||
'studentsee' => 0,
|
||||
'correctfeedback' => '',
|
||||
'incorrectfeedback' => '',
|
||||
'partiallycorrectfeedback' => ''
|
||||
);
|
||||
$this->options->id = $DB->insert_record('question_ordering', $options);
|
||||
}
|
||||
}
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
public function get_ordering_answers() {
|
||||
global $CFG, $DB;
|
||||
if ($this->answers===null) {
|
||||
$this->answers = $DB->get_records('question_answers', array('question' => $this->id), 'fraction,id');
|
||||
if ($this->answers) {
|
||||
if (isset($CFG->passwordsaltmain)) {
|
||||
$salt = $CFG->passwordsaltmain;
|
||||
} else {
|
||||
$salt = ''; // complex_random_string()
|
||||
}
|
||||
foreach ($this->answers as $answerid => $answer) {
|
||||
$this->answers[$answerid]->md5key = 'ordering_item_'.md5($salt.$answer->answer);
|
||||
}
|
||||
} else {
|
||||
$this->answers = array();
|
||||
}
|
||||
}
|
||||
return $this->answers;
|
||||
}
|
||||
|
||||
public function is_complete_response(array $response) {
|
||||
global $CFG, $DB;
|
||||
|
||||
$responses = explode(',', $_POST['q'.$this->id]);
|
||||
$name = $this->get_response_fieldname();
|
||||
if (isset($response[$name])) {
|
||||
$responses = $response[$name];
|
||||
} else {
|
||||
$responses = optional_param($name, '', PARAM_TEXT);
|
||||
}
|
||||
$responses = preg_replace('[^a-zA-Z0-9,_-]', '', $responses);
|
||||
$responses = explode(',', $responses); // convert to array
|
||||
$responses = array_filter($responses); // remove blanks
|
||||
$responses = array_unique($responses); // remove duplicates
|
||||
|
||||
@ -74,23 +134,25 @@ class qtype_ordering_question extends question_graded_automatically {
|
||||
}
|
||||
}
|
||||
|
||||
if (! $options = $DB->get_record ('question_ordering', array('question' => $this->id))) {
|
||||
$options = (object)array('logical' => 0); // shouldn't happen !!
|
||||
}
|
||||
if (! $answers = $DB->get_records ('question_answers', array('question' => $this->id), 'fraction,id')) {
|
||||
$answers = array(); // shouldn't happen !!
|
||||
}
|
||||
$ordering = $this->get_ordering_options();
|
||||
$answers = $this->get_ordering_answers();
|
||||
|
||||
if ($options->logical==0) {
|
||||
if ($ordering->logical==0) {
|
||||
$total = count($answers); // require all answers
|
||||
} else {
|
||||
$total = $options->studentsee + 2; // a subset of answers
|
||||
$total = $ordering->studentsee + 2; // a subset of answers
|
||||
}
|
||||
|
||||
if (isset($CFG->passwordsaltmain)) {
|
||||
$salt = $CFG->passwordsaltmain;
|
||||
} else {
|
||||
$salt = ''; // complex_random_string()
|
||||
}
|
||||
|
||||
$validresponses = array();
|
||||
foreach ($answers as $answerid => $answer) {
|
||||
|
||||
$response = md5($CFG->passwordsaltmain.$answer->answer);
|
||||
$response = md5($salt.$answer->answer);
|
||||
$sortorder = intval($answer->fraction);
|
||||
|
||||
if (in_array($response, $responses)) {
|
||||
@ -165,7 +227,7 @@ class qtype_ordering_question extends question_graded_automatically {
|
||||
return array($fraction, question_state::graded_state_for_fraction($fraction));
|
||||
}
|
||||
|
||||
public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) {
|
||||
|
||||
public function check_file_access($qa, $ordering, $component, $filearea, $args, $forcedownload) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
@ -41,9 +41,10 @@ if (class_exists('question_type')) {
|
||||
*/
|
||||
class qtype_ordering extends question_type {
|
||||
|
||||
public function is_not_blank($value) {
|
||||
$value = trim($value);
|
||||
return ($value || $value==='0');
|
||||
protected function initialise_question_instance(question_definition $question, $questiondata) {
|
||||
parent::initialise_question_instance($question, $questiondata);
|
||||
$answers = array_keys($questiondata->options->answers);
|
||||
$question->rightanswer = implode(',', $answers);
|
||||
}
|
||||
|
||||
public function save_question_options($question) {
|
||||
@ -128,6 +129,11 @@ class qtype_ordering extends question_type {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function is_not_blank($value) {
|
||||
$value = trim($value);
|
||||
return ($value || $value==='0');
|
||||
}
|
||||
|
||||
public function get_question_options($question) {
|
||||
global $DB, $OUTPUT;
|
||||
|
||||
|
@ -35,53 +35,69 @@ class qtype_ordering_renderer extends qtype_renderer {
|
||||
|
||||
public function formulation_and_controls(question_attempt $qa, question_display_options $options) {
|
||||
global $CFG, $DB;
|
||||
|
||||
static $addStyle = true;
|
||||
static $addScript = true;
|
||||
|
||||
$question = $qa->get_question();
|
||||
$ordering = $question->get_ordering_options();
|
||||
$answers = $question->get_ordering_answers();
|
||||
|
||||
if (! $options = $DB->get_record('question_ordering', array('question' => $question->id))) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (! $answers = $DB->get_records('question_answers', array('question' => $question->id), '', '*')) {
|
||||
if (empty($ordering) || empty($answers)) {
|
||||
return ''; // shouldn't happen !!
|
||||
}
|
||||
|
||||
if ($options->studentsee==0) { // all items
|
||||
$options->studentsee = count($answers);
|
||||
if ($ordering->studentsee==0) { // all items
|
||||
$ordering->studentsee = count($answers);
|
||||
} else {
|
||||
// a nasty hack so that "studentsee" is the same
|
||||
// as what is displayed by edit_ordering_form.php
|
||||
$options->studentsee += 2;
|
||||
$ordering->studentsee += 2;
|
||||
}
|
||||
|
||||
switch ($options->logical) {
|
||||
|
||||
case 0: // all
|
||||
$answerids = array_keys($answers);
|
||||
break;
|
||||
|
||||
case 1: // random subset
|
||||
$answerids = array_rand($answers, $options->studentsee);
|
||||
break;
|
||||
|
||||
case 2: // contiguous subset
|
||||
if (count($answers) > $options->studentsee) {
|
||||
$offset = mt_rand(0, count($answers) - $options->studentsee);
|
||||
$answers = array_slice($answers, $offset, $options->studentsee, true);
|
||||
}
|
||||
$answerids = array_keys($answers);
|
||||
break;
|
||||
if ($options->readonly || $options->correctness) {
|
||||
// don't allow items to be dragged and dropped
|
||||
$readonly = true;
|
||||
} else {
|
||||
$readonly = false;
|
||||
}
|
||||
shuffle($answerids);
|
||||
|
||||
if ($options->correctness) {
|
||||
list($answerids, $correctorder) = $this->get_response($qa, $question, $answers);
|
||||
} else {
|
||||
$correctorder = array();
|
||||
switch ($ordering->logical) {
|
||||
case 0: // all
|
||||
$answerids = array_keys($answers);
|
||||
break;
|
||||
|
||||
case 1: // random subset
|
||||
$answerids = array_rand($answers, $ordering->studentsee);
|
||||
break;
|
||||
|
||||
case 2: // contiguous subset
|
||||
if (count($answers) > $ordering->studentsee) {
|
||||
$offset = mt_rand(0, count($answers) - $ordering->studentsee);
|
||||
$answers = array_slice($answers, $offset, $ordering->studentsee, true);
|
||||
}
|
||||
$answerids = array_keys($answers);
|
||||
break;
|
||||
}
|
||||
shuffle($answerids);
|
||||
}
|
||||
|
||||
$response_name = $qa->get_qt_field_name($question->get_response_fieldname());
|
||||
$response_id = 'id_'.preg_replace('/[^a-zA-Z0-9]+/', '_', $response_name);
|
||||
$sortable_id = 'id_sortable_'.$question->id;
|
||||
|
||||
$result = '';
|
||||
$result .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/js/jquery.js'));
|
||||
$result .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/js/jquery-ui.js'));
|
||||
if ($readonly==false) {
|
||||
$result .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/js/jquery.js'));
|
||||
$result .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/js/jquery-ui.js'));
|
||||
}
|
||||
|
||||
$style = "\n";
|
||||
$style .= "ul.sortable".$question->id." li {\n";
|
||||
$style .= "ul#$sortable_id li {\n";
|
||||
$style .= " position: relative;\n";
|
||||
$style .= "}\n";
|
||||
if ($addStyle) {
|
||||
@ -101,7 +117,9 @@ class qtype_ordering_renderer extends qtype_renderer {
|
||||
$style .= " background-color: #eeeeee;\n";
|
||||
$style .= " border: 1px solid #cccccc;\n";
|
||||
$style .= " border-image: initial;\n";
|
||||
$style .= " cursor: move;\n";
|
||||
if ($readonly==false) {
|
||||
$style .= " cursor: move;\n";
|
||||
}
|
||||
$style .= " list-style-type: none;\n";
|
||||
$style .= " margin-bottom: 1px;\n";
|
||||
$style .= " min-height: 20px;\n";
|
||||
@ -110,92 +128,109 @@ class qtype_ordering_renderer extends qtype_renderer {
|
||||
}
|
||||
$result .= html_writer::tag('style', $style, array('type' => 'text/css'));
|
||||
|
||||
$script = "\n";
|
||||
$script .= "//<![CDATA[\n";
|
||||
$script .= "$(function() {\n";
|
||||
$script .= " $('#sortable".$question->id."').sortable({\n";
|
||||
$script .= " update: function(event, ui) {\n";
|
||||
$script .= " var ItemsOrder = $(this).sortable('toArray').toString();\n";
|
||||
$script .= " $('#q".$question->id."').attr('value', ItemsOrder);\n";
|
||||
$script .= " }\n";
|
||||
$script .= " });\n";
|
||||
$script .= " $('#sortable".$question->id."').disableSelection();\n";
|
||||
$script .= "});\n";
|
||||
$script .= "$(document).ready(function() {\n";
|
||||
$script .= " var ItemsOrder = $('#sortable".$question->id."').sortable('toArray').toString();\n";
|
||||
$script .= " $('#q".$question->id."').attr('value', ItemsOrder);\n";
|
||||
$script .= "});\n";
|
||||
$script .= "//]]>\n";
|
||||
$result .= html_writer::tag('script', $script, array('type' => 'text/javascript'));
|
||||
|
||||
$result .= html_writer::tag('div', stripslashes($question->format_questiontext($qa)), array('class' => 'qtext'));
|
||||
$result .= html_writer::start_tag('div', array('class' => 'ablock'));
|
||||
$result .= html_writer::start_tag('div', array('class' => 'answer'));
|
||||
$result .= html_writer::start_tag('ul', array('class' => 'boxy', 'id' => 'sortable'.$question->id));
|
||||
|
||||
// a salt (=random string) to help disguise answer ids
|
||||
if (isset($CFG->passwordsaltmain)) {
|
||||
$salt = $CFG->passwordsaltmain;
|
||||
} else {
|
||||
$salt = complex_random_string();
|
||||
}
|
||||
|
||||
// generate ordering items
|
||||
foreach ($answerids as $i => $answerid) {
|
||||
// the original "id" revealed the correct order of the answers
|
||||
// because $answer->fraction holds the correct order number
|
||||
// $id = 'ordering_item_'.$answerid.'_'.intval($answers[$answerid]->fraction);
|
||||
$id = 'ordering_item_'.md5($salt.$answers[$answerid]->answer);
|
||||
$params = array('class' => 'ui-state-default', 'id' => $id);
|
||||
$result .= html_writer::tag('li', $answers[$answerid]->answer, $params);
|
||||
}
|
||||
|
||||
$result .= html_writer::end_tag('ul');
|
||||
$result .= html_writer::end_tag('div'); // answer
|
||||
$result .= html_writer::end_tag('div'); // ablock
|
||||
|
||||
$result .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'q'.$question->id, 'id' => 'q'.$question->id, 'value' => '9'));
|
||||
$result .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'answer', 'value' => ''));
|
||||
|
||||
$result .= html_writer::tag('div', '', array('style' => 'clear:both;'));
|
||||
|
||||
$script = "\n";
|
||||
$script .= "//<![CDATA[\n";
|
||||
if ($addScript) {
|
||||
$addScript = false; // only add these functions once
|
||||
$script .= "function orderingTouchHandler(event) {\n";
|
||||
$script .= " var touch = event.changedTouches[0];\n";
|
||||
$script .= " switch (event.type) {\n";
|
||||
$script .= " case 'touchstart': var type = 'mousedown'; break;\n";
|
||||
$script .= " case 'touchmove': var type = 'mousemove'; event.preventDefault(); break;\n";
|
||||
$script .= " case 'touchend': var type = 'mouseup'; break;\n";
|
||||
$script .= " default: return;\n";
|
||||
$script .= " }\n";
|
||||
$script .= " var simulatedEvent = document.createEvent('MouseEvent');\n";
|
||||
$script .= " // initMouseEvent(type, canBubble, cancelable, view, clickCount, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget)\n";
|
||||
$script .= " simulatedEvent.initMouseEvent(type, true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);\n";
|
||||
$script .= " touch.target.dispatchEvent(simulatedEvent);\n";
|
||||
$script .= " event.preventDefault();\n";
|
||||
$script .= "}\n";
|
||||
$script .= "function orderingInit(sortableid) {\n";
|
||||
$script .= " var obj = document.getElementById(sortableid);\n";
|
||||
$script .= " if (obj) {\n";
|
||||
$script .= " for (var i=0; i<obj.childNodes.length; i++) {\n";
|
||||
$script .= " obj.childNodes.item(i).addEventListener('touchstart', orderingTouchHandler, false);\n";
|
||||
$script .= " obj.childNodes.item(i).addEventListener('touchmove', orderingTouchHandler, false);\n";
|
||||
$script .= " obj.childNodes.item(i).addEventListener('touchend', orderingTouchHandler, false);\n";
|
||||
$script .= " obj.childNodes.item(i).addEventListener('touchcancel', orderingTouchHandler, false);\n";
|
||||
if ($readonly==false) {
|
||||
$script = "\n";
|
||||
$script .= "//<![CDATA[\n";
|
||||
$script .= "$(function() {\n";
|
||||
$script .= " $('#$sortable_id').sortable({\n";
|
||||
$script .= " update: function(event, ui) {\n";
|
||||
$script .= " var ItemsOrder = $(this).sortable('toArray').toString();\n";
|
||||
$script .= " $('#$response_id').attr('value', ItemsOrder);\n";
|
||||
$script .= " }\n";
|
||||
$script .= " obj = null;\n";
|
||||
$script .= " } else {\n";
|
||||
$script .= " // try again in 1/2 a second - shouldn't be necessary !!\n";
|
||||
$script .= " setTimeout(new Function('orderingInit(".'"'."'+sortableid+'".'"'.")'), 500);\n";
|
||||
$script .= " }\n";
|
||||
$script .= "}\n";
|
||||
$script .= " });\n";
|
||||
$script .= " $('#$sortable_id').disableSelection();\n";
|
||||
$script .= "});\n";
|
||||
$script .= "$(document).ready(function() {\n";
|
||||
$script .= " var ItemsOrder = $('#$sortable_id').sortable('toArray').toString();\n";
|
||||
$script .= " $('#$response_id').attr('value', ItemsOrder);\n";
|
||||
$script .= "});\n";
|
||||
$script .= "//]]>\n";
|
||||
$result .= html_writer::tag('script', $script, array('type' => 'text/javascript'));
|
||||
}
|
||||
|
||||
$result .= html_writer::tag('div', $question->format_questiontext($qa), array('class' => 'qtext'));
|
||||
|
||||
if (count($answerids)) {
|
||||
$result .= html_writer::start_tag('div', array('class' => 'ablock'));
|
||||
$result .= html_writer::start_tag('div', array('class' => 'answer'));
|
||||
$result .= html_writer::start_tag('ul', array('class' => 'boxy', 'id' => $sortable_id));
|
||||
|
||||
// generate ordering items
|
||||
foreach ($answerids as $position => $answerid) {
|
||||
if (array_key_exists($answerid, $answers)) {
|
||||
if ($options->correctness) {
|
||||
if ($correctorder[$position]==$answerid) {
|
||||
$class = 'correctposition';
|
||||
$img = $this->feedback_image(1);
|
||||
} else {
|
||||
$class = 'wrongposition';
|
||||
$img = $this->feedback_image(0);
|
||||
}
|
||||
$img = "$img ";
|
||||
} else {
|
||||
$class = 'ui-state-default';
|
||||
$img = '';
|
||||
}
|
||||
// the original "id" revealed the correct order of the answers
|
||||
// because $answer->fraction holds the correct order number
|
||||
// $id = 'ordering_item_'.$answerid.'_'.intval($answers[$answerid]->fraction);
|
||||
$params = array('class' => $class, 'id' => $answers[$answerid]->md5key);
|
||||
|
||||
$result .= html_writer::tag('li', $img.$answers[$answerid]->answer, $params);
|
||||
}
|
||||
}
|
||||
|
||||
$result .= html_writer::end_tag('ul');
|
||||
$result .= html_writer::end_tag('div'); // answer
|
||||
$result .= html_writer::end_tag('div'); // ablock
|
||||
|
||||
$params = array('type' => 'hidden', 'name' => $response_name, 'id' => $response_id, 'value' => '');
|
||||
$result .= html_writer::empty_tag('input', $params);
|
||||
$result .= html_writer::tag('div', '', array('style' => 'clear:both;'));
|
||||
}
|
||||
|
||||
if ($readonly==false) {
|
||||
$script = "\n";
|
||||
$script .= "//<![CDATA[\n";
|
||||
if ($addScript) {
|
||||
$addScript = false; // only add these functions once
|
||||
$script .= "function orderingTouchHandler(evt) {\n";
|
||||
$script .= " var touchEvt = evt.changedTouches[0];\n";
|
||||
$script .= " switch (evt.type) {\n";
|
||||
$script .= " case 'touchstart': var type = 'mousedown'; break;\n";
|
||||
$script .= " case 'touchmove': var type = 'mousemove'; break;\n";
|
||||
$script .= " case 'touchend': var type = 'mouseup'; break;\n";
|
||||
$script .= " default: return;\n";
|
||||
$script .= " }\n";
|
||||
$script .= " var mouseEvt = document.createEvent('MouseEvent');\n";
|
||||
$script .= " // initMouseEvent(type, canBubble, cancelable, view, clickCount, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget)\n";
|
||||
$script .= " mouseEvt.initMouseEvent(type, true, true, window, 1, touchEvt.screenX, touchEvt.screenY, touchEvt.clientX, touchEvt.clientY, false, false, false, false, 0, null);\n";
|
||||
$script .= " touchEvt.target.dispatchEvent(mouseEvt);\n";
|
||||
$script .= " evt.preventDefault();\n";
|
||||
$script .= "}\n";
|
||||
$script .= "function orderingTouchHandlers(sortableid) {\n";
|
||||
$script .= " var obj = document.getElementById(sortableid);\n";
|
||||
$script .= " if (obj) {\n";
|
||||
$script .= " for (var i=0; i<obj.childNodes.length; i++) {\n";
|
||||
$script .= " obj.childNodes.item(i).addEventListener('touchstart', orderingTouchHandler, false);\n";
|
||||
$script .= " obj.childNodes.item(i).addEventListener('touchmove', orderingTouchHandler, false);\n";
|
||||
$script .= " obj.childNodes.item(i).addEventListener('touchend', orderingTouchHandler, false);\n";
|
||||
$script .= " obj.childNodes.item(i).addEventListener('touchcancel', orderingTouchHandler, false);\n";
|
||||
$script .= " }\n";
|
||||
$script .= " obj = null;\n";
|
||||
$script .= " } else {\n";
|
||||
$script .= " // try again in 1/2 a second - shouldn't be necessary !!\n";
|
||||
$script .= " setTimeout(new Function('orderingTouchHandlers(".'"'."'+sortableid+'".'"'.")'), 500);\n";
|
||||
$script .= " }\n";
|
||||
$script .= "}\n";
|
||||
}
|
||||
$script .= "if (document.body.ontouchstart) {\n";
|
||||
$script .= " orderingTouchHandlers('$sortable_id');\n";
|
||||
$script .= "}\n";
|
||||
|
||||
$script .= "//]]>\n";
|
||||
$result .= html_writer::tag('script', $script, array('type' => 'text/javascript'));
|
||||
}
|
||||
$script .= "orderingInit('sortable".$question->id."');\n";
|
||||
$script .= "//]]>\n";
|
||||
$result .= html_writer::tag('script', $script, array('type' => 'text/javascript'));
|
||||
|
||||
return $result;
|
||||
}
|
||||
@ -203,21 +238,75 @@ class qtype_ordering_renderer extends qtype_renderer {
|
||||
public function correct_response(question_attempt $qa) {
|
||||
global $DB;
|
||||
|
||||
$question = $qa->get_question();
|
||||
$output = '';
|
||||
|
||||
if (! $step = $DB->get_records('question_attempt_steps', array('questionattemptid' => $question->contextid), 'id DESC')) {
|
||||
return ''; // shouldn't happen !!
|
||||
}
|
||||
$step = current($step); // first one
|
||||
|
||||
if ($step->fraction >= 1) {
|
||||
$feedback = get_string('correctfeedback', 'qtype_ordering');
|
||||
} else if ($step->fraction > 0) {
|
||||
$feedback = get_string('partiallycorrectfeedback', 'qtype_ordering').' '.round($step->fraction, 2);
|
||||
} else {
|
||||
$feedback = get_string('incorrectfeedback', 'qtype_ordering');
|
||||
$showcorrect = false;
|
||||
if ($step = $qa->get_last_step()) {
|
||||
switch ($step->get_state()) {
|
||||
case 'gradedright':
|
||||
$msg = get_string('correctfeedback', 'qtype_ordering');
|
||||
break;
|
||||
case 'gradedpartial':
|
||||
$showcorrect = true;
|
||||
$fraction = round($step->get_fraction(), 2);
|
||||
$msg = get_string('partiallycorrectfeedback', 'qtype_ordering', $fraction);
|
||||
break;
|
||||
case 'gradedwrong':
|
||||
$showcorrect = true;
|
||||
$msg = get_string('incorrectfeedback', 'qtype_ordering');
|
||||
break;
|
||||
default:
|
||||
$msg = '';
|
||||
}
|
||||
if ($msg) {
|
||||
$output .= html_writer::tag('p', $msg);
|
||||
}
|
||||
}
|
||||
|
||||
return $feedback;
|
||||
if ($showcorrect) {
|
||||
$question = $qa->get_question();
|
||||
$answers = $question->get_ordering_answers();
|
||||
list($answerids, $correctorder) = $this->get_response($qa, $question, $answers);
|
||||
if (count($correctorder)) {
|
||||
$output .= html_writer::tag('p', get_string('correctorder', 'qtype_ordering'));
|
||||
$output .= html_writer::start_tag('ol');
|
||||
foreach ($correctorder as $position => $answerid) {
|
||||
$output .= html_writer::tag('li', $answers[$answerid]->answer);
|
||||
}
|
||||
$output .= html_writer::end_tag('ol');
|
||||
} else {
|
||||
$output .= html_writer::tag('p', get_string('noresponsedetails', 'qtype_ordering'));
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function get_response($qa, $question, $answers) {
|
||||
$answerids = array();
|
||||
$correctorder = array();
|
||||
|
||||
$name = $question->get_response_fieldname();
|
||||
if ($step = $qa->get_last_step_with_qt_var($name)) {
|
||||
|
||||
$response = $step->get_qt_var($name); // "$md5key, ..."
|
||||
$response = explode(',', $response); // array($position => $md5key, ...)
|
||||
$response = array_flip($response); // array($md5key => $position, ...)
|
||||
|
||||
foreach ($answers as $answer) {
|
||||
if (array_key_exists($answer->md5key, $response)) {
|
||||
$position = $response[$answer->md5key];
|
||||
$sortorder = intval($answer->fraction);
|
||||
$answerids[$position] = $answer->id;
|
||||
$correctorder[$sortorder] = $answer->id;
|
||||
}
|
||||
}
|
||||
|
||||
ksort($answerids);
|
||||
ksort($correctorder);
|
||||
$correctorder = array_values($correctorder);
|
||||
}
|
||||
|
||||
return array($answerids, $correctorder);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
ul.boxy li.correctposition {
|
||||
border: 4px solid #99ff66; /* gentle green */
|
||||
}
|
||||
ul.boxy li.wrongposition {
|
||||
border: 4px solid #ff7373; /* gentle red */
|
||||
}
|
||||
ul.boxy li.correctposition,
|
||||
ul.boxy li.wrongposition {
|
||||
margin-bottom: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
@ -30,6 +30,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
$plugin->cron = 0;
|
||||
$plugin->component = 'qtype_ordering';
|
||||
$plugin->maturity = MATURITY_STABLE; // ALPHA=50, BETA=100, RC=150, STABLE=200
|
||||
$plugin->release = '2014-05-14.01';
|
||||
$plugin->version = 2014051401;
|
||||
$plugin->release = '2014-07-26 (02)';
|
||||
$plugin->version = 2014072602;
|
||||
$plugin->requires = 2010112400; // Moodle 2.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user