diff --git a/question/type/multianswer/edit_multianswer_form.php b/question/type/multianswer/edit_multianswer_form.php index a06b79f8202..86692fd19f0 100644 --- a/question/type/multianswer/edit_multianswer_form.php +++ b/question/type/multianswer/edit_multianswer_form.php @@ -32,8 +32,13 @@ class question_edit_multianswer_form extends question_edit_form { $this->questiondisplay = ""; } - if ( isset($this->questiondisplay->options->questions) && count($this->questiondisplay->options->questions) > 0 ) { - $countsubquestions = count($this->questiondisplay->options->questions); + if ( isset($this->questiondisplay->options->questions) && is_array($this->questiondisplay->options->questions) ) { + $countsubquestions =0; + foreach($this->questiondisplay->options->questions as $subquestion){ + if ($subquestion != ''){ + $countsubquestions++; + } + } } else { $countsubquestions =0; } @@ -83,6 +88,7 @@ class question_edit_multianswer_form extends question_edit_form { if (isset($question->id) and $question->id and $question->qtype and $question->questiontext) { foreach ($question->options->questions as $key => $wrapped) { + if($wrapped != ''){ // The old way of restoring the definitions is kept to gradually // update all multianswer questions if (empty($wrapped->questiontext)) { @@ -126,6 +132,7 @@ class question_edit_multianswer_form extends question_edit_form { $question->questiontext = str_replace("{#$key}", $parsableanswerdef, $question->questiontext); } } + } // set default to $questiondisplay questions elements if (isset($this->questiondisplay->options->questions)) { @@ -230,7 +237,7 @@ class question_edit_multianswer_form extends question_edit_form { $sub++; } } else { - $errors['questiontext']=get_string('questions missing', 'question'); + $errors['questiontext']=get_string('questionsmissing', 'qtype_multianswer'); } } diff --git a/question/type/multianswer/questiontype.php b/question/type/multianswer/questiontype.php index bb266c4a378..e8955b738e9 100644 --- a/question/type/multianswer/questiontype.php +++ b/question/type/multianswer/questiontype.php @@ -28,8 +28,9 @@ class embedded_cloze_qtype extends default_questiontype { // Get relevant data indexed by positionkey from the multianswers table if (!$sequence = get_field('question_multianswer', 'sequence', 'question', $question->id)) { - notify('Error: Cloze question '.$question->id.' is missing question options!'); - return false; + notify(get_string('noquestions','qtype_multianswer',$question->name)); + $question->options->questions['1']= ''; + return true ; } $wrappedquestions = get_records_list('question', 'id', $sequence, 'id ASC'); @@ -37,17 +38,31 @@ class embedded_cloze_qtype extends default_questiontype { // We want an array with question ids as index and the positions as values $sequence = array_flip(explode(',', $sequence)); array_walk($sequence, create_function('&$val', '$val++;')); - + //If a question is lost, the corresponding index is null + // so this null convention is used to test $question->options->questions + // before using the values. + // first all possible questions from sequence are nulled + // then filled with the data if available in $wrappedquestions + $nbvaliquestion = 0 ; + foreach($sequence as $seq){ + $question->options->questions[$seq]= ''; + } + if (isset($wrappedquestions) && is_array($wrappedquestions)){ foreach ($wrappedquestions as $wrapped) { if (!$QTYPES[$wrapped->qtype]->get_question_options($wrapped)) { notify("Unable to get options for questiontype {$wrapped->qtype} (id={$wrapped->id})"); - } + }else { // for wrapped questions the maxgrade is always equal to the defaultgrade, // there is no entry in the question_instances table for them $wrapped->maxgrade = $wrapped->defaultgrade; - + $nbvaliquestion++ ; $question->options->questions[$sequence[$wrapped->id]] = clone($wrapped); // ??? Why do we need a clone here? } + } + } + if ($nbvaliquestion == 0 ) { + notify(get_string('noquestions','qtype_multianswer',$question->name)); + } return true; } @@ -65,31 +80,36 @@ class embedded_cloze_qtype extends default_questiontype { // First we get all the existing wrapped questions if (!$oldwrappedids = get_field('question_multianswer', 'sequence', 'question', $question->id)) { - $oldwrappedids = array(); + $oldwrappedquestions = array(); } else { - $oldwrappedids = explode(',', $oldwrappedids); + $oldwrappedquestions = get_records_list('question', 'id', $oldwrappedids, 'id ASC'); } $sequence = array(); foreach($question->options->questions as $wrapped) { + if ($wrapped != ''){ // if we still have some old wrapped question ids, reuse the next of them - if ($oldwrappedid = array_shift($oldwrappedids)) { - $wrapped->id = $oldwrappedid; - $oldqtype = get_field('question', 'qtype', 'id',$oldwrappedid) ; - if($oldqtype != $wrapped->qtype ) { - switch ($oldqtype) { + + if (is_array($oldwrappedquestions) && $oldwrappedquestion = array_shift($oldwrappedquestions)) { + $wrapped->id = $oldwrappedquestion->id; + if($oldwrappedquestion->qtype != $wrapped->qtype ) { + switch ($oldwrappedquestion->qtype) { case 'multichoice': - delete_records('question_multichoice', 'question', $oldwrappedid); + delete_records('question_multichoice', 'question' , $oldwrappedquestion->id ); break; case 'shortanswer': - delete_records('question_shortanswer', 'question', $oldwrappedid); + delete_records('question_shortanswer', 'question' , $oldwrappedquestion->id ); break; case 'numerical': - delete_records('question_numerical', 'question', $oldwrappedid); + delete_records('question_numerical', 'question' , $oldwrappedquestion->id ); break; default: error("questiontype $wrapped->qtype not recognized"); + $wrapped->id = 0 ; } } + }else { + $wrapped->id = 0 ; + } } $wrapped->name = $question->name; $wrapped->parent = $question->id; @@ -197,6 +217,7 @@ class embedded_cloze_qtype extends default_questiontype { global $QTYPES; $responses = array(); foreach($question->options->questions as $key => $wrapped) { + if ($wrapped != ''){ if ($correct = $QTYPES[$wrapped->qtype]->get_correct_responses($wrapped, $state)) { $responses[$key] = $correct['']; } else { @@ -206,6 +227,7 @@ class embedded_cloze_qtype extends default_questiontype { return null; } } + } return $responses; } @@ -247,9 +269,10 @@ class embedded_cloze_qtype extends default_questiontype { $qtextremaining = $qtextsplits[1]; $positionkey = $regs[1]; + if (isset($question->options->questions[$positionkey]) && $question->options->questions[$positionkey] != ''){ $wrapped = &$question->options->questions[$positionkey]; $answers = &$wrapped->options->answers; - $correctanswers = $QTYPES[$wrapped->qtype]->get_correct_responses($wrapped, $state); + // $correctanswers = $QTYPES[$wrapped->qtype]->get_correct_responses($wrapped, $state); $inputname = $nameprefix.$positionkey; if (isset($state->responses[$positionkey])) { @@ -262,6 +285,38 @@ class embedded_cloze_qtype extends default_questiontype { $popup = ''; $style = ''; $feedbackimg = ''; + $feedback = '' ; + $correctanswer = ''; + $strfeedbackwrapped = $strfeedback; + // if($wrapped->qtype == 'numerical' ||$wrapped->qtype == 'shortanswer'){ + $testedstate = clone($state); + if ($correctanswers = $QTYPES[$wrapped->qtype]->get_correct_responses($wrapped, $testedstate)) { + if ($options->readonly && $options->correct_responses) { + $delimiter = ''; + if ($correctanswers) { + foreach ($correctanswers as $ca) { + switch($wrapped->qtype){ + case 'numerical': + case 'shortanswer': + $correctanswer .= $delimiter.$ca; + break ; + case 'multichoice': + if (isset($answers[$ca])){ + $correctanswer .= $delimiter.$answers[$ca]->answer; + } + break ; + } + $delimiter = ', '; + } + } + } + if ($correctanswer) { + $feedback = '
'; + $feedback .= get_string('correctansweris', 'quiz', s($correctanswer, true)); + $feedback .= '
'; + // $strfeedbackwrapped = get_string('correctanswer and', 'quiz').get_string('feedback', 'quiz'); + } + } if ($options->feedback) { $chosenanswer = null; switch ($wrapped->qtype) { @@ -294,8 +349,13 @@ class embedded_cloze_qtype extends default_questiontype { } if (!empty($chosenanswer->feedback)) { - $feedback = s(str_replace(array("\\", "'"), array("\\\\", "\\'"), $chosenanswer->feedback)); - $popup = " onmouseover=\"return overlib('$feedback', STICKY, MOUSEOFF, CAPTION, '$strfeedback', FGCOLOR, '#FFFFFF');\" ". + $feedback = s(str_replace(array("\\", "'"), array("\\\\", "\\'"), $feedback.$chosenanswer->feedback)); + if ($options->readonly && $options->correct_responses) { + $strfeedbackwrapped = get_string('correctanswerandfeedback', 'qtype_multianswer'); + }else { + $strfeedbackwrapped = get_string('feedback', 'quiz'); + } + $popup = " onmouseover=\"return overlib('$feedback', STICKY, MOUSEOFF, CAPTION, '$strfeedbackwrapped', FGCOLOR, '#FFFFFF');\" ". " onmouseout=\"return nd();\" "; } @@ -308,6 +368,12 @@ class embedded_cloze_qtype extends default_questiontype { $feedbackimg = ''; } } + if ($feedback !='' && $popup == ''){ + $strfeedbackwrapped = get_string('correctanswer', 'qtype_multianswer'); + $feedback = s(str_replace(array("\\", "'"), array("\\\\", "\\'"), $feedback)); + $popup = " onmouseover=\"return overlib('$feedback', STICKY, MOUSEOFF, CAPTION, '$strfeedbackwrapped', FGCOLOR, '#FFFFFF');\" ". + " onmouseout=\"return nd();\" "; + } // Print the input control switch ($wrapped->qtype) { @@ -384,6 +450,14 @@ class embedded_cloze_qtype extends default_questiontype { } echo ""; // MDL-7497 } + else { + if(! isset($question->options->questions[$positionkey])){ + echo $regs[0].""; + }else { + echo '
'.get_string('questionnotfound','qtype_multianswer',$positionkey).'
'; + } + } + } // Print the final piece of question text: echo $qtextremaining; @@ -396,6 +470,7 @@ class embedded_cloze_qtype extends default_questiontype { $teststate = clone($state); $state->raw_grade = 0; foreach($question->options->questions as $key => $wrapped) { + if ($wrapped != ''){ $state->responses[$key] = $state->responses[$key]; $teststate->responses = array('' => $state->responses[$key]); $teststate->raw_grade = 0; @@ -405,6 +480,7 @@ class embedded_cloze_qtype extends default_questiontype { } $state->raw_grade += $teststate->raw_grade; } + } $state->raw_grade /= $question->defaultgrade; $state->raw_grade = min(max((float) $state->raw_grade, 0.0), 1.0) * $question->maxgrade; @@ -724,6 +800,7 @@ function qtype_multianswer_extract_question($text) { } else if(!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE])) { $wrapped->qtype = 'multichoice'; $wrapped->single = 1; + $wrapped->answernumbering = 0; $wrapped->correctfeedback = ''; $wrapped->partiallycorrectfeedback = ''; $wrapped->incorrectfeedback = '';