MDL-20636 Fix a lot more coding style issues.

This commit is contained in:
Tim Hunt 2011-05-20 20:18:04 +01:00
parent e0736817f0
commit 59a3fcd3eb
9 changed files with 601 additions and 750 deletions

View file

@ -59,7 +59,8 @@ class backup_qtype_multianswer_plugin extends backup_qtype_plugin {
$pluginwrapper->add_child($multianswer); $pluginwrapper->add_child($multianswer);
// set source to populate the data // set source to populate the data
$multianswer->set_source_table('question_multianswer', array('question' => backup::VAR_PARENTID)); $multianswer->set_source_table('question_multianswer',
array('question' => backup::VAR_PARENTID));
// don't need to annotate ids nor files // don't need to annotate ids nor files

View file

@ -38,7 +38,6 @@ class restore_qtype_multianswer_plugin extends restore_qtype_plugin {
* Returns the paths to be handled by the plugin at question level * Returns the paths to be handled by the plugin at question level
*/ */
protected function define_question_plugin_structure() { protected function define_question_plugin_structure() {
$paths = array(); $paths = array();
// This qtype uses question_answers, add them // This qtype uses question_answers, add them
@ -46,10 +45,9 @@ class restore_qtype_multianswer_plugin extends restore_qtype_plugin {
// Add own qtype stuff // Add own qtype stuff
$elename = 'multianswer'; $elename = 'multianswer';
$elepath = $this->get_pathfor('/multianswer'); // we used get_recommended_name() so this works $elepath = $this->get_pathfor('/multianswer');
$paths[] = new restore_path_element($elename, $elepath); $paths[] = new restore_path_element($elename, $elepath);
return $paths; // And we return the interesting paths return $paths; // And we return the interesting paths
} }
@ -67,7 +65,8 @@ class restore_qtype_multianswer_plugin extends restore_qtype_plugin {
$newquestionid = $this->get_new_parentid('question'); $newquestionid = $this->get_new_parentid('question');
$questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false; $questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
// If the question has been created by restore, we need to create its question_multianswer too // If the question has been created by restore, we need to create its
// question_multianswer too
if ($questioncreated) { if ($questioncreated) {
// Adjust some columns // Adjust some columns
$data->question = $newquestionid; $data->question = $newquestionid;
@ -79,8 +78,6 @@ class restore_qtype_multianswer_plugin extends restore_qtype_plugin {
$newitemid = $DB->insert_record('question_multianswer', $data); $newitemid = $DB->insert_record('question_multianswer', $data);
// Create mapping (need it for after_execute recode of sequence) // Create mapping (need it for after_execute recode of sequence)
$this->set_mapping('question_multianswer', $oldid, $newitemid); $this->set_mapping('question_multianswer', $oldid, $newitemid);
} else {
// Nothing to remap if the question already existed
} }
} }
@ -97,18 +94,21 @@ class restore_qtype_multianswer_plugin extends restore_qtype_plugin {
global $DB; global $DB;
// Now that all the questions have been restored, let's process // Now that all the questions have been restored, let's process
// the created question_multianswer sequences (list of question ids) // the created question_multianswer sequences (list of question ids)
$rs = $DB->get_recordset_sql("SELECT qma.id, qma.sequence $rs = $DB->get_recordset_sql("
FROM {question_multianswer} qma SELECT qma.id, qma.sequence
JOIN {backup_ids_temp} bi ON bi.newitemid = qma.question FROM {question_multianswer} qma
WHERE bi.backupid = ? JOIN {backup_ids_temp} bi ON bi.newitemid = qma.question
AND bi.itemname = 'question_created'", array($this->get_restoreid())); WHERE bi.backupid = ?
AND bi.itemname = 'question_created'",
array($this->get_restoreid()));
foreach ($rs as $rec) { foreach ($rs as $rec) {
$sequencearr = explode(',', $rec->sequence); $sequencearr = explode(',', $rec->sequence);
foreach ($sequencearr as $key => $question) { foreach ($sequencearr as $key => $question) {
$sequencearr[$key] = $this->get_mappingid('question', $question); $sequencearr[$key] = $this->get_mappingid('question', $question);
} }
$sequence = implode(',', $sequencearr); $sequence = implode(',', $sequencearr);
$DB->set_field('question_multianswer', 'sequence', $sequence, array('id' => $rec->id)); $DB->set_field('question_multianswer', 'sequence', $sequence,
array('id' => $rec->id));
} }
$rs->close(); $rs->close();
} }
@ -127,7 +127,8 @@ class restore_qtype_multianswer_plugin extends restore_qtype_plugin {
$answer = $state->answer; $answer = $state->answer;
$resultarr = array(); $resultarr = array();
// Get sequence of questions // Get sequence of questions
$sequence = $DB->get_field('question_multianswer', 'sequence', array('question' => $state->question)); $sequence = $DB->get_field('question_multianswer', 'sequence',
array('question' => $state->question));
$sequencearr = explode(',', $sequence); $sequencearr = explode(',', $sequence);
// Let's process each pair // Let's process each pair
foreach (explode(',', $answer) as $pair) { foreach (explode(',', $answer) as $pair) {

View file

@ -35,221 +35,257 @@ defined('MOODLE_INTERNAL') || die();
*/ */
class question_edit_multianswer_form extends question_edit_form { class question_edit_multianswer_form extends question_edit_form {
// $questiondisplay will contain the qtype_multianswer_extract_question from the questiontext // $questiondisplay will contain the qtype_multianswer_extract_question from
public $questiondisplay ; // the questiontext
// $savedquestiondisplay will contain the qtype_multianswer_extract_question from the questiontext in database public $questiondisplay;
public $savedquestion ; // $savedquestiondisplay will contain the qtype_multianswer_extract_question
public $savedquestiondisplay ; // from the questiontext in database
public $used_in_quiz = false ; public $savedquestion;
public $qtype_change = false ; public $savedquestiondisplay;
public $negative_diff = 0 ; public $used_in_quiz = false;
public $qtype_change = false;
public $negative_diff = 0;
public $nb_of_quiz = 0; public $nb_of_quiz = 0;
public $nb_of_attempts = 0; public $nb_of_attempts = 0;
public $confirm = 0 ; public $confirm = 0;
public $reload = false ; public $reload = false;
function question_edit_multianswer_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){ public function __construct($submiturl, $question, $category, $contexts, $formeditable = true) {
global $QTYPES, $SESSION, $CFG, $DB; global $SESSION, $CFG, $DB;
$this->regenerate = true; $this->regenerate = true;
if ( "1" == optional_param('reload','', PARAM_INT )) { if ("1" == optional_param('reload', '', PARAM_INT)) {
$this->reload = true ; $this->reload = true;
}else { } else {
$this->reload = false ; $this->reload = false;
} }
// $this->question = $question;
$this->used_in_quiz =false; $this->used_in_quiz = false;
// echo "<p> question <pre>";print_r($question);echo "</pre></p>";
if(isset($question->id) && $question->id != 0 ){ if (isset($question->id) && $question->id != 0) {
$this->savedquestiondisplay =fullclone($question ) ; $this->savedquestiondisplay = fullclone($question);
if ($list = $DB->get_records('quiz_question_instances', array( 'question'=> $question->id))){ if ($list = $DB->get_records('quiz_question_instances',
foreach($list as $key => $li){ array('question' => $question->id))) {
foreach ($list as $key => $li) {
$this->nb_of_quiz ++; $this->nb_of_quiz ++;
if($att = $DB->get_records('quiz_attempts',array( 'quiz'=> $li->quiz, 'preview'=> '0'))){ if ($att = $DB->get_records('quiz_attempts',
$this->nb_of_attempts+= count($att); array('quiz' => $li->quiz, 'preview' => '0'))) {
$this->nb_of_attempts += count($att);
$this->used_in_quiz = true; $this->used_in_quiz = true;
} }
} }
} }
} }
parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable); parent::__construct($submiturl, $question, $category, $contexts, $formeditable);
} }
protected function definition_inner($mform) { protected function definition_inner($mform) {
$mform->addElement('hidden', 'reload', 1); $mform->addElement('hidden', 'reload', 1);
// $mform->addElement('hidden', 'generalfeedback','');
$mform->setType('reload', PARAM_INT); $mform->setType('reload', PARAM_INT);
// Remove meaningless defaultgrade field. // Remove meaningless defaultgrade field.
$mform->removeElement('defaultgrade'); $mform->removeElement('defaultgrade');
$this->confirm = optional_param('confirm','0', PARAM_RAW); $this->confirm = optional_param('confirm', '0', PARAM_RAW);
// display the questions from questiontext; // display the questions from questiontext;
if ( "" != optional_param('questiontext','', PARAM_RAW)) { if ("" != optional_param('questiontext', '', PARAM_RAW)) {
// echo "<p> optional_param('questiontext' <pre>";print_r(optional_param('questiontext','', PARAM_RAW));echo "</pre></p>"; $this->questiondisplay = fullclone(qtype_multianswer_extract_question(
optional_param('questiontext', '', PARAM_RAW)));
$this->questiondisplay = fullclone(qtype_multianswer_extract_question(optional_param('questiontext','', PARAM_RAW))) ; } else {
if (!$this->reload && !empty($this->savedquestiondisplay->id)) {
}else {
if(!$this->reload && !empty($this->savedquestiondisplay->id)){
// use database data as this is first pass // use database data as this is first pass
// question->id == 0 so no stored datasets // question->id == 0 so no stored datasets
$this->questiondisplay = fullclone($this->savedquestiondisplay); $this->questiondisplay = fullclone($this->savedquestiondisplay);
foreach($this->questiondisplay->options->questions as $subquestion){ foreach ($this->questiondisplay->options->questions as $subquestion) {
if (!empty($subquestion)){ if (!empty($subquestion)) {
$subquestion->answer = array(''); $subquestion->answer = array('');
foreach($subquestion->options->answers as $ans){ foreach ($subquestion->options->answers as $ans) {
$subquestion->answer[]=$ans->answer ; $subquestion->answer[] = $ans->answer;
} }
// $subquestion->answer = fullclone($subquestion->options->answers);
} }
} }
}else { } else {
$this->questiondisplay = ""; $this->questiondisplay = "";
} }
} }
if ( isset($this->savedquestiondisplay->options->questions) && is_array($this->savedquestiondisplay->options->questions) ) { if (isset($this->savedquestiondisplay->options->questions) &&
$countsavedsubquestions =0; is_array($this->savedquestiondisplay->options->questions)) {
foreach($this->savedquestiondisplay->options->questions as $subquestion){ $countsavedsubquestions = 0;
if (!empty($subquestion)){ foreach ($this->savedquestiondisplay->options->questions as $subquestion) {
if (!empty($subquestion)) {
$countsavedsubquestions++; $countsavedsubquestions++;
} }
} }
} else { } else {
$countsavedsubquestions =0; $countsavedsubquestions = 0;
} }
if ($this->reload){ if ($this->reload) {
if ( isset($this->questiondisplay->options->questions) && is_array($this->questiondisplay->options->questions) ) { if (isset($this->questiondisplay->options->questions) &&
$countsubquestions =0; is_array($this->questiondisplay->options->questions)) {
foreach($this->questiondisplay->options->questions as $subquestion){ $countsubquestions = 0;
if (!empty($subquestion)){ foreach ($this->questiondisplay->options->questions as $subquestion) {
if (!empty($subquestion)) {
$countsubquestions++; $countsubquestions++;
} }
} }
} else { } else {
$countsubquestions =0; $countsubquestions = 0;
} }
}else{ } else {
$countsubquestions =$countsavedsubquestions ; $countsubquestions = $countsavedsubquestions;
} }
// echo "<p> count subquestion $countsubquestions <pre>";print_r($this->savedquestiondisplay);echo "</pre></p>";
// // echo "<p> saved question $countsubquestions <pre>";print_r($this->questiondisplay);echo "</pre></p>";
$mform->addElement('submit', 'analyzequestion',
$mform->addElement('submit', 'analyzequestion', get_string('decodeverifyquestiontext','qtype_multianswer')); get_string('decodeverifyquestiontext', 'qtype_multianswer'));
$mform->registerNoSubmitButton('analyzequestion'); $mform->registerNoSubmitButton('analyzequestion');
if ( $this->reload ){ if ($this->reload) {
$mform->addElement('html', '<div class="ablock clearfix">'); $mform->addElement('html', '<div class="ablock clearfix">');
$mform->addElement('html', '<div class=" clearfix">'); $mform->addElement('html', '<div class=" clearfix">');
for ($sub =1;$sub <=$countsubquestions ;$sub++) { for ($sub = 1; $sub <= $countsubquestions; $sub++) {
$this->editas[$sub] = 'unknown type'; $this->editas[$sub] = 'unknown type';
if (isset( $this->questiondisplay->options->questions[$sub]->qtype) ) { if (isset($this->questiondisplay->options->questions[$sub]->qtype)) {
$this->editas[$sub] = $this->questiondisplay->options->questions[$sub]->qtype ; $this->editas[$sub] = $this->questiondisplay->options->questions[$sub]->qtype;
} else if (optional_param('sub_'.$sub."_".'qtype', '', PARAM_RAW) != '') { } else if (optional_param('sub_'.$sub."_".'qtype', '', PARAM_RAW) != '') {
$this->editas[$sub] = optional_param('sub_'.$sub."_".'qtype', '', PARAM_RAW); $this->editas[$sub] = optional_param('sub_'.$sub."_".'qtype', '', PARAM_RAW);
} }
$storemess = ''; $storemess = '';
if(isset($this->savedquestiondisplay->options->questions[$sub]->qtype) && if (isset($this->savedquestiondisplay->options->questions[$sub]->qtype) &&
$this->savedquestiondisplay->options->questions[$sub]->qtype != $this->questiondisplay->options->questions[$sub]->qtype ){ $this->savedquestiondisplay->options->questions[$sub]->qtype !=
$this->qtype_change = true ; $this->questiondisplay->options->questions[$sub]->qtype) {
$storemess = "<font class=\"error\"> STORED QTYPE ".question_bank::get_qtype_name($this->savedquestiondisplay->options->questions[$sub]->qtype)."</font >"; $this->qtype_change = true;
} $storemess = "<font class=\"error\"> STORED QTYPE " .
question_bank::get_qtype_name(
$this->savedquestiondisplay->options->questions[$sub]->qtype).
"</font >";
}
$mform->addElement('header', 'subhdr'.$sub, get_string('questionno', 'question', $mform->addElement('header', 'subhdr'.$sub, get_string('questionno', 'question',
'{#'.$sub.'}').'&nbsp;'.question_bank::get_qtype_name($this->questiondisplay->options->questions[$sub]->qtype).$storemess); '{#'.$sub.'}').'&nbsp;'.question_bank::get_qtype_name(
$this->questiondisplay->options->questions[$sub]->qtype).$storemess);
$mform->addElement('static', 'sub_'.$sub."_".'questiontext', get_string('questiondefinition','qtype_multianswer'),array('cols'=>60, 'rows'=>3)); $mform->addElement('static', 'sub_'.$sub."_".'questiontext',
get_string('questiondefinition', 'qtype_multianswer'),
array('cols' => 60, 'rows' => 3));
if (isset ( $this->questiondisplay->options->questions[$sub]->questiontext)) { if (isset ($this->questiondisplay->options->questions[$sub]->questiontext)) {
$mform->setDefault('sub_'.$sub."_".'questiontext', $this->questiondisplay->options->questions[$sub]->questiontext['text']); $mform->setDefault('sub_'.$sub."_".'questiontext',
$this->questiondisplay->options->questions[$sub]->questiontext['text']);
} }
$mform->addElement('static', 'sub_'.$sub."_".'defaultgrade', get_string('defaultgrade', 'question')); $mform->addElement('static', 'sub_'.$sub."_".'defaultgrade',
$mform->setDefault('sub_'.$sub."_".'defaultgrade',$this->questiondisplay->options->questions[$sub]->defaultgrade); get_string('defaultgrade', 'question'));
$mform->setDefault('sub_'.$sub."_".'defaultgrade',
$this->questiondisplay->options->questions[$sub]->defaultgrade);
if ($this->questiondisplay->options->questions[$sub]->qtype =='shortanswer' ) { if ($this->questiondisplay->options->questions[$sub]->qtype == 'shortanswer') {
$mform->addElement('static', 'sub_'.$sub."_".'usecase', get_string('casesensitive', 'question')); $mform->addElement('static', 'sub_'.$sub."_".'usecase',
get_string('casesensitive', 'question'));
} }
if ($this->questiondisplay->options->questions[$sub]->qtype =='multichoice' ) { if ($this->questiondisplay->options->questions[$sub]->qtype == 'multichoice') {
$mform->addElement('static', 'sub_'.$sub."_".'layout', get_string('layout', 'qtype_multianswer'),array('cols'=>60, 'rows'=>1)) ;//, $gradeoptions); $mform->addElement('static', 'sub_'.$sub."_".'layout',
get_string('layout', 'qtype_multianswer'),
array('cols' => 60, 'rows' => 1));
} }
foreach ($this->questiondisplay->options->questions[$sub]->answer as $key =>$ans) { foreach ($this->questiondisplay->options->questions[$sub]->answer as $key => $ans) {
$mform->addElement('static', 'sub_'.$sub."_".'answer['.$key.']', get_string('answer', 'question'), array('cols'=>60, 'rows'=>1)); $mform->addElement('static', 'sub_'.$sub."_".'answer['.$key.']',
get_string('answer', 'question'), array('cols' => 60, 'rows' => 1));
if ($this->questiondisplay->options->questions[$sub]->qtype =='numerical' && $key == 0 ) { if ($this->questiondisplay->options->questions[$sub]->qtype == 'numerical' &&
$mform->addElement('static', 'sub_'.$sub."_".'tolerance['.$key.']', get_string('acceptederror', 'quiz')) ;//, $gradeoptions); $key == 0) {
$mform->addElement('static', 'sub_'.$sub."_".'tolerance['.$key.']',
get_string('acceptederror', 'quiz'));
} }
$mform->addElement('static', 'sub_'.$sub."_".'fraction['.$key.']', get_string('grade')) ;//, $gradeoptions); $mform->addElement('static', 'sub_'.$sub."_".'fraction['.$key.']',
get_string('grade'));
$mform->addElement('static', 'sub_'.$sub."_".'feedback['.$key.']', get_string('feedback', 'question')); $mform->addElement('static', 'sub_'.$sub."_".'feedback['.$key.']',
get_string('feedback', 'question'));
} }
} }
$mform->addElement('html', '</div>'); $mform->addElement('html', '</div>');
$this->negative_diff =$countsavedsubquestions - $countsubquestions ; $this->negative_diff = $countsavedsubquestions - $countsubquestions;
if ( ($this->negative_diff > 0 ) ||$this->qtype_change || ($this->used_in_quiz && $this->negative_diff != 0)){ if (($this->negative_diff > 0) ||$this->qtype_change ||
$mform->addElement('header', 'additemhdr', get_string('warningquestionmodified','qtype_multianswer')); ($this->used_in_quiz && $this->negative_diff != 0)) {
$mform->addElement('header', 'additemhdr',
get_string('warningquestionmodified', 'qtype_multianswer'));
} }
if($this->negative_diff > 0) { if ($this->negative_diff > 0) {
$mform->addElement('static', 'alert1', "<strong>".get_string('questiondeleted','qtype_multianswer')."</strong>",get_string('questionsless','qtype_multianswer',$this->negative_diff)); $mform->addElement('static', 'alert1', "<strong>".
get_string('questiondeleted', 'qtype_multianswer')."</strong>",
get_string('questionsless', 'qtype_multianswer', $this->negative_diff));
} }
if($this->qtype_change ) { if ($this->qtype_change) {
$mform->addElement('static', 'alert1', "<strong>".get_string('questiontypechanged','qtype_multianswer')."</strong>",get_string('questiontypechangedcomment','qtype_multianswer')); $mform->addElement('static', 'alert1', "<strong>".
get_string('questiontypechanged', 'qtype_multianswer')."</strong>",
get_string('questiontypechangedcomment', 'qtype_multianswer'));
} }
$mform->addElement('html', '</div>'); $mform->addElement('html', '</div>');
} }
if( $this->used_in_quiz){ if ($this->used_in_quiz) {
if($this->negative_diff < 0) { if ($this->negative_diff < 0) {
$diff = $countsubquestions - $countsavedsubquestions; $diff = $countsubquestions - $countsavedsubquestions;
$mform->addElement('static', 'alert1', "<strong>".get_string('questionsadded','qtype_multianswer')."</strong>","<strong>".get_string('questionsmore','qtype_multianswer',$diff)."</strong>"); $mform->addElement('static', 'alert1', "<strong>".
get_string('questionsadded', 'qtype_multianswer')."</strong>",
"<strong>".get_string('questionsmore', 'qtype_multianswer', $diff).
"</strong>");
} }
$a = new stdClass() ; $a = new stdClass();
$a->nb_of_quiz = $this->nb_of_quiz; $a->nb_of_quiz = $this->nb_of_quiz;
$a->nb_of_attempts = $this->nb_of_attempts; $a->nb_of_attempts = $this->nb_of_attempts;
$mform->addElement('header', 'additemhdr2', get_string('questionusedinquiz','qtype_multianswer',$a)); $mform->addElement('header', 'additemhdr2',
$mform->addElement('static', 'alertas', get_string('youshouldnot','qtype_multianswer')); get_string('questionusedinquiz', 'qtype_multianswer', $a));
$mform->addElement('static', 'alertas',
get_string('youshouldnot', 'qtype_multianswer'));
} }
if ( ($this->negative_diff > 0 || $this->used_in_quiz && ($this->negative_diff > 0 ||$this->negative_diff < 0 || $this->qtype_change ) ) && $this->reload ){ if (($this->negative_diff > 0 || $this->used_in_quiz &&
$mform->addElement('header', 'additemhdr', get_string('questionsaveasedited', 'qtype_multianswer')); ($this->negative_diff > 0 || $this->negative_diff < 0 || $this->qtype_change)) &&
$mform->addElement('checkbox', 'confirm','' ,get_string('confirmquestionsaveasedited', 'qtype_multianswer')); $this->reload) {
$mform->addElement('header', 'additemhdr',
get_string('questionsaveasedited', 'qtype_multianswer'));
$mform->addElement('checkbox', 'confirm', '',
get_string('confirmquestionsaveasedited', 'qtype_multianswer'));
$mform->setDefault('confirm', 0); $mform->setDefault('confirm', 0);
}else { } else {
$mform->addElement('hidden', 'confirm',0); $mform->addElement('hidden', 'confirm', 0);
} }
} }
function set_data($question) { public function set_data($question) {
global $DB; global $DB;
$default_values =array(); $default_values = array();
if (isset($question->id) and $question->id and $question->qtype and $question->questiontext) { if (isset($question->id) and $question->id and $question->qtype &&
$question->questiontext) {
foreach ($question->options->questions as $key => $wrapped) { foreach ($question->options->questions as $key => $wrapped) {
if(!empty($wrapped)){ if (!empty($wrapped)) {
// The old way of restoring the definitions is kept to gradually // The old way of restoring the definitions is kept to gradually
// update all multianswer questions // update all multianswer questions
if (empty($wrapped->questiontext)) { if (empty($wrapped->questiontext)) {
$parsableanswerdef = '{' . $wrapped->defaultgrade . ':'; $parsableanswerdef = '{' . $wrapped->defaultgrade . ':';
switch ($wrapped->qtype) { switch ($wrapped->qtype) {
case 'multichoice': case 'multichoice':
$parsableanswerdef .= 'MULTICHOICE:'; $parsableanswerdef .= 'MULTICHOICE:';
break; break;
case 'shortanswer': case 'shortanswer':
$parsableanswerdef .= 'SHORTANSWER:'; $parsableanswerdef .= 'SHORTANSWER:';
break; break;
case 'numerical': case 'numerical':
$parsableanswerdef .= 'NUMERICAL:'; $parsableanswerdef .= 'NUMERICAL:';
break; break;
default: default:
print_error('unknownquestiontype', 'question', '', $wrapped->qtype); print_error('unknownquestiontype', 'question', '',
$wrapped->qtype);
} }
$separator= ''; $separator = '';
foreach ($wrapped->options->answers as $subanswer) { foreach ($wrapped->options->answers as $subanswer) {
$parsableanswerdef .= $separator $parsableanswerdef .= $separator
. '%' . round(100*$subanswer->fraction) . '%'; . '%' . round(100*$subanswer->fraction) . '%';
@ -268,168 +304,192 @@ class question_edit_multianswer_form extends question_edit_form {
} }
$parsableanswerdef .= '}'; $parsableanswerdef .= '}';
// Fix the questiontext fields of old questions // Fix the questiontext fields of old questions
$DB->set_field('question', 'questiontext', $parsableanswerdef, array('id' => $wrapped->id)); $DB->set_field('question', 'questiontext', $parsableanswerdef,
array('id' => $wrapped->id));
} else { } else {
$parsableanswerdef = str_replace('&#', '&\#', $wrapped->questiontext); $parsableanswerdef = str_replace('&#', '&\#', $wrapped->questiontext);
} }
$question->questiontext = str_replace("{#$key}", $parsableanswerdef, $question->questiontext); $question->questiontext = str_replace("{#$key}", $parsableanswerdef,
$question->questiontext);
} }
} }
} }
// set default to $questiondisplay questions elements // set default to $questiondisplay questions elements
if ( $this->reload ){ if ($this->reload) {
if (isset($this->questiondisplay->options->questions)) { if (isset($this->questiondisplay->options->questions)) {
$subquestions = fullclone($this->questiondisplay->options->questions) ; $subquestions = fullclone($this->questiondisplay->options->questions);
if (count($subquestions)) { if (count($subquestions)) {
$sub =1; $sub = 1;
foreach ($subquestions as $subquestion) { foreach ($subquestions as $subquestion) {
$prefix = 'sub_'.$sub.'_' ; $prefix = 'sub_'.$sub.'_';
// validate parameters // validate parameters
$answercount = 0; $answercount = 0;
$maxgrade = false; $maxgrade = false;
$maxfraction = -1; $maxfraction = -1;
if ($subquestion->qtype =='shortanswer' ) { if ($subquestion->qtype == 'shortanswer') {
switch ($subquestion->usecase) { switch ($subquestion->usecase) {
case '1': case '1':
$default_values[$prefix.'usecase']= get_string('caseyes', 'qtype_shortanswer'); $default_values[$prefix.'usecase'] =
break; get_string('caseyes', 'qtype_shortanswer');
case '0': break;
default : case '0':
$default_values[$prefix.'usecase']= get_string('caseno', 'qtype_shortanswer'); default :
$default_values[$prefix.'usecase'] =
get_string('caseno', 'qtype_shortanswer');
} }
} }
if ($subquestion->qtype == 'multichoice' ) { if ($subquestion->qtype == 'multichoice') {
$default_values[$prefix.'layout'] = $subquestion->layout ; $default_values[$prefix.'layout'] = $subquestion->layout;
switch ($subquestion->layout) { switch ($subquestion->layout) {
case '0': case '0':
$default_values[$prefix.'layout']= get_string('layoutselectinline', 'qtype_multianswer'); $default_values[$prefix.'layout'] =
break; get_string('layoutselectinline', 'qtype_multianswer');
case '1': break;
$default_values[$prefix.'layout']= get_string('layoutvertical', 'qtype_multianswer'); case '1':
break; $default_values[$prefix.'layout'] =
case '2': get_string('layoutvertical', 'qtype_multianswer');
$default_values[$prefix.'layout']= get_string('layouthorizontal', 'qtype_multianswer'); break;
break; case '2':
default: $default_values[$prefix.'layout'] =
$default_values[$prefix.'layout']= get_string('layoutundefined', 'qtype_multianswer'); get_string('layouthorizontal', 'qtype_multianswer');
break;
default:
$default_values[$prefix.'layout'] =
get_string('layoutundefined', 'qtype_multianswer');
} }
} }
foreach ($subquestion->answer as $key=>$answer) { foreach ($subquestion->answer as $key => $answer) {
if ( $subquestion->qtype == 'numerical' && $key == 0 ) { if ($subquestion->qtype == 'numerical' && $key == 0) {
$default_values[$prefix.'tolerance['.$key.']'] = $subquestion->tolerance[0] ; $default_values[$prefix.'tolerance['.$key.']'] =
$subquestion->tolerance[0];
} }
$trimmedanswer = trim($answer); $trimmedanswer = trim($answer);
if ($trimmedanswer !== '') { if ($trimmedanswer !== '') {
$answercount++; $answercount++;
if ($subquestion->qtype == 'numerical' && !(is_numeric($trimmedanswer) || $trimmedanswer == '*')) { if ($subquestion->qtype == 'numerical' &&
$this->_form->setElementError($prefix.'answer['.$key.']' , get_string('answermustbenumberorstar', 'qtype_numerical')); !(is_numeric($trimmedanswer) || $trimmedanswer == '*')) {
$this->_form->setElementError($prefix.'answer['.$key.']',
get_string('answermustbenumberorstar',
'qtype_numerical'));
} }
if ($subquestion->fraction[$key] == 1) { if ($subquestion->fraction[$key] == 1) {
$maxgrade = true; $maxgrade = true;
} }
if ($subquestion->fraction[$key] > $maxfraction) { if ($subquestion->fraction[$key] > $maxfraction) {
$maxfraction = $subquestion->fraction[$key] ; $maxfraction = $subquestion->fraction[$key];
} }
} }
$default_values[$prefix.'answer['.$key.']'] = htmlspecialchars ($answer); $default_values[$prefix.'answer['.$key.']'] =
htmlspecialchars($answer);
} }
if ($answercount == 0) { if ($answercount == 0) {
if ($subquestion->qtype == 'multichoice' ) { if ($subquestion->qtype == 'multichoice') {
$this->_form->setElementError($prefix.'answer[0]' , get_string('notenoughanswers', 'qtype_multichoice', 2)); $this->_form->setElementError($prefix.'answer[0]',
get_string('notenoughanswers', 'qtype_multichoice', 2));
} else { } else {
$this->_form->setElementError($prefix.'answer[0]' , get_string('notenoughanswers', 'question', 1)); $this->_form->setElementError($prefix.'answer[0]',
get_string('notenoughanswers', 'question', 1));
} }
} }
if ($maxgrade == false) { if ($maxgrade == false) {
$this->_form->setElementError($prefix.'fraction[0]' ,get_string('fractionsnomax', 'question')); $this->_form->setElementError($prefix.'fraction[0]',
get_string('fractionsnomax', 'question'));
} }
foreach ($subquestion->feedback as $key=>$answer) { foreach ($subquestion->feedback as $key => $answer) {
$default_values[$prefix.'feedback['.$key.']'] = htmlspecialchars ($answer['text']); $default_values[$prefix.'feedback['.$key.']'] =
htmlspecialchars ($answer['text']);
} }
foreach ( $subquestion->fraction as $key=>$answer) { foreach ($subquestion->fraction as $key => $answer) {
$default_values[$prefix.'fraction['.$key.']'] = $answer; $default_values[$prefix.'fraction['.$key.']'] = $answer;
} }
$sub++; $sub++;
} }
} }
} }
} }
$default_values['alertas']= "<strong>".get_string('questioninquiz','qtype_multianswer')."</strong>"; $default_values['alertas']= "<strong>".get_string('questioninquiz', 'qtype_multianswer').
"</strong>";
if( $default_values != "") { if ($default_values != "") {
$question = (object)((array)$question + $default_values); $question = (object)((array)$question + $default_values);
} }
parent::set_data($question); parent::set_data($question);
} }
function validation($data, $files) { public function validation($data, $files) {
$errors = parent::validation($data, $files); $errors = parent::validation($data, $files);
$questiondisplay = qtype_multianswer_extract_question($data['questiontext']); $questiondisplay = qtype_multianswer_extract_question($data['questiontext']);
// echo "<p> questiondisplay ".$data['questiontext']['text']." <pre>";print_r($questiondisplay);echo "</pre></p>";
if (isset($questiondisplay->options->questions)) { if (isset($questiondisplay->options->questions)) {
$subquestions = fullclone($questiondisplay->options->questions) ; $subquestions = fullclone($questiondisplay->options->questions);
if (count($subquestions)) { if (count($subquestions)) {
$sub =1; $sub = 1;
foreach ($subquestions as $subquestion) { foreach ($subquestions as $subquestion) {
$prefix = 'sub_'.$sub.'_' ; $prefix = 'sub_'.$sub.'_';
$answercount = 0; $answercount = 0;
$maxgrade = false; $maxgrade = false;
$maxfraction = -1; $maxfraction = -1;
if(isset($this->savedquestiondisplay->options->questions[$sub]->qtype) && if (isset($this->savedquestiondisplay->options->questions[$sub]->qtype) &&
$this->savedquestiondisplay->options->questions[$sub]->qtype != $questiondisplay->options->questions[$sub]->qtype ){ $this->savedquestiondisplay->options->questions[$sub]->qtype !=
$storemess = " STORED QTYPE ".question_bank::get_qtype_name($this->savedquestiondisplay->options->questions[$sub]->qtype); $questiondisplay->options->questions[$sub]->qtype) {
} $storemess = " STORED QTYPE ".question_bank::get_qtype_name(
foreach ( $subquestion->answer as $key=>$answer) { $this->savedquestiondisplay->options->questions[$sub]->qtype);
}
foreach ($subquestion->answer as $key => $answer) {
$trimmedanswer = trim($answer); $trimmedanswer = trim($answer);
if ($trimmedanswer !== '') { if ($trimmedanswer !== '') {
$answercount++; $answercount++;
if ($subquestion->qtype =='numerical' && !(is_numeric($trimmedanswer) || $trimmedanswer == '*')) { if ($subquestion->qtype == 'numerical' &&
$errors[$prefix.'answer['.$key.']']= get_string('answermustbenumberorstar', 'qtype_numerical'); !(is_numeric($trimmedanswer) || $trimmedanswer == '*')) {
$errors[$prefix.'answer['.$key.']'] =
get_string('answermustbenumberorstar', 'qtype_numerical');
} }
if ($subquestion->fraction[$key] == 1) { if ($subquestion->fraction[$key] == 1) {
$maxgrade = true; $maxgrade = true;
} }
if ($subquestion->fraction[$key] > $maxfraction) { if ($subquestion->fraction[$key] > $maxfraction) {
$maxfraction = $subquestion->fraction[$key] ; $maxfraction = $subquestion->fraction[$key];
} }
} }
} }
if ($answercount==0) { if ($answercount == 0) {
if ( $subquestion->qtype =='multichoice' ) { if ($subquestion->qtype == 'multichoice') {
$errors[$prefix.'answer[0]']= get_string('notenoughanswers', 'qtype_multichoice', 2); $errors[$prefix.'answer[0]'] =
}else { get_string('notenoughanswers', 'qtype_multichoice', 2);
$errors[$prefix.'answer[0]'] = get_string('notenoughanswers', 'question', 1); } else {
$errors[$prefix.'answer[0]'] =
get_string('notenoughanswers', 'question', 1);
} }
} }
if ($maxgrade == false) { if ($maxgrade == false) {
$errors[$prefix.'fraction[0]']=get_string('fractionsnomax', 'question'); $errors[$prefix.'fraction[0]'] =
get_string('fractionsnomax', 'question');
} }
$sub++; $sub++;
} }
} else { } else {
$errors['questiontext']=get_string('questionsmissing', 'qtype_multianswer'); $errors['questiontext'] = get_string('questionsmissing', 'qtype_multianswer');
} }
} }
// $question = qtype_multianswer_extract_question($data['questiontext']);
// if (isset $question->options->questions
if (( $this->negative_diff > 0 || $this->used_in_quiz && ($this->negative_diff > 0 ||$this->negative_diff < 0 || $this->qtype_change ))&& $this->confirm == 0 ){
$errors['confirm']=get_string('confirmsave', 'qtype_multianswer',$this->negative_diff);
}
if (($this->negative_diff > 0 || $this->used_in_quiz &&
($this->negative_diff > 0 || $this->negative_diff < 0 ||
$this->qtype_change))&& $this->confirm == 0) {
$errors['confirm'] =
get_string('confirmsave', 'qtype_multianswer', $this->negative_diff);
}
return $errors; return $errors;
} }
function qtype() { public function qtype() {
return 'multianswer'; return 'multianswer';
} }
} }

View file

@ -44,7 +44,7 @@ $string['noquestions'] = 'The Cloze(multianswer) question "<strong>{$a}</strong>
$string['qtypenotrecognized'] = 'questiontype {$a} not recognized'; $string['qtypenotrecognized'] = 'questiontype {$a} not recognized';
$string['questionnadded'] = 'Question added'; $string['questionnadded'] = 'Question added';
$string['questiondefinition'] = 'Question definition'; $string['questiondefinition'] = 'Question definition';
$string['questiondeleted'] = 'Question deleted' ; $string['questiondeleted'] = 'Question deleted';
$string['questioninquiz'] = ' $string['questioninquiz'] = '
<ul> <ul>
@ -61,5 +61,5 @@ $string['questiontypechanged'] = 'Question type changed';
$string['questiontypechangedcomment'] = 'At least one question type has been changed.<br \>Did you add, delete or move a question?<br \>Look ahead.'; $string['questiontypechangedcomment'] = 'At least one question type has been changed.<br \>Did you add, delete or move a question?<br \>Look ahead.';
$string['questionusedinquiz'] = 'This question is used in {$a->nb_of_quiz} quiz(s), total attempt(s) : {$a->nb_of_attempts} '; $string['questionusedinquiz'] = 'This question is used in {$a->nb_of_quiz} quiz(s), total attempt(s) : {$a->nb_of_attempts} ';
$string['unknownquestiontypeofsubquestion'] = 'Unknown question type: {$a->type} of question part # {$a->sub}'; $string['unknownquestiontypeofsubquestion'] = 'Unknown question type: {$a->type} of question part # {$a->sub}';
$string['warningquestionmodified'] = '<b>WARNING</b>' ; $string['warningquestionmodified'] = '<b>WARNING</b>';
$string['youshouldnot'] = '<b>YOU SHOULD NOT</b>'; $string['youshouldnot'] = '<b>YOU SHOULD NOT</b>';

View file

@ -35,22 +35,22 @@ defined('MOODLE_INTERNAL') || die();
*/ */
class embedded_cloze_qtype extends question_type { class embedded_cloze_qtype extends question_type {
function name() { public function name() {
return 'multianswer'; return 'multianswer';
} }
function requires_qtypes() { public function requires_qtypes() {
return array('shortanswer', 'numerical', 'multichoice'); return array('shortanswer', 'numerical', 'multichoice');
} }
function get_question_options(&$question) { public function get_question_options($question) {
global $QTYPES, $DB, $OUTPUT; global $DB, $OUTPUT;
// Get relevant data indexed by positionkey from the multianswers table // Get relevant data indexed by positionkey from the multianswers table
if (!$sequence = $DB->get_field('question_multianswer', 'sequence', array('question' => $question->id))) { if (!$sequence = $DB->get_field('question_multianswer', 'sequence', array('question' => $question->id))) {
echo $OUTPUT->notification(get_string('noquestions','qtype_multianswer',$question->name)); echo $OUTPUT->notification(get_string('noquestions', 'qtype_multianswer', $question->name));
$question->options->questions['1']= ''; $question->options->questions['1'] = '';
return true ; return true;
} }
$wrappedquestions = $DB->get_records_list('question', 'id', explode(',', $sequence), 'id ASC'); $wrappedquestions = $DB->get_records_list('question', 'id', explode(',', $sequence), 'id ASC');
@ -63,32 +63,33 @@ class embedded_cloze_qtype extends question_type {
// before using the values. // before using the values.
// first all possible questions from sequence are nulled // first all possible questions from sequence are nulled
// then filled with the data if available in $wrappedquestions // then filled with the data if available in $wrappedquestions
$nbvaliquestion = 0 ; $nbvaliquestion = 0;
foreach($sequence as $seq){ foreach ($sequence as $seq) {
$question->options->questions[$seq]= ''; $question->options->questions[$seq] = '';
} }
if (isset($wrappedquestions) && is_array($wrappedquestions)){ if (isset($wrappedquestions) && is_array($wrappedquestions)) {
foreach ($wrappedquestions as $wrapped) { foreach ($wrappedquestions as $wrapped) {
if (!$QTYPES[$wrapped->qtype]->get_question_options($wrapped)) { if (!$QTYPES[$wrapped->qtype]->get_question_options($wrapped)) {
echo $OUTPUT->notification("Unable to get options for questiontype {$wrapped->qtype} (id={$wrapped->id})"); echo $OUTPUT->notification("Unable to get options for questiontype {$wrapped->qtype} (id={$wrapped->id})");
}else { } else {
// for wrapped questions the maxgrade is always equal to the defaultgrade, // for wrapped questions the maxgrade is always equal to the defaultgrade,
// there is no entry in the question_instances table for them // there is no entry in the question_instances table for them
$wrapped->maxgrade = $wrapped->defaultgrade; $wrapped->maxgrade = $wrapped->defaultgrade;
$nbvaliquestion++ ; $nbvaliquestion++;
$question->options->questions[$sequence[$wrapped->id]] = clone($wrapped); // ??? Why do we need a clone here? // ??? Why do we need a clone here?
$question->options->questions[$sequence[$wrapped->id]] = clone($wrapped);
}
} }
} }
} if ($nbvaliquestion == 0) {
if ($nbvaliquestion == 0 ) { echo $OUTPUT->notification(get_string('noquestions', 'qtype_multianswer', $question->name));
echo $OUTPUT->notification(get_string('noquestions','qtype_multianswer',$question->name));
} }
return true; return true;
} }
function save_question_options($question) { public function save_question_options($question) {
global $QTYPES, $DB; global $DB;
$result = new stdClass(); $result = new stdClass();
// This function needs to be able to handle the case where the existing set of wrapped // This function needs to be able to handle the case where the existing set of wrapped
@ -105,49 +106,49 @@ class embedded_cloze_qtype extends question_type {
$oldwrappedquestions = $DB->get_records_list('question', 'id', explode(',', $oldwrappedids), 'id ASC'); $oldwrappedquestions = $DB->get_records_list('question', 'id', explode(',', $oldwrappedids), 'id ASC');
} }
$sequence = array(); $sequence = array();
foreach($question->options->questions as $wrapped) { foreach ($question->options->questions as $wrapped) {
if (!empty($wrapped)){ if (!empty($wrapped)) {
// if we still have some old wrapped question ids, reuse the next of them // if we still have some old wrapped question ids, reuse the next of them
if (is_array($oldwrappedquestions) && $oldwrappedquestion = array_shift($oldwrappedquestions)) { if (is_array($oldwrappedquestions) && $oldwrappedquestion = array_shift($oldwrappedquestions)) {
$wrapped->id = $oldwrappedquestion->id; $wrapped->id = $oldwrappedquestion->id;
if($oldwrappedquestion->qtype != $wrapped->qtype ) { if ($oldwrappedquestion->qtype != $wrapped->qtype) {
switch ($oldwrappedquestion->qtype) { switch ($oldwrappedquestion->qtype) {
case 'multichoice': case 'multichoice':
$DB->delete_records('question_multichoice', array('question' => $oldwrappedquestion->id)); $DB->delete_records('question_multichoice', array('question' => $oldwrappedquestion->id));
break; break;
case 'shortanswer': case 'shortanswer':
$DB->delete_records('question_shortanswer', array('question' => $oldwrappedquestion->id)); $DB->delete_records('question_shortanswer', array('question' => $oldwrappedquestion->id));
break; break;
case 'numerical': case 'numerical':
$DB->delete_records('question_numerical', array('question' => $oldwrappedquestion->id)); $DB->delete_records('question_numerical', array('question' => $oldwrappedquestion->id));
break; break;
default: default:
print_error('qtypenotrecognized', 'qtype_multianswer','',$oldwrappedquestion->qtype); print_error('qtypenotrecognized', 'qtype_multianswer', '', $oldwrappedquestion->qtype);
$wrapped->id = 0 ; $wrapped->id = 0;
} }
} }
}else { } else {
$wrapped->id = 0 ; $wrapped->id = 0;
} }
} }
$wrapped->name = $question->name; $wrapped->name = $question->name;
$wrapped->parent = $question->id; $wrapped->parent = $question->id;
$previousid = $wrapped->id ; $previousid = $wrapped->id;
$wrapped->category = $question->category . ',1'; // save_question strips this extra bit off again. $wrapped->category = $question->category . ',1'; // save_question strips this extra bit off again.
$wrapped = $QTYPES[$wrapped->qtype]->save_question($wrapped, clone($wrapped)); $wrapped = $QTYPES[$wrapped->qtype]->save_question($wrapped, clone($wrapped));
$sequence[] = $wrapped->id; $sequence[] = $wrapped->id;
if ($previousid != 0 && $previousid != $wrapped->id ) { if ($previousid != 0 && $previousid != $wrapped->id) {
// for some reasons a new question has been created // for some reasons a new question has been created
// so delete the old one // so delete the old one
delete_question($previousid) ; delete_question($previousid);
} }
} }
// Delete redundant wrapped questions // Delete redundant wrapped questions
if(is_array($oldwrappedquestions) && count($oldwrappedquestions)){ if (is_array($oldwrappedquestions) && count($oldwrappedquestions)) {
foreach ($oldwrappedquestions as $oldwrappedquestion) { foreach ($oldwrappedquestions as $oldwrappedquestion) {
delete_question($oldwrappedquestion->id) ; delete_question($oldwrappedquestion->id);
} }
} }
@ -164,7 +165,7 @@ class embedded_cloze_qtype extends question_type {
} }
} }
function save_question($authorizedquestion, $form) { public function save_question($authorizedquestion, $form) {
$question = qtype_multianswer_extract_question($form->questiontext); $question = qtype_multianswer_extract_question($form->questiontext);
if (isset($authorizedquestion->id)) { if (isset($authorizedquestion->id)) {
$question->id = $authorizedquestion->id; $question->id = $authorizedquestion->id;
@ -179,7 +180,7 @@ class embedded_cloze_qtype extends question_type {
return parent::save_question($question, $form); return parent::save_question($question, $form);
} }
function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) { public function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
$state->responses = array(); $state->responses = array();
foreach ($question->options->questions as $key => $wrapped) { foreach ($question->options->questions as $key => $wrapped) {
$state->responses[$key] = ''; $state->responses[$key] = '';
@ -187,7 +188,7 @@ class embedded_cloze_qtype extends question_type {
return true; return true;
} }
function restore_session_and_responses(&$question, &$state) { public function restore_session_and_responses(&$question, &$state) {
$responses = explode(',', $state->responses['']); $responses = explode(',', $state->responses['']);
$state->responses = array(); $state->responses = array();
foreach ($responses as $response) { foreach ($responses as $response) {
@ -199,7 +200,7 @@ class embedded_cloze_qtype extends question_type {
return true; return true;
} }
function save_session_and_responses(&$question, &$state) { public function save_session_and_responses(&$question, &$state) {
global $DB; global $DB;
$responses = $state->responses; $responses = $state->responses;
// encode - (hyphen) and , (comma) to &#0045; because they are used as // encode - (hyphen) and , (comma) to &#0045; because they are used as
@ -214,36 +215,17 @@ class embedded_cloze_qtype extends question_type {
return true; return true;
} }
function delete_question($questionid, $contextid) { public function delete_question($questionid, $contextid) {
global $DB; global $DB;
$DB->delete_records("question_multianswer", array("question" => $questionid)); $DB->delete_records("question_multianswer", array("question" => $questionid));
parent::delete_question($questionid, $contextid); parent::delete_question($questionid, $contextid);
} }
function get_correct_responses(&$question, &$state) { public function get_possible_responses(&$question) {
global $QTYPES;
$responses = array(); $responses = array();
foreach($question->options->questions as $key => $wrapped) { foreach ($question->options->questions as $key => $wrapped) {
if (!empty($wrapped)){ if (!empty($wrapped)) {
if ($correct = $QTYPES[$wrapped->qtype]->get_correct_responses($wrapped, $state)) {
$responses[$key] = $correct[''];
} else {
// if there is no correct answer to this subquestion then there
// can not be a correct answer to the whole question either, so
// we have to return null.
return null;
}
}
}
return $responses;
}
function get_possible_responses(&$question) {
global $QTYPES;
$responses = array();
foreach($question->options->questions as $key => $wrapped) {
if (!empty($wrapped)){
if ($correct = $QTYPES[$wrapped->qtype]->get_possible_responses($wrapped)) { if ($correct = $QTYPES[$wrapped->qtype]->get_possible_responses($wrapped)) {
$responses += $correct; $responses += $correct;
} else { } else {
@ -256,28 +238,16 @@ class embedded_cloze_qtype extends question_type {
} }
return $responses; return $responses;
} }
function get_actual_response_details($question, $state){
global $QTYPES;
$details = array();
foreach($question->options->questions as $key => $wrapped) {
if (!empty($wrapped)){
$stateforquestion = clone($state);
$stateforquestion->responses[''] = $state->responses[$key];
$details = array_merge($details, $QTYPES[$wrapped->qtype]->get_actual_response_details($wrapped, $stateforquestion));
}
}
return $details;
}
function get_html_head_contributions(&$question, &$state) { public function get_html_head_contributions(&$question, &$state) {
global $PAGE; global $PAGE;
parent::get_html_head_contributions($question, $state); parent::get_html_head_contributions($question, $state);
$PAGE->requires->js('/lib/overlib/overlib.js', true); $PAGE->requires->js('/lib/overlib/overlib.js', true);
$PAGE->requires->js('/lib/overlib/overlib_cssstyle.js', true); $PAGE->requires->js('/lib/overlib/overlib_cssstyle.js', true);
} }
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) { public function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $QTYPES, $CFG, $USER, $OUTPUT, $PAGE; global $CFG, $USER, $OUTPUT, $PAGE;
$readonly = empty($options->readonly) ? '' : 'readonly="readonly"'; $readonly = empty($options->readonly) ? '' : 'readonly="readonly"';
$disabled = empty($options->readonly) ? '' : 'disabled="disabled"'; $disabled = empty($options->readonly) ? '' : 'disabled="disabled"';
@ -290,7 +260,7 @@ class embedded_cloze_qtype extends question_type {
// MDL-7497 // MDL-7497
if (!empty($USER->screenreader)) { if (!empty($USER->screenreader)) {
echo "<img src=\"".$OUTPUT->pix_url('icon', 'qtype_'.$question->qtype)."\" ". echo "<img src=\"".$OUTPUT->pix_url('icon', 'qtype_'.$question->qtype)."\" ".
"class=\"icon\" alt=\"".get_string('clozeaid','qtype_multichoice')."\" /> "; "class=\"icon\" alt=\"".get_string('clozeaid', 'qtype_multichoice')."\" /> ";
} }
echo '<div class="ablock clearfix">'; echo '<div class="ablock clearfix">';
@ -309,290 +279,266 @@ class embedded_cloze_qtype extends question_type {
$qtextremaining = $qtextsplits[1]; $qtextremaining = $qtextsplits[1];
$positionkey = $regs[1]; $positionkey = $regs[1];
if (isset($question->options->questions[$positionkey]) && $question->options->questions[$positionkey] != ''){ if (isset($question->options->questions[$positionkey]) && $question->options->questions[$positionkey] != '') {
$wrapped = &$question->options->questions[$positionkey]; $wrapped = &$question->options->questions[$positionkey];
$answers = &$wrapped->options->answers; $answers = &$wrapped->options->answers;
// $correctanswers = $QTYPES[$wrapped->qtype]->get_correct_responses($wrapped, $state);
$inputname = $nameprefix.$positionkey; $inputname = $nameprefix.$positionkey;
if (isset($state->responses[$positionkey])) { if (isset($state->responses[$positionkey])) {
$response = $state->responses[$positionkey]; $response = $state->responses[$positionkey];
} else { } else {
$response = null; $response = null;
} }
// echo "<p> multianswer positionkey $positionkey response $response state <pre>";print_r($state);echo "</pre></p>";
// Determine feedback popup if any // Determine feedback popup if any
$popup = ''; $popup = '';
$style = ''; $style = '';
$feedbackimg = ''; $feedbackimg = '';
$feedback = '' ; $feedback = '';
$correctanswer = ''; $correctanswer = '';
$strfeedbackwrapped = $strfeedback; $strfeedbackwrapped = $strfeedback;
$testedstate = clone($state); $testedstate = clone($state);
if ($correctanswers = $QTYPES[$wrapped->qtype]->get_correct_responses($wrapped, $state)) { if ($correctanswers = $QTYPES[$wrapped->qtype]->get_correct_responses($wrapped, $state)) {
if ($options->readonly && $options->correct_responses) { if ($options->readonly && $options->correct_responses) {
$delimiter = ''; $delimiter = '';
if ($correctanswers) { if ($correctanswers) {
foreach ($correctanswers as $ca) { foreach ($correctanswers as $ca) {
switch($wrapped->qtype){ switch($wrapped->qtype) {
case 'numerical': case 'numerical':
case 'shortanswer': case 'shortanswer':
$correctanswer .= $delimiter.$ca; $correctanswer .= $delimiter.$ca;
break ; break;
case 'multichoice': case 'multichoice':
if (isset($answers[$ca])){ if (isset($answers[$ca])) {
$correctanswer .= $delimiter.$answers[$ca]->answer; $correctanswer .= $delimiter.$answers[$ca]->answer;
} }
break ; break;
} }
$delimiter = ', '; $delimiter = ', ';
} }
} }
} }
if ($correctanswer != '' ) { if ($correctanswer != '') {
$feedback = '<div class="correctness">'; $feedback = '<div class="correctness">';
$feedback .= get_string('correctansweris', 'question', s($correctanswer)); $feedback .= get_string('correctansweris', 'question', s($correctanswer));
$feedback .= '</div>'; $feedback .= '</div>';
} }
} }
if ($options->feedback) { if ($options->feedback) {
$chosenanswer = null; $chosenanswer = null;
switch ($wrapped->qtype) {
case 'numerical':
case 'shortanswer':
$testedstate = clone($state);
$testedstate->responses[''] = $response;
foreach ($answers as $answer) {
if ($QTYPES[$wrapped->qtype]->test_response($wrapped, $testedstate, $answer)) {
$chosenanswer = clone($answer);
break;
}
}
break;
case 'multichoice':
if (isset($answers[$response])) {
$chosenanswer = clone($answers[$response]);
}
break;
default:
break;
}
// Set up a default chosenanswer so that all non-empty wrong
// answers are highlighted red
if (empty($chosenanswer) && $response != '') {
$chosenanswer = new stdClass();
$chosenanswer->fraction = 0.0;
}
if (!empty($chosenanswer->feedback)) {
$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', 'question');
}
$popup = " onmouseover=\"return overlib('$feedback', STICKY, MOUSEOFF, CAPTION, '$strfeedbackwrapped', FGCOLOR, '#FFFFFF');\" ".
" onmouseout=\"return nd();\" ";
}
/// Determine style
if ($options->feedback && $response != '') {
$style = 'class = "'.question_get_feedback_class($chosenanswer->fraction).'"';
$feedbackimg = question_get_feedback_image($chosenanswer->fraction);
} else {
$style = '';
$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) { switch ($wrapped->qtype) {
case 'numerical':
case 'shortanswer': case 'shortanswer':
$testedstate = clone($state); case 'numerical':
$testedstate->responses[''] = $response; $size = 1;
foreach ($answers as $answer) { foreach ($answers as $answer) {
if($QTYPES[$wrapped->qtype] if (strlen(trim($answer->answer)) > $size) {
->test_response($wrapped, $testedstate, $answer)) { $size = strlen(trim($answer->answer));
$chosenanswer = clone($answer);
break;
} }
} }
break; if (strlen(trim($response))> $size) {
case 'multichoice': $size = strlen(trim($response))+1;
if (isset($answers[$response])) {
$chosenanswer = clone($answers[$response]);
} }
break; $size = $size + rand(0, $size*0.15);
default: $size > 60 ? $size = 60 : $size = $size;
break; $styleinfo = "size=\"$size\"";
}
// Set up a default chosenanswer so that all non-empty wrong echo "<input $style $readonly $popup name=\"$inputname\"";
// answers are highlighted red echo " type=\"text\" value=\"".s($response)."\" ".$styleinfo." /> ";
if (empty($chosenanswer) && $response != '') {
$chosenanswer = new stdClass();
$chosenanswer->fraction = 0.0;
}
if (!empty($chosenanswer->feedback)) {
$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', 'question');
}
$popup = " onmouseover=\"return overlib('$feedback', STICKY, MOUSEOFF, CAPTION, '$strfeedbackwrapped', FGCOLOR, '#FFFFFF');\" ".
" onmouseout=\"return nd();\" ";
}
/// Determine style
if ($options->feedback && $response != '') {
$style = 'class = "'.question_get_feedback_class($chosenanswer->fraction).'"';
$feedbackimg = question_get_feedback_image($chosenanswer->fraction);
} else {
$style = '';
$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) {
case 'shortanswer':
case 'numerical':
$size = 1 ;
foreach ($answers as $answer) {
if (strlen(trim($answer->answer)) > $size ){
$size = strlen(trim($answer->answer));
}
}
if (strlen(trim($response))> $size ){
$size = strlen(trim($response))+1;
}
$size = $size + rand(0,$size*0.15);
$size > 60 ? $size = 60 : $size = $size;
$styleinfo = "size=\"$size\"";
/**
* Uncomment the following lines if you want to limit for small sizes.
* Results may vary with browsers see MDL-3274
*/
/*
if ($size < 2) {
$styleinfo = 'style="width: 1.1em;"';
}
if ($size == 2) {
$styleinfo = 'style="width: 1.9em;"';
}
if ($size == 3) {
$styleinfo = 'style="width: 2.3em;"';
}
if ($size == 4) {
$styleinfo = 'style="width: 2.8em;"';
}
*/
echo "<input $style $readonly $popup name=\"$inputname\"";
echo " type=\"text\" value=\"".s($response)."\" ".$styleinfo." /> ";
if (!empty($feedback) && !empty($USER->screenreader)) {
echo "<img src=\"" . $OUTPUT->pix_url('i/feedback') . "\" alt=\"$feedback\" />";
}
echo $feedbackimg;
break;
case 'multichoice':
if ($wrapped->options->layout == 0 ){
$outputoptions = '<option></option>'; // Default empty option
foreach ($answers as $mcanswer) {
$selected = '';
if ($response == $mcanswer->id) {
$selected = ' selected="selected"';
}
$outputoptions .= "<option value=\"$mcanswer->id\"$selected>" .
s($mcanswer->answer) . '</option>';
}
// In the next line, $readonly is invalid HTML, but it works in
// all browsers. $disabled would be valid, but then the JS for
// displaying the feedback does not work. Of course, we should
// not be relying on JS (for accessibility reasons), but that is
// a bigger problem.
//
// The span is used for safari, which does not allow styling of
// selects.
echo "<span $style><select $popup $readonly $style name=\"$inputname\">";
echo $outputoptions;
echo '</select></span>';
if (!empty($feedback) && !empty($USER->screenreader)) { if (!empty($feedback) && !empty($USER->screenreader)) {
echo "<img src=\"" . $OUTPUT->pix_url('i/feedback') . "\" alt=\"$feedback\" />"; echo "<img src=\"" . $OUTPUT->pix_url('i/feedback') . "\" alt=\"$feedback\" />";
} }
echo $feedbackimg; echo $feedbackimg;
}else if ($wrapped->options->layout == 1 || $wrapped->options->layout == 2){ break;
$ordernumber=0; case 'multichoice':
$anss = Array(); if ($wrapped->options->layout == 0) {
foreach ($answers as $mcanswer) { $outputoptions = '<option></option>'; // Default empty option
$ordernumber++; foreach ($answers as $mcanswer) {
$checked = ''; $selected = '';
$chosen = false; if ($response == $mcanswer->id) {
$type = 'type="radio"'; $selected = ' selected="selected"';
$name = "name=\"{$inputname}\""; }
if ($response == $mcanswer->id) { $outputoptions .= "<option value=\"$mcanswer->id\"$selected>" .
$checked = 'checked="checked"'; s($mcanswer->answer) . '</option>';
$chosen = true;
} }
$a = new stdClass(); // In the next line, $readonly is invalid HTML, but it works in
$a->id = $question->name_prefix . $mcanswer->id; // all browsers. $disabled would be valid, but then the JS for
$a->class = ''; // displaying the feedback does not work. Of course, we should
$a->feedbackimg = ''; // not be relying on JS (for accessibility reasons), but that is
// a bigger problem.
//
// The span is used for safari, which does not allow styling of
// selects.
echo "<span $style><select $popup $readonly $style name=\"$inputname\">";
echo $outputoptions;
echo '</select></span>';
if (!empty($feedback) && !empty($USER->screenreader)) {
echo "<img src=\"" . $OUTPUT->pix_url('i/feedback') . "\" alt=\"$feedback\" />";
}
echo $feedbackimg;
} else if ($wrapped->options->layout == 1 || $wrapped->options->layout == 2) {
$ordernumber = 0;
$anss = Array();
foreach ($answers as $mcanswer) {
$ordernumber++;
$checked = '';
$chosen = false;
$type = 'type="radio"';
$name = "name=\"{$inputname}\"";
if ($response == $mcanswer->id) {
$checked = 'checked="checked"';
$chosen = true;
}
$a = new stdClass();
$a->id = $question->name_prefix . $mcanswer->id;
$a->class = '';
$a->feedbackimg = '';
// Print the control // Print the control
$a->control = "<input $readonly id=\"$a->id\" $name $checked $type value=\"$mcanswer->id\" />"; $a->control = "<input $readonly id=\"$a->id\" $name $checked $type value=\"$mcanswer->id\" />";
if ($options->correct_responses && $mcanswer->fraction > 0) { if ($options->correct_responses && $mcanswer->fraction > 0) {
$a->class = question_get_feedback_class(1); $a->class = question_get_feedback_class(1);
}
if (($options->feedback && $chosen) || $options->correct_responses) {
if ($type == ' type="checkbox" ') {
$a->feedbackimg = question_get_feedback_image($mcanswer->fraction > 0 ? 1 : 0, $chosen && $options->feedback);
} else {
$a->feedbackimg = question_get_feedback_image($mcanswer->fraction, $chosen && $options->feedback);
}
}
// Print the answer text: no automatic numbering
$a->text = format_text($mcanswer->answer, $mcanswer->answerformat, $formatoptions, $cmoptions->course);
// Print feedback if feedback is on
if (($options->feedback || $options->correct_responses) && ($checked)) { //|| $options->readonly
$a->feedback = format_text($mcanswer->feedback, $mcanswer->feedbackformat, $formatoptions, $cmoptions->course);
} else {
$a->feedback = '';
}
$anss[] = clone($a);
}
if ($wrapped->options->layout == 1) {
?><table class="answer"><?php
$row = 1;
foreach ($anss as $answer) {
?><tr class="<?php echo 'r'.$row = $row ? 0 : 1; ?>">
<td class="c0 control">
<?php echo $answer->control; ?>
</td>
<td class="c1 text <?php echo $answer->class ?>">
<label for="<?php echo $answer->id ?>">
<?php echo $answer->text; ?>
<?php echo $answer->feedbackimg; ?>
</label>
</td>
<td class="c0 feedback">
<?php echo $answer->feedback; ?>
</td>
</tr><?php
}
?></table><?php
} else if ($wrapped->options->layout == 2) {
?><table class="answer">
<tr class="<?php echo 'r'.$row = $row ? 0 : 1; ?>">
<?php $row = 1;
foreach ($anss as $answer) { ?>
<td class="c0 control">
<?php echo $answer->control; ?>
</td>
<td class="c1 text <?php echo $answer->class ?>">
<label for="<?php echo $answer->id ?>">
<?php echo $answer->text; ?>
<?php echo $answer->feedbackimg; ?>
</label>
</td>
<td class="c0 feedback">
<?php echo $answer->feedback; ?>
</td><?php
}
?>
</tr>
</table><?php
}
} else {
echo "no valid layout";
}
break;
default:
$a = new stdClass();
$a->type = $wrapped->qtype;
$a->sub = $positionkey;
print_error('unknownquestiontypeofsubquestion', 'qtype_multianswer', '', $a);
break;
} }
if (($options->feedback && $chosen) || $options->correct_responses) { echo "</label>"; // MDL-7497
if ($type == ' type="checkbox" ') { } else {
$a->feedbackimg = question_get_feedback_image($mcanswer->fraction > 0 ? 1 : 0, $chosen && $options->feedback); if (! isset($question->options->questions[$positionkey])) {
} else { echo $regs[0]."</label>";
$a->feedbackimg = question_get_feedback_image($mcanswer->fraction, $chosen && $options->feedback);
}
}
// Print the answer text: no automatic numbering
$a->text = format_text($mcanswer->answer, $mcanswer->answerformat, $formatoptions, $cmoptions->course);
// Print feedback if feedback is on
if (($options->feedback || $options->correct_responses) && ($checked )) { //|| $options->readonly
$a->feedback = format_text($mcanswer->feedback, $mcanswer->feedbackformat, $formatoptions, $cmoptions->course);
} else { } else {
$a->feedback = ''; echo '</label><div class="error" >'.get_string('questionnotfound', 'qtype_multianswer', $positionkey).'</div>';
} }
$anss[] = clone($a);
}
?>
<?php if ($wrapped->options->layout == 1 ){
?>
<table class="answer">
<?php $row = 1; foreach ($anss as $answer) { ?>
<tr class="<?php echo 'r'.$row = $row ? 0 : 1; ?>">
<td class="c0 control">
<?php echo $answer->control; ?>
</td>
<td class="c1 text <?php echo $answer->class ?>">
<label for="<?php echo $answer->id ?>">
<?php echo $answer->text; ?>
<?php echo $answer->feedbackimg; ?>
</label>
</td>
<td class="c0 feedback">
<?php echo $answer->feedback; ?>
</td>
</tr>
<?php } ?>
</table>
<?php }else if ($wrapped->options->layout == 2 ){
?>
<table class="answer">
<tr class="<?php echo 'r'.$row = $row ? 0 : 1; ?>">
<?php $row = 1; foreach ($anss as $answer) { ?>
<td class="c0 control">
<?php echo $answer->control; ?>
</td>
<td class="c1 text <?php echo $answer->class ?>">
<label for="<?php echo $answer->id ?>">
<?php echo $answer->text; ?>
<?php echo $answer->feedbackimg; ?>
</label>
</td>
<td class="c0 feedback">
<?php echo $answer->feedback; ?>
</td>
<?php } ?>
</tr>
</table>
<?php }
}else {
echo "no valid layout";
}
break;
default:
$a = new stdClass();
$a->type = $wrapped->qtype ;
$a->sub = $positionkey;
print_error('unknownquestiontypeofsubquestion', 'qtype_multianswer','',$a);
break;
}
echo "</label>"; // MDL-7497
}
else {
if(! isset($question->options->questions[$positionkey])){
echo $regs[0]."</label>";
}else {
echo '</label><div class="error" >'.get_string('questionnotfound','qtype_multianswer',$positionkey).'</div>';
} }
} }
}
// Print the final piece of question text: // Print the final piece of question text:
echo $qtextremaining; echo $qtextremaining;
@ -601,8 +547,6 @@ class embedded_cloze_qtype extends question_type {
} }
public function compare_responses($question, $state, $teststate) { public function compare_responses($question, $state, $teststate) {
global $QTYPES;
foreach ($question->options->questions as $key => $wrapped) { foreach ($question->options->questions as $key => $wrapped) {
if (empty($wrapped)) { if (empty($wrapped)) {
continue; continue;
@ -639,21 +583,19 @@ class embedded_cloze_qtype extends question_type {
return true; return true;
} }
function grade_responses(&$question, &$state, $cmoptions) { public function grade_responses(&$question, &$state, $cmoptions) {
global $QTYPES;
$teststate = clone($state); $teststate = clone($state);
$state->raw_grade = 0; $state->raw_grade = 0;
foreach($question->options->questions as $key => $wrapped) { foreach ($question->options->questions as $key => $wrapped) {
if (!empty($wrapped)){ if (!empty($wrapped)) {
if(isset($state->responses[$key])){ if (isset($state->responses[$key])) {
$state->responses[$key] = $state->responses[$key]; $state->responses[$key] = $state->responses[$key];
}else { } else {
$state->responses[$key] = '' ; $state->responses[$key] = '';
} }
$teststate->responses = array('' => $state->responses[$key]); $teststate->responses = array('' => $state->responses[$key]);
$teststate->raw_grade = 0; $teststate->raw_grade = 0;
if (false === $QTYPES[$wrapped->qtype] if (false === $QTYPES[$wrapped->qtype]->grade_responses($wrapped, $teststate, $cmoptions)) {
->grade_responses($wrapped, $teststate, $cmoptions)) {
return false; return false;
} }
$state->raw_grade += $teststate->raw_grade; $state->raw_grade += $teststate->raw_grade;
@ -674,73 +616,21 @@ class embedded_cloze_qtype extends question_type {
return true; return true;
} }
function get_actual_response($question, $state) {
global $QTYPES;
$teststate = clone($state);
foreach($question->options->questions as $key => $wrapped) {
$state->responses[$key] = html_entity_decode($state->responses[$key]);
$teststate->responses = array('' => $state->responses[$key]);
$correct = $QTYPES[$wrapped->qtype]
->get_actual_response($wrapped, $teststate);
$responses[$key] = implode(';', $correct);
}
return $responses;
}
/** /**
* @param object $question * @param object $question
* @return mixed either a integer score out of 1 that the average random * @return mixed either a integer score out of 1 that the average random
* guess by a student might give or an empty string which means will not * guess by a student might give or an empty string which means will not
* calculate. * calculate.
*/ */
function get_random_guess_score($question) { public function get_random_guess_score($question) {
$totalfraction = 0; $totalfraction = 0;
foreach (array_keys($question->options->questions) as $key){ foreach (array_keys($question->options->questions) as $key) {
$totalfraction += question_get_random_guess_score($question->options->questions[$key]); $totalfraction += question_get_random_guess_score($question->options->questions[$key]);
} }
return $totalfraction / count($question->options->questions); return $totalfraction / count($question->options->questions);
} }
/**
* Runs all the code required to set up and save an essay question for testing purposes.
* Alternate DB table prefix may be used to facilitate data deletion.
*/
function generate_test($name, $courseid = null) {
global $DB;
list($form, $question) = parent::generate_test($name, $courseid);
$question->category = $form->category;
$form->questiontext = "This question consists of some text with an answer embedded right here {1:MULTICHOICE:Wrong answer#Feedback for this wrong answer~Another wrong answer#Feedback for the other wrong answer~=Correct answer#Feedback for correct answer~%50%Answer that gives half the credit#Feedback for half credit answer} and right after that you will have to deal with this short answer {1:SHORTANSWER:Wrong answer#Feedback for this wrong answer~=Correct answer#Feedback for correct answer~%50%Answer that gives half the credit#Feedback for half credit answer} and finally we have a floating point number {2:NUMERICAL:=23.8:0.1#Feedback for correct answer 23.8~%50%23.8:2#Feedback for half credit answer in the nearby region of the correct answer}.
Note that addresses like www.moodle.org and smileys :-) all work as normal:
a) How good is this? {:MULTICHOICE:=Yes#Correct~No#We have a different opinion}
b) What grade would you give it? {3:NUMERICAL:=3:2}
Good luck!
";
$form->feedback = "feedback";
$form->generalfeedback = "General feedback";
$form->fraction = 0;
$form->penalty = 0.1;
$form->versioning = 0;
if ($courseid) {
$course = $DB->get_record('course', array('id' => $courseid));
}
return $this->save_question($question, $form);
}
} }
//// END OF CLASS ////
/////////////////////////////////////////////////////////////
//// ADDITIONAL FUNCTIONS
//// The functions below deal exclusivly with editing
//// of questions with question type 'multianswer'.
//// Therefore they are kept in this file.
//// They are not in the class as they are not
//// likely to be subject for overriding.
/////////////////////////////////////////////////////////////
// ANSWER_ALTERNATIVE regexes // ANSWER_ALTERNATIVE regexes
define("ANSWER_ALTERNATIVE_FRACTION_REGEX", define("ANSWER_ALTERNATIVE_FRACTION_REGEX",
@ -783,7 +673,7 @@ define("ANSWER_REGEX",
. '(' . ANSWER_ALTERNATIVE_REGEX . '(' . ANSWER_ALTERNATIVE_REGEX
. '(~' . '(~'
. ANSWER_ALTERNATIVE_REGEX . ANSWER_ALTERNATIVE_REGEX
. ')*)\}' ); . ')*)\}');
// Parenthesis positions for singulars in ANSWER_REGEX // Parenthesis positions for singulars in ANSWER_REGEX
define("ANSWER_REGEX_NORM", 1); define("ANSWER_REGEX_NORM", 1);
@ -803,16 +693,16 @@ function qtype_multianswer_extract_question($text) {
$question->generalfeedback['text'] = ''; $question->generalfeedback['text'] = '';
$question->generalfeedback['format'] = '1'; $question->generalfeedback['format'] = '1';
$question->generalfeedback['itemid'] = ''; $question->generalfeedback['itemid'] = '';
$question->options->questions = array(); $question->options->questions = array();
$question->defaultgrade = 0; // Will be increased for each answer norm $question->defaultgrade = 0; // Will be increased for each answer norm
for ($positionkey=1; preg_match('/'.ANSWER_REGEX.'/', $question->questiontext['text'], $answerregs); ++$positionkey ) { for ($positionkey = 1; preg_match('/'.ANSWER_REGEX.'/', $question->questiontext['text'], $answerregs); ++$positionkey) {
$wrapped = new stdClass(); $wrapped = new stdClass();
$wrapped->generalfeedback['text'] = ''; $wrapped->generalfeedback['text'] = '';
$wrapped->generalfeedback['format'] = '1'; $wrapped->generalfeedback['format'] = '1';
$wrapped->generalfeedback['itemid'] = ''; $wrapped->generalfeedback['itemid'] = '';
if (isset($answerregs[ANSWER_REGEX_NORM])&& $answerregs[ANSWER_REGEX_NORM]!== ''){ if (isset($answerregs[ANSWER_REGEX_NORM])&& $answerregs[ANSWER_REGEX_NORM]!== '') {
$wrapped->defaultgrade = $answerregs[ANSWER_REGEX_NORM]; $wrapped->defaultgrade = $answerregs[ANSWER_REGEX_NORM];
} else { } else {
$wrapped->defaultgrade = '1'; $wrapped->defaultgrade = '1';
@ -824,13 +714,13 @@ function qtype_multianswer_extract_question($text) {
$wrapped->instructions['text'] = ''; $wrapped->instructions['text'] = '';
$wrapped->instructions['format'] = '1'; $wrapped->instructions['format'] = '1';
$wrapped->instructions['itemid'] = ''; $wrapped->instructions['itemid'] = '';
} else if(!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_SHORTANSWER])) { } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_SHORTANSWER])) {
$wrapped->qtype = 'shortanswer'; $wrapped->qtype = 'shortanswer';
$wrapped->usecase = 0; $wrapped->usecase = 0;
} else if(!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_SHORTANSWER_C])) { } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_SHORTANSWER_C])) {
$wrapped->qtype = 'shortanswer'; $wrapped->qtype = 'shortanswer';
$wrapped->usecase = 1; $wrapped->usecase = 1;
} else if(!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE])) { } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE])) {
$wrapped->qtype = 'multichoice'; $wrapped->qtype = 'multichoice';
$wrapped->single = 1; $wrapped->single = 1;
$wrapped->answernumbering = 0; $wrapped->answernumbering = 0;
@ -844,7 +734,7 @@ function qtype_multianswer_extract_question($text) {
$wrapped->incorrectfeedback['format'] = '1'; $wrapped->incorrectfeedback['format'] = '1';
$wrapped->incorrectfeedback['itemid'] = ''; $wrapped->incorrectfeedback['itemid'] = '';
$wrapped->layout = 0; $wrapped->layout = 0;
} else if(!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_REGULAR])) { } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_REGULAR])) {
$wrapped->qtype = 'multichoice'; $wrapped->qtype = 'multichoice';
$wrapped->single = 1; $wrapped->single = 1;
$wrapped->answernumbering = 0; $wrapped->answernumbering = 0;
@ -858,7 +748,7 @@ function qtype_multianswer_extract_question($text) {
$wrapped->incorrectfeedback['format'] = '1'; $wrapped->incorrectfeedback['format'] = '1';
$wrapped->incorrectfeedback['itemid'] = ''; $wrapped->incorrectfeedback['itemid'] = '';
$wrapped->layout = 1; $wrapped->layout = 1;
} else if(!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_HORIZONTAL])) { } else if (!empty($answerregs[ANSWER_REGEX_ANSWER_TYPE_MULTICHOICE_HORIZONTAL])) {
$wrapped->qtype = 'multichoice'; $wrapped->qtype = 'multichoice';
$wrapped->single = 1; $wrapped->single = 1;
$wrapped->answernumbering = 0; $wrapped->answernumbering = 0;
@ -885,15 +775,15 @@ function qtype_multianswer_extract_question($text) {
$wrapped->feedback = array(); $wrapped->feedback = array();
$wrapped->shuffleanswers = 1; $wrapped->shuffleanswers = 1;
$wrapped->questiontext['text'] = $answerregs[0]; $wrapped->questiontext['text'] = $answerregs[0];
$wrapped->questiontext['format'] = 0 ; $wrapped->questiontext['format'] = 0;
$wrapped->questiontext['itemid'] = '' ; $wrapped->questiontext['itemid'] = '';
$answerindex = 0 ; $answerindex = 0;
$remainingalts = $answerregs[ANSWER_REGEX_ALTERNATIVES]; $remainingalts = $answerregs[ANSWER_REGEX_ALTERNATIVES];
while (preg_match('/~?'.ANSWER_ALTERNATIVE_REGEX.'/', $remainingalts, $altregs)) { while (preg_match('/~?'.ANSWER_ALTERNATIVE_REGEX.'/', $remainingalts, $altregs)) {
if ('=' == $altregs[ANSWER_ALTERNATIVE_REGEX_FRACTION]) { if ('=' == $altregs[ANSWER_ALTERNATIVE_REGEX_FRACTION]) {
$wrapped->fraction["$answerindex"] = '1'; $wrapped->fraction["$answerindex"] = '1';
} else if ($percentile = $altregs[ANSWER_ALTERNATIVE_REGEX_PERCENTILE_FRACTION]){ } else if ($percentile = $altregs[ANSWER_ALTERNATIVE_REGEX_PERCENTILE_FRACTION]) {
$wrapped->fraction["$answerindex"] = .01 * $percentile; $wrapped->fraction["$answerindex"] = .01 * $percentile;
} else { } else {
$wrapped->fraction["$answerindex"] = '0'; $wrapped->fraction["$answerindex"] = '0';
@ -927,17 +817,14 @@ function qtype_multianswer_extract_question($text) {
} }
$tmp = explode($altregs[0], $remainingalts, 2); $tmp = explode($altregs[0], $remainingalts, 2);
$remainingalts = $tmp[1]; $remainingalts = $tmp[1];
$answerindex++ ; $answerindex++;
} }
$question->defaultgrade += $wrapped->defaultgrade; $question->defaultgrade += $wrapped->defaultgrade;
$question->options->questions[$positionkey] = clone($wrapped); $question->options->questions[$positionkey] = clone($wrapped);
$question->questiontext['text'] = implode("{#$positionkey}", $question->questiontext['text'] = implode("{#$positionkey}",
explode($answerregs[0], $question->questiontext['text'], 2)); explode($answerregs[0], $question->questiontext['text'], 2));
// echo"<p>questiontext 2 <pre>";print_r($question->questiontext);echo"<pre></p>";
} }
// echo"<p>questiontext<pre>";print_r($question->questiontext);echo"<pre></p>";
$question->questiontext = $question->questiontext; $question->questiontext = $question->questiontext;
// echo"<p>question<pre>";print_r($question);echo"<pre></p>";
return $question; return $question;
} }

View file

@ -55,9 +55,8 @@ class backup_qtype_randomsamatch_plugin extends backup_qtype_plugin {
$pluginwrapper->add_child($randomsamatch); $pluginwrapper->add_child($randomsamatch);
// set source to populate the data // set source to populate the data
$randomsamatch->set_source_table('question_randomsamatch', array('question' => backup::VAR_PARENTID)); $randomsamatch->set_source_table('question_randomsamatch',
array('question' => backup::VAR_PARENTID));
// don't need to annotate ids nor files
return $plugin; return $plugin;
} }

View file

@ -43,10 +43,9 @@ class restore_qtype_randomsamatch_plugin extends restore_qtype_plugin {
// Add own qtype stuff // Add own qtype stuff
$elename = 'randomsamatch'; $elename = 'randomsamatch';
$elepath = $this->get_pathfor('/randomsamatch'); // we used get_recommended_name() so this works $elepath = $this->get_pathfor('/randomsamatch');
$paths[] = new restore_path_element($elename, $elepath); $paths[] = new restore_path_element($elename, $elepath);
return $paths; // And we return the interesting paths return $paths; // And we return the interesting paths
} }
@ -64,7 +63,8 @@ class restore_qtype_randomsamatch_plugin extends restore_qtype_plugin {
$newquestionid = $this->get_new_parentid('question'); $newquestionid = $this->get_new_parentid('question');
$questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false; $questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false;
// If the question has been created by restore, we need to create its question_randomsamatch too // If the question has been created by restore, we need to create its
// question_randomsamatch too
if ($questioncreated) { if ($questioncreated) {
// Adjust some columns // Adjust some columns
$data->question = $newquestionid; $data->question = $newquestionid;
@ -72,8 +72,6 @@ class restore_qtype_randomsamatch_plugin extends restore_qtype_plugin {
$newitemid = $DB->insert_record('question_randomsamatch', $data); $newitemid = $DB->insert_record('question_randomsamatch', $data);
// Create mapping // Create mapping
$this->set_mapping('question_randomsamatch', $oldid, $newitemid); $this->set_mapping('question_randomsamatch', $oldid, $newitemid);
} else {
// Nothing to remap if the question already existed
} }
} }

View file

@ -33,19 +33,15 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2007 Jamie Pratt me@jamiep.org * @copyright 2007 Jamie Pratt me@jamiep.org
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/ */
class question_edit_randomsamatch_form extends question_edit_form { class qtype_randomsamatch_edit_form extends question_edit_form {
/** protected function definition_inner($mform) {
* Add question-type specific form fields.
*
* @param MoodleQuickForm $mform the form being built.
*/
protected function definition_inner(&$mform) {
$questionstoselect = array(); $questionstoselect = array();
for ($i=2; $i<=QUESTION_NUMANS; $i++){ for ($i = 2; $i <= qtype_randomsamatch::MAX_SUBQUESTIONS; $i++) {
$questionstoselect[$i] = $i; $questionstoselect[$i] = $i;
} }
$mform->addElement('select', 'choose', get_string("randomsamatchnumber", "quiz"), $questionstoselect); $mform->addElement('select', 'choose',
get_string('randomsamatchnumber', 'quiz'), $questionstoselect);
$mform->setType('feedback', PARAM_RAW); $mform->setType('feedback', PARAM_RAW);
$mform->addElement('hidden', 'fraction', 0); $mform->addElement('hidden', 'fraction', 0);
@ -54,11 +50,11 @@ class question_edit_randomsamatch_form extends question_edit_form {
protected function data_preprocessing($question) { protected function data_preprocessing($question) {
if (empty($question->name)) { if (empty($question->name)) {
$question->name = get_string("randomsamatch", "quiz"); $question->name = get_string('randomsamatch', 'quiz');
} }
if (empty($question->questiontext)) { if (empty($question->questiontext)) {
$question->questiontext = get_string("randomsamatchintro", "quiz"); $question->questiontext = get_string('randomsamatchintro', 'quiz');
} }
return $question; return $question;
} }
@ -67,22 +63,22 @@ class question_edit_randomsamatch_form extends question_edit_form {
return 'randomsamatch'; return 'randomsamatch';
} }
function validation($data, $files) { public function validation($data, $files) {
global $QTYPES, $DB; global $DB;
$errors = parent::validation($data, $files); $errors = parent::validation($data, $files);
if (isset($data->categorymoveto)) { if (isset($data->categorymoveto)) {
list($category) = explode(',', $data['categorymoveto']); list($category) = explode(',', $data['categorymoveto']);
} else { } else {
list($category) = explode(',', $data['category']); list($category) = explode(',', $data['category']);
} }
$saquestions = $QTYPES['randomsamatch']->get_sa_candidates($category); $saquestions = question_bank::get_qtype('randomsamatch')->get_sa_candidates($category);
$numberavailable = count($saquestions); $numberavailable = count($saquestions);
if ($saquestions === false){ if ($saquestions === false) {
$a = new stdClass(); $a = new stdClass();
$a->catname = $DB->get_field('question_categories', 'name', array('id' => $category)); $a->catname = $DB->get_field('question_categories', 'name', array('id' => $category));
$errors['choose'] = get_string('nosaincategory', 'qtype_randomsamatch', $a); $errors['choose'] = get_string('nosaincategory', 'qtype_randomsamatch', $a);
} elseif ($numberavailable < $data['choose']){ } else if ($numberavailable < $data['choose']) {
$a = new stdClass(); $a = new stdClass();
$a->catname = $DB->get_field('question_categories', 'name', array('id' => $category)); $a->catname = $DB->get_field('question_categories', 'name', array('id' => $category));
$a->nosaquestions = $numberavailable; $a->nosaquestions = $numberavailable;

View file

@ -36,27 +36,21 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
class question_randomsamatch_qtype extends qtype_match { class qtype_randomsamatch extends question_type {
/// Extends 'match' as there are quite a few simularities... const MAX_SUBQUESTIONS = 10;
function name() { public function requires_qtypes() {
return 'randomsamatch'; return array('shortanswer', 'match');
} }
function requires_qtypes() { public function is_usable_by_random() {
return array('shortanswer');
}
function is_usable_by_random() {
return false; return false;
} }
function get_question_options(&$question) { public function get_question_options($question) {
global $DB, $OUTPUT; global $DB;
if (!$question->options = $DB->get_record('question_randomsamatch', array('question' => $question->id))) { $question->options = $DB->get_record('question_randomsamatch',
echo $OUTPUT->notification('Error: Missing question options for random short answer question '.$question->id.'!'); array('question' => $question->id), '*', MUST_EXIST);
return false;
}
// This could be included as a flag in the database. It's already // This could be included as a flag in the database. It's already
// supported by the code. // supported by the code.
@ -66,7 +60,7 @@ class question_randomsamatch_qtype extends qtype_match {
} }
function save_question_options($question) { public function save_question_options($question) {
global $DB; global $DB;
$options->question = $question->id; $options->question = $question->id;
$options->choose = $question->choose; $options->choose = $question->choose;
@ -76,23 +70,24 @@ class question_randomsamatch_qtype extends qtype_match {
return $result; return $result;
} }
if ($existing = $DB->get_record("question_randomsamatch", array("question" => $options->question))) { if ($existing = $DB->get_record('question_randomsamatch',
array('question' => $options->question))) {
$options->id = $existing->id; $options->id = $existing->id;
$DB->update_record("question_randomsamatch", $options); $DB->update_record('question_randomsamatch', $options);
} else { } else {
$DB->insert_record("question_randomsamatch", $options); $DB->insert_record('question_randomsamatch', $options);
} }
return true; return true;
} }
function delete_question($questionid, $contextid) { public function delete_question($questionid, $contextid) {
global $DB; global $DB;
$DB->delete_records('question_randomsamatch', array('question' => $questionid)); $DB->delete_records('question_randomsamatch', array('question' => $questionid));
parent::delete_question($questionid, $contextid); parent::delete_question($questionid, $contextid);
} }
function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) { public function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
// Choose a random shortanswer question from the category: // Choose a random shortanswer question from the category:
// We need to make sure that no question is used more than once in the // We need to make sure that no question is used more than once in the
// quiz. Therfore the following need to be excluded: // quiz. Therfore the following need to be excluded:
@ -190,7 +185,7 @@ class question_randomsamatch_qtype extends qtype_match {
foreach ($responses as $response) { foreach ($responses as $response) {
$wqid = $response[0]; $wqid = $response[0];
$state->responses[$wqid] = $response[1]; $state->responses[$wqid] = $response[1];
if (!isset($wrappedquestions[$wqid])){ if (!isset($wrappedquestions[$wqid])) {
if (!$wrappedquestions[$wqid] = $DB->get_record('question', array('id' => $wqid))) { if (!$wrappedquestions[$wqid] = $DB->get_record('question', array('id' => $wqid))) {
echo $OUTPUT->notification("Couldn't get question (id=$wqid)!"); echo $OUTPUT->notification("Couldn't get question (id=$wqid)!");
return false; return false;
@ -231,23 +226,11 @@ class question_randomsamatch_qtype extends qtype_match {
return true; return true;
} }
function extract_response($rawresponse, $nameprefix) { public function get_sa_candidates($categorylist, $questionsinuse = 0) {
/// Simple implementation that does not check with the database
/// and thus - does not bother to check whether there has been
/// any changes to the question options.
$response = array();
$rawitems = explode(',', $rawresponse->answer);
foreach ($rawitems as $rawitem) {
$splits = explode('-', $rawitem, 2);
$response[$nameprefix.$splits[0]] = $splits[1];
}
return $response;
}
function get_sa_candidates($categorylist, $questionsinuse = 0) {
global $DB; global $DB;
list ($usql, $params) = $DB->get_in_or_equal($categorylist); list ($usql, $params) = $DB->get_in_or_equal($categorylist);
list ($ques_usql, $ques_params) = $DB->get_in_or_equal(explode(',', $questionsinuse), SQL_PARAMS_QM, null, false); list ($ques_usql, $ques_params) = $DB->get_in_or_equal(explode(',', $questionsinuse),
SQL_PARAMS_QM, null, false);
$params = array_merge($params, $ques_params); $params = array_merge($params, $ques_params);
return $DB->get_records_select('question', return $DB->get_records_select('question',
"qtype = 'shortanswer' " . "qtype = 'shortanswer' " .
@ -256,80 +239,6 @@ class question_randomsamatch_qtype extends qtype_match {
"AND hidden = '0'" . "AND hidden = '0'" .
"AND id $ques_usql", $params); "AND id $ques_usql", $params);
} }
function get_all_responses($question, $state) {
$answers = array();
if (is_array($question->options->subquestions)) {
foreach ($question->options->subquestions as $aid => $answer) {
if ($answer->questiontext) {
foreach($answer->options->answers as $ans ){
$answer->answertext = $ans->answer ;
}
$r = new stdClass();
$r->answer = $answer->questiontext . ": " . $answer->answertext;
$r->credit = 1;
$answers[$aid] = $r;
}
}
}
$result = new stdClass();
$result->id = $question->id;
$result->responses = $answers;
return $result;
}
/**
* The difference between this method an get_all_responses is that this
* method is not passed a state object. It is the possible answers to a
* question no matter what the state.
* This method is not called for random questions.
* @return array of possible answers.
*/
function get_possible_responses(&$question) {
global $QTYPES;
static $answers = array();
if (!isset($answers[$question->id])){
if ($question->options->subcats) {
// recurse into subcategories
$categorylist = question_categorylist($question->category);
} else {
$categorylist = array($question->category);
}
$question->options->subquestions = $this->get_sa_candidates($categorylist);
foreach ($question->options->subquestions as $key => $wrappedquestion) {
if (!$QTYPES[$wrappedquestion->qtype]
->get_question_options($wrappedquestion)) {
return false;
}
// Now we overwrite the $question->options->answers field to only
// *one* (the first) correct answer. This loop can be deleted to
// take all answers into account (i.e. put them all into the
// drop-down menu.
$foundcorrect = false;
foreach ($wrappedquestion->options->answers as $answer) {
if ($foundcorrect || $answer->fraction != 1.0) {
unset($wrappedquestion->options->answers[$answer->id]);
} else if (!$foundcorrect) {
$foundcorrect = true;
}
}
}
$answers[$question->id] = array();
if (is_array($question->options->subquestions)) {
foreach ($question->options->subquestions as $subqid => $answer) {
if ($answer->questiontext) {
$ans = array_shift($answer->options->answers);
$answer->answertext = $ans->answer ;
$r = new stdClass();
$r->answer = $answer->questiontext . ": " . $answer->answertext;
$r->credit = 1;
$answers[$question->id][$subqid] = array($ans->id => $r);
}
}
}
}
return $answers[$question->id];
}
/** /**
* @param object $question * @param object $question
@ -337,7 +246,7 @@ class question_randomsamatch_qtype extends qtype_match {
* guess by a student might give or an empty string which means will not * guess by a student might give or an empty string which means will not
* calculate. * calculate.
*/ */
function get_random_guess_score($question) { public function get_random_guess_score($question) {
return 1/$question->options->choose; return 1/$question->options->choose;
} }
} }