MDL-47494 gapselect: ddwtos and gapselect qtypes now very nearly work.
authorTim Hunt <T.J.Hunt@open.ac.uk>
Tue, 1 Feb 2011 23:44:40 +0000 (23:44 +0000)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Tue, 1 Feb 2011 23:44:40 +0000 (23:44 +0000)
Todo rendering questions with files, and ddwtos javascript.

question/type/gapselect/db/install.xml
question/type/gapselect/edit_form_base.php
question/type/gapselect/edit_gapselect_form.php
question/type/gapselect/lang/en/qtype_gapselect.php
question/type/gapselect/pix/icon.gif [moved from question/type/gapselect/icon.gif with 100% similarity]
question/type/gapselect/questiontype.php
question/type/gapselect/questiontypebase.php

index 0eb4f71..cfab23d 100755 (executable)
@@ -9,10 +9,13 @@
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="questionid"/>
         <FIELD NAME="questionid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="shuffleanswers"/>
         <FIELD NAME="shuffleanswers" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="questionid" NEXT="correctfeedback"/>
-        <FIELD NAME="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="shuffleanswers" NEXT="partiallycorrectfeedback"/>
-        <FIELD NAME="partiallycorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="correctfeedback" NEXT="incorrectfeedback"/>
-        <FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="partiallycorrectfeedback" NEXT="shownumcorrect"/>
-        <FIELD NAME="shownumcorrect" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="incorrectfeedback"/>
+        <FIELD NAME="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any correct response." PREVIOUS="shuffleanswers" NEXT="correctfeedbackformat"/>
+        <FIELD NAME="correctfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="correctfeedback" NEXT="partiallycorrectfeedback"/>
+        <FIELD NAME="partiallycorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any partially correct response." PREVIOUS="correctfeedbackformat" NEXT="partiallycorrectfeedbackformat"/>
+        <FIELD NAME="partiallycorrectfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="partiallycorrectfeedback" NEXT="incorrectfeedback"/>
+        <FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="partiallycorrectfeedbackformat" NEXT="incorrectfeedbackformat"/>
+        <FIELD NAME="incorrectfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="incorrectfeedback" NEXT="shownumcorrect"/>
+        <FIELD NAME="shownumcorrect" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="incorrectfeedbackformat"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="questionid"/>
index 822eae2..26c97a0 100755 (executable)
@@ -8,6 +8,7 @@
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class qtype_gapselect_edit_form_base extends question_edit_form {
+    const MAX_GROUPS = 8;
 
     /** @var array of HTML tags allowed in choices / drag boxes. */
     protected $allowedhtmltags = array(
@@ -64,7 +65,7 @@ class qtype_gapselect_edit_form_base extends question_edit_form {
      * definition_inner adds all specific fields to the form.
      * @param object $mform (the form being built).
      */
-    function definition_inner(&$mform) {
+    function definition_inner($mform) {
         global $CFG;
 
         //add the answer (choice) fields to the form
@@ -75,19 +76,14 @@ class qtype_gapselect_edit_form_base extends question_edit_form {
     }
 
     protected function definition_answer_choice(&$mform) {
-        $mform->addElement('header', 'choicehdr',   get_string('choices', 'qtype_gapselect'));
+        $mform->addElement('header', 'choicehdr', get_string('choices', 'qtype_gapselect'));
 
-        $mform->addElement('checkbox', 'shuffleanswers',  get_string('shuffle', 'quiz'));
+        $mform->addElement('checkbox', 'shuffleanswers', get_string('shuffle', 'qtype_gapselect'));
         $mform->setDefault('shuffleanswers', 0);
 
         $textboxgroup = array();
-
-        $grouparray = array();
-        $grouparray[] =& $mform->createElement('text', 'answer', get_string('answer', 'qtype_gapselect'), array('size'=>30, 'class'=>'tweakcss'));
-        $grouparray[] =& $mform->createElement('static', '', '',' '.get_string('group', 'qtype_gapselect').' ');
-
-        $grouparray = $this->choice_group($mform, $grouparray);
-        $textboxgroup[] = $mform->createElement('group','choices', 'Choice {no}',$grouparray);
+        $textboxgroup[] = $mform->createElement('group', 'choices',
+                get_string('choicex', 'qtype_gapselect'), $this->choice_group($mform));
 
         if (isset($this->question->options)) {
             $countanswers = count($this->question->options->answers);
@@ -96,7 +92,7 @@ class qtype_gapselect_edit_form_base extends question_edit_form {
         }
 
         if ($this->question->formoptions->repeatelements) {
-            $defaultstartnumbers = QUESTION_NUMANS_START*2;
+            $defaultstartnumbers = QUESTION_NUMANS_START * 2;
             $repeatsatstart = max($defaultstartnumbers, QUESTION_NUMANS_START, $countanswers + QUESTION_NUMANS_ADD);
         } else {
             $repeatsatstart = $countanswers;
@@ -107,29 +103,51 @@ class qtype_gapselect_edit_form_base extends question_edit_form {
         $this->repeat_elements($textboxgroup, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', QUESTION_NUMANS_ADD, get_string('addmorechoiceblanks', 'qtype_gapselect'));
     }
 
+    protected function choice_group($mform) {
+        $options = array();
+        for ($i = 1; $i <= self::MAX_GROUPS; $i += 1) {
+            $options[$i] = $i;
+        }
+        $grouparray = array();
+        $grouparray[] = $mform->createElement('text', 'answer', get_string('answer', 'qtype_gapselect'), array('size'=>30, 'class'=>'tweakcss'));
+        $grouparray[] = $mform->createElement('static', '', '',' '.get_string('group', 'qtype_gapselect').' ');
+        $grouparray[] = $mform->createElement('select', 'choicegroup', get_string('group', 'qtype_gapselect'), $options);
+        return $grouparray;
+    }
 
+    protected function repeated_options() {
+        $repeatedoptions = array();
+        $repeatedoptions['choicegroup']['default'] = '1';
+        return $repeatedoptions;
+    }
 
-    public function set_data($question) {
-        if (isset($question->options)) {
-            $options = $question->options;
-            $default_values = array();
-            if (count($options->answers)) {
-                $key = 0;
-                foreach ($options->answers as $answer) {
-                    $default_values['choices['.$key.'][answer]'] = $answer->answer;
-                    $default_values += $this->default_values_from_feedback_field($answer->feedback, $key);
-                    $key++;
-                }
+    public function data_preprocessing($question) {
+        $question = parent::data_preprocessing($question);
+        $question = $this->data_preprocessing_combined_feedback($question, true);
+        $question = $this->data_preprocessing_hints($question, true, true);
+
+        $question = $this->data_preprocessing_answers($question, true);
+        if (!empty($question->options->answers)) {
+            $key = 0;
+            foreach ($question->options->answers as $answer) {
+                $question = $this->data_preprocessing_choice($question, $answer, $key);
+                $key++;
             }
+        }
 
-            $default_values['shuffleanswers'] =  $question->options->shuffleanswers;
-            $default_values['correctfeedback'] =  $question->options->correctfeedback;
-            $default_values['partiallycorrectfeedback'] =  $question->options->partiallycorrectfeedback;
-            $default_values['incorrectfeedback'] =  $question->options->incorrectfeedback;
-            $default_values['shownumcorrect'] = $question->options->shownumcorrect;
-            $question = (object)((array)$question + $default_values);
+        if (!empty($question->options)) {
+            $question->shuffleanswers = $question->options->shuffleanswers;
         }
-        parent::set_data($question);
+
+        return $question;
+    }
+
+    protected function data_preprocessing_choice($question, $answer, $key) {
+        // See comment in data_preprocessing_answers.
+        unset($this->_form->_defaultValues['choices[$key][choicegroup]']);
+        $question->choices[$key]['answer'] = $answer->answer;
+        $question->choices[$key]['choicegroup'] = $answer->feedback;
+        return $question;
     }
 
     public function validation($data, $files) {
@@ -138,7 +156,7 @@ class qtype_gapselect_edit_form_base extends question_edit_form {
         $choices = $data['choices'];
 
         //check the whether the slots are valid
-        $errorsinquestiontext = $this->validate_slots($questiontext, $choices);
+        $errorsinquestiontext = $this->validate_slots($questiontext['text'], $choices);
         if ($errorsinquestiontext) {
             $errors['questiontext'] = $errorsinquestiontext;
         }
@@ -196,17 +214,8 @@ class qtype_gapselect_edit_form_base extends question_edit_form {
         }
         return false;
     }
+
     function qtype() {
         return '';
     }
-
-    protected function default_values_from_feedback_field($feedback, $key) {
-        $default_values = array();
-        return $default_values;
-    }
-
-    protected function repeated_options() {
-        $repeatedoptions = array();
-        return $repeatedoptions;
-    }
 }
\ No newline at end of file
index 62ab9c6..cb5cf44 100755 (executable)
@@ -35,31 +35,10 @@ require_once($CFG->dirroot . '/question/type/gapselect/edit_form_base.php');
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class qtype_gapselect_edit_form extends qtype_gapselect_edit_form_base {
-
     // HTML tags allowed in answers (choices).
     protected $allowedhtmltags = array();
 
     function qtype() {
         return 'gapselect';
     }
-
-    protected function default_values_from_feedback_field($feedback, $key) {
-        $default_values = array();
-        $default_values['choices['.$key.'][selectgroup]'] = $feedback;
-        return $default_values;
-    }
-
-    protected function repeated_options() {
-        $repeatedoptions = array();
-        $repeatedoptions['selectgroup']['default'] = '1';
-        return $repeatedoptions;
-    }
-    protected function choice_group(&$mform, $grouparray) {
-        $options = array();
-        for ($i = 1; $i <= 8; $i += 1) {
-            $options[$i] = $i;
-        }
-        $grouparray[] =& $mform->createElement('select', 'selectgroup', get_string('group', 'qtype_gapselect'), $options);
-        return $grouparray;
-    }
 }
index 099b40b..5cce5c2 100755 (executable)
@@ -30,10 +30,12 @@ $string['addinggapselect'] = 'Adding a select missing words question';
 $string['addmorechoiceblanks'] = 'Blanks for {no} more choices';
 $string['answer'] = 'Answer';
 $string['choices'] = 'Choices';
+$string['choicex'] = 'Choice {no}';
 $string['correctansweris'] = 'The correct answer is: {$a}';
+$string['editinggapselect'] = 'Editing a select missing words question';
 $string['gapselect'] = 'Select missing words';
 $string['gapselect_help'] = 'Type in some question text like "The [[1]] jumped over the [[2]]", then enter the possible words to go in gaps 1 and 2 underneath.';
 $string['gapselectsummary'] = 'Missing words in some text are filled in using dropdown menus.';
-$string['editinggapselect'] = 'Editing a select missing words question';
 $string['group'] = 'Group';
 $string['pleaseputananswerineachbox'] = 'Please put an answer in each box.';
+$string['shuffle'] = 'Shuffle';
index 736eb5c..dc4af4c 100755 (executable)
@@ -40,7 +40,7 @@ require_once($CFG->dirroot . '/question/type/gapselect/questiontypebase.php');
  */
 class qtype_gapselect extends qtype_gapselect_base {
     protected function choice_options_to_feedback($choice) {
-        return $choice['selectgroup'];
+        return $choice['choicegroup'];
     }
 
     protected function make_choice($choicedata) {
index 7b3b336..472fbac 100755 (executable)
@@ -49,11 +49,12 @@ abstract class qtype_gapselect_base extends question_type {
     protected abstract function choice_options_to_feedback($choice);
 
     public function save_question_options($question) {
+        global $DB;
+        $context = $question->context;
         $result = new stdClass();
 
-        if (!$oldanswers = get_records('question_answers', 'question', $question->id, 'id ASC')) {
-            $oldanswers = array();
-        }
+        $oldanswers = $DB->get_records('question_answers',
+                array('question' => $question->id), 'id ASC');
 
         // Insert all the new answers
         foreach ($question->choices as $key => $choice) {
@@ -64,80 +65,56 @@ abstract class qtype_gapselect_base extends question_type {
 
             $feedback = $this->choice_options_to_feedback($choice);
 
-            if ($answer = array_shift($oldanswers)) {  // Existing answer, so reuse it
+            if ($answer = array_shift($oldanswers)) {
                 $answer->answer = $choice['answer'];
-                $answer->fraction = 0;
                 $answer->feedback = $feedback;
-                if (!update_record('question_answers', $answer)) {
-                    $result->error = "Could not update question type '".$this->name()."' question answer! (id=$answer->id)";
-                    return $result;
-                }
+                $DB->update_record('question_answers', $answer);
+
             } else {
-                $answer = new stdClass;
-                $answer->answer = $choice['answer'];
+                $answer = new stdClass();
                 $answer->question = $question->id;
+                $answer->answer = $choice['answer'];
+                $answer->answerformat = FORMAT_HTML;
                 $answer->fraction = 0;
                 $answer->feedback = $feedback;
-                if (!$answer->id = insert_record('question_answers', $answer)) {
-                    $result->error = 'Could not insert question type \''.$this->name().'\'  question answer!';
-                    return $result;
-                }
+                $answer->feedbackformat = 0;
+                $DB->insert_record('question_answers', $answer);
             }
         }
 
         // Delete old answer records
-        if (!empty($oldanswers)) {
-            foreach($oldanswers as $oa) {
-                delete_records('question_answers', 'id', $oa->id);
-            }
+        foreach($oldanswers as $oa) {
+            delete_records('question_answers', 'id', $oa->id);
         }
 
-        $update = true;
-        $options = get_record('question_' . $this->name(), 'questionid', $question->id);
+        $options = $DB->get_record('question_' . $this->name(), array('questionid' => $question->id));
         if (!$options) {
-            $update = false;
-            $options = new stdClass;
+            $options = new stdClass();
             $options->questionid = $question->id;
+            $options->correctfeedback = '';
+            $options->partiallycorrectfeedback = '';
+            $options->incorrectfeedback = '';
+            $options->id = $DB->insert_record('question_' . $this->name(), $options);
         }
 
         $options->shuffleanswers = !empty($question->shuffleanswers);
-        $options->correctfeedback = trim($question->correctfeedback);
-        $options->partiallycorrectfeedback = trim($question->partiallycorrectfeedback);
-        $options->shownumcorrect = !empty($question->shownumcorrect);
-        $options->incorrectfeedback = trim($question->incorrectfeedback);
-
-        if ($update) {
-            if (!update_record('question_'.$this->name(), $options)) {
-                $result->error = "Could not update question type '".$this->name()."' options! (id=$options->id)";
-                return $result;
-            }
-
-        } else {
-            if (!insert_record('question_gapselect', $options)) {
-                $result->error = 'Could not insert question type \''.$this->name().'\' options!';
-                return $result;
-            }
-        }
+        $options = $this->save_combined_feedback_helper($options, $question, $context, true);
+        $DB->update_record('question_' . $this->name(), $options);
 
         $this->save_hints($question, true);
-
-        return true;
     }
 
     public function get_question_options($question) {
-        // Get additional information from database and attach it to the question object
-        if (!$question->options = get_record('question_'.$this->name(), 'questionid', $question->id)) {
-            notify('Error: Missing question options for question type \''.$this->name().'\' question '.$question->id.'!');
-            return false;
-        }
-
+        global $DB;
+        $question->options = $DB->get_record('question_'.$this->name(),
+                array('questionid' => $question->id), '*', MUST_EXIST);
         parent::get_question_options($question);
-        return true;
     }
 
-    public function delete_question($questionid) {
-        delete_records('question_'.$this->name(), 'questionid', $questionid);
-        return parent::delete_question($questionid);
+    public function delete_question($questionid, $contextid) {
+        global $DB;
+        $DB->delete_records('question_'.$this->name(), array('questionid' => $questionid));
+        return parent::delete_question($questionid, $contextid);
     }
 
     /**
@@ -261,15 +238,15 @@ abstract class qtype_gapselect_base extends question_type {
         $arrayofchoices = $this->get_array_of_choices($question);
         $arrayofplaceholdeers = $this->get_array_of_placeholders($question);
 
-        $correctplayeers = array();
+        $correctplayers = array();
         foreach($arrayofplaceholdeers as $ph) {
             foreach($arrayofchoices as $key=>$choice) {
                 if(($key+1) == $ph) {
-                    $correctplayeers[]= $choice;
+                    $correctplayers[]= $choice;
                 }
             }
         }
-        return $correctplayeers;
+        return $correctplayers;
     }
 
     protected function get_array_of_placeholders($question) {
@@ -290,22 +267,21 @@ abstract class qtype_gapselect_base extends question_type {
 
         $output = array();
         foreach ($slots as $slot) {
-            $output[]=substr($slot, 2, (strlen($slot)-4));//2 is for'[[' and 4 is for '[[]]'
+            $output[] = substr($slot, 2, strlen($slot) - 4); //2 is for '[[' and 4 is for '[[]]'.
         }
         return $output;
      }
 
-    protected function get_group_of_players ($question, $state, $subquestions, $group) {
-        $goupofanswers=array();
-        foreach($subquestions as $key=>$subquestion) {
-            if($subquestion[$this->choice_group_key()] == $group) {
-                $goupofanswers[] =  $subquestion;
+    protected function get_group_of_players($question, $state, $subquestions, $group) {
+        $goupofanswers = array();
+        foreach ($subquestions as $key => $subquestion) {
+            if ($subquestion[$this->choice_group_key()] == $group) {
+                $goupofanswers[] = $subquestion;
             }
         }
 
-        //shuffle answers within this group
+        // Shuffle answers within this group
         if ($question->options->shuffleanswers == 1) {
-            srand($state->attempt);
             shuffle($goupofanswers);
         }
         return $goupofanswers;
@@ -331,5 +307,24 @@ abstract class qtype_gapselect_base extends question_type {
         return $parts;
     }
 
+    function move_files($questionid, $oldcontextid, $newcontextid) {
+        parent::move_files($questionid, $oldcontextid, $newcontextid);
+
+        $fs = get_file_storage();
+        $fs->move_area_files_to_new_context($oldcontextid,
+                $newcontextid, 'question', 'correctfeedback', $questionid);
+        $fs->move_area_files_to_new_context($oldcontextid,
+                $newcontextid, 'question', 'partiallycorrectfeedback', $questionid);
+        $fs->move_area_files_to_new_context($oldcontextid,
+                $newcontextid, 'question', 'incorrectfeedback', $questionid);
+    }
 
+    protected function delete_files($questionid, $contextid) {
+        parent::delete_files($questionid, $contextid);
+
+        $fs = get_file_storage();
+        $fs->delete_area_files($contextid, 'question', 'correctfeedback', $questionid);
+        $fs->delete_area_files($contextid, 'question', 'partiallycorrectfeedback', $questionid);
+        $fs->delete_area_files($contextid, 'question', 'incorrectfeedback', $questionid);
+    }
 }