--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<XMLDB PATH="question/type/gapselect/db" VERSION="20080909" COMMENT="XMLDB file for Moodle question/type/gapselect"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
+>
+ <TABLES>
+ <TABLE NAME="question_gapselect" COMMENT="Defines select missing words questions">
+ <FIELDS>
+ <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"/>
+ </FIELDS>
+ <KEYS>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="questionid"/>
+ <KEY NAME="questionid" TYPE="foreign" FIELDS="questionid" REFTABLE="questions" REFFIELDS="id" PREVIOUS="primary"/>
+ </KEYS>
+ </TABLE>
+ </TABLES>
+</XMLDB>
\ No newline at end of file
--- /dev/null
+<?php
+
+
+/**
+ * Elements embedded in question text editing form definition.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_gapselect_edit_form_base extends question_edit_form {
+
+ /** @var array of HTML tags allowed in choices / drag boxes. */
+ protected $allowedhtmltags = array(
+ 'sub',
+ 'sup',
+ 'b',
+ 'i',
+ 'em',
+ 'strong'
+ );
+
+ /** @var string regex to match HTML open tags. */
+ private $htmltstarttagsandattributes = '/<\s*\w.*?>/';
+
+ /** @var string regex to match HTML close tags or br. */
+ private $htmltclosetags = '~<\s*/\s*\w\s*.*?>|<\s*br\s*>~';
+
+ /** @var string regex to select text like [[cat]] (including the square brackets). */
+ private $squareBracketsRegex = '/\[\[[^]]*?\]\]/';
+
+ private function get_html_tags($text) {
+ $textarray = array();
+ foreach ($this->allowedhtmltags as $htmltag) {
+ $tagpair = "/<\s*\/?\s*$htmltag\s*.*?>/";
+ preg_match_all($tagpair, $text, $textarray);
+ if ($textarray[0]) {
+ return $textarray[0];
+ }
+ }
+ preg_match_all($this->htmltstarttagsandattributes, $text, $textarray);
+ if ($textarray[0]) {
+ $tag = htmlspecialchars($textarray[0][0]);
+ $allowedtaglist = $this->get_list_of_printable_allowed_tags($this->allowedhtmltags);
+ return $tag . " is not allowed (only $allowedtaglist and corresponsing closing tags are allowed)";
+ }
+ preg_match_all($this->htmltclosetags, $text, $textarray);
+ if ($textarray[0]) {
+ $tag = htmlspecialchars($textarray[0][0]);
+ $allowedtaglist=$this->get_list_of_printable_allowed_tags($this->allowedhtmltags);
+ return $tag . " is not allowed HTML tag! (only $allowedtaglist and corresponsing closing tags are allowed)";
+ }
+ return false;
+ }
+
+ private function get_list_of_printable_allowed_tags($allowedhtmltags) {
+ $allowedtaglist = null;
+ foreach ($allowedhtmltags as $htmltag) {
+ $allowedtaglist .= htmlspecialchars('<'.$htmltag.'>') . ', ';
+ }
+ return $allowedtaglist;
+ }
+
+ /**
+ * definition_inner adds all specific fields to the form.
+ * @param object $mform (the form being built).
+ */
+ function definition_inner(&$mform) {
+ global $CFG;
+
+ //add the answer (choice) fields to the form
+ $this->definition_answer_choice($mform);
+
+ $this->add_combined_feedback_fields(true);
+ $this->add_interactive_settings(true, true);
+ }
+
+ protected function definition_answer_choice(&$mform) {
+ $mform->addElement('header', 'choicehdr', get_string('choices', 'qtype_gapselect'));
+
+ $mform->addElement('checkbox', 'shuffleanswers', get_string('shuffle', 'quiz'));
+ $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);
+
+ if (isset($this->question->options)) {
+ $countanswers = count($this->question->options->answers);
+ } else {
+ $countanswers = 0;
+ }
+
+ if ($this->question->formoptions->repeatelements) {
+ $defaultstartnumbers = QUESTION_NUMANS_START*2;
+ $repeatsatstart = max($defaultstartnumbers, QUESTION_NUMANS_START, $countanswers + QUESTION_NUMANS_ADD);
+ } else {
+ $repeatsatstart = $countanswers;
+ }
+
+ $repeatedoptions = $this->repeated_options();
+ $mform->setType('answer', PARAM_RAW);
+ $this->repeat_elements($textboxgroup, $repeatsatstart, $repeatedoptions, 'noanswers', 'addanswers', QUESTION_NUMANS_ADD, get_string('addmorechoiceblanks', 'qtype_gapselect'));
+ }
+
+
+
+ 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++;
+ }
+ }
+
+ $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);
+ }
+ parent::set_data($question);
+ }
+
+ public function validation($data, $files) {
+ $errors = parent::validation($data, $files);
+ $questiontext = $data['questiontext'];
+ $choices = $data['choices'];
+
+ //check the whether the slots are valid
+ $errorsinquestiontext = $this->validate_slots($questiontext, $choices);
+ if ($errorsinquestiontext) {
+ $errors['questiontext'] = $errorsinquestiontext;
+ }
+ foreach ($choices as $key => $choice) {
+ $answer = $choice['answer'];
+
+ //check whether the html-tags are allowed tags
+ $validtags = $this->get_html_tags($answer);
+ if (is_array($validtags)) {
+ continue;
+ }
+ if ($validtags) {
+ $errors['choices['.$key.']'] = $validtags;
+ }
+ }
+ return $errors;
+ }
+
+ private function validate_slots($questiontext, $choices) {
+ $error = 'Please check the Question text: ';
+ if (!$questiontext) {
+ return $error . 'The question text is empty!';
+ }
+
+ $matches = array();
+ preg_match_all($this->squareBracketsRegex, $questiontext, $matches);
+ $slots = $matches[0];
+
+ if (!$slots) {
+ return $error . 'The question text is not in the correct format!';
+ }
+
+ $output = array();
+ foreach ($slots as $slot) {
+ // The 2 is for'[[' and 4 is for '[[]]'.
+ $output[] = substr($slot, 2, (strlen($slot)-4));
+ }
+
+ $slots = $output;
+ $found = false;
+ foreach ($slots as $slot) {
+ $found = false;
+ foreach ($choices as $key => $choice) {
+ if ($slot == $key + 1) {
+ if (!$choice['answer']) {
+ return " Please check Choices: The choice <b>$slot</b> empty.";
+ }
+ $found = true;
+ break;
+ }
+ }
+ if (!$found) {
+ return $error . "<b>$slot</b> was not found in Choices! (only the choice numbers that exist in choices are allowed to be used a place holders!";
+ }
+ }
+ 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
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Defines the editing form for the select missing words question type.
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once($CFG->dirroot . '/question/type/gapselect/edit_form_base.php');
+
+
+/**
+ * Select from drop down list question editing form definition.
+ *
+ * @copyright 2009 The Open University
+ * @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;
+ }
+}
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Language strings for the select missing words question type.
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+$string['addinggapselect'] = 'Adding a select missing words question';
+$string['addmorechoiceblanks'] = 'Blanks for {no} more choices';
+$string['answer'] = 'Answer';
+$string['choices'] = 'Choices';
+$string['correctansweris'] = 'The correct answer is: $a';
+$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.';
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Select from drop down list question definition class.
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once($CFG->dirroot . '/question/type/gapselect/questionbase.php');
+
+/**
+ * Represents select missing words question.
+ *
+ * @copyright 2009 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_gapselect_question extends qtype_gapselect_question_base {
+//is actually exactly the same.
+}
+
+
+/**
+ * Represents one of the choices (select box option).
+ *
+ * @copyright 2009 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_gapselect_choice {
+ public $text;
+ public $selectgroup;
+
+ public function __construct($text, $selectgroup = 1) {
+ $this->text = $text;
+ $this->selectgroup = $selectgroup;
+ }
+
+ public function choice_group() {
+ return $this->selectgroup;
+ }
+}
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Definition class for embedded element in question text question. Parent of drag and drop and select from
+ * drop down list and ?others? *
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+/**
+ * Represents embedded element in question text question. Parent of drag and drop and select from
+ * drop down list and ?others?
+ *
+ * @copyright 2009 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_gapselect_question_base extends question_graded_automatically_with_countback {
+ /** @var boolean Whether the question stems should be shuffled. */
+ public $shufflechoices;
+
+ public $correctfeedback;
+ public $partiallycorrectfeedback;
+ public $incorrectfeedback;
+
+ /** @var array of arrays. The keys are the choice group numbers. The values
+ * are arrays of qtype_gapselect_choice objects. */
+ public $choices;
+
+ /**
+ * @var array place number => group number of the places in the question
+ * text where choices can be put. Places are numbered from 1.
+ */
+ public $places;
+
+ /**
+ * @var array of strings, one longer than $places, which is achieved by
+ * indexing from 0. The bits of question text that go between the placeholders.
+ */
+ public $textfragments;
+
+ /** @var array index of the right choice for each stem. */
+ public $rightchoices;
+
+ /** @var array shuffled choice indexes. */
+ protected $choiceorder;
+
+ public function init_first_step(question_attempt_step $step) {
+ foreach ($this->choices as $group => $choices) {
+ $varname = '_choiceorder' . $group;
+
+ if ($step->has_qt_var($varname)) {
+ $choiceorder = explode(',', $step->get_qt_var($varname));
+
+ } else {
+ $choiceorder = array_keys($choices);
+ if ($this->shufflechoices) {
+ shuffle($choiceorder);
+ }
+ }
+
+ foreach ($choiceorder as $key => $value) {
+ $this->choiceorder[$group][$key + 1] = $value;
+ }
+
+ if (!$step->has_qt_var($varname)) {
+ $step->set_qt_var($varname, implode(',', $this->choiceorder[$group]));
+ }
+ }
+ }
+
+ public function get_question_summary() {
+ $question = html_to_text($this->format_questiontext(), 0, false);
+ $groups = array();
+ foreach ($this->choices as $group => $choices) {
+ $cs = array();
+ foreach ($choices as $choice) {
+ $cs[] = html_to_text($this->format_text($choice->text), 0, false);
+ }
+ $groups[] = '[[' . $group . ']] -> {' . implode(' / ', $cs) . '}';
+ }
+ return $question . '; ' . implode('; ', $groups);
+ }
+
+ protected function get_selected_choice($group, $shuffledchoicenumber) {
+ $choiceno = $this->choiceorder[$group][$shuffledchoicenumber];
+ return $this->choices[$group][$choiceno];
+ }
+
+ public function summarise_response(array $response) {
+ $matches = array();
+ $allblank = true;
+ foreach ($this->places as $place => $group) {
+ if (array_key_exists($this->field($place), $response) &&
+ $response[$this->field($place)]) {
+ $choices[] = '{' . html_to_text($this->format_text($this->get_selected_choice(
+ $group, $response[$this->field($place)])->text), 0, false) . '}';
+ $allblank = false;
+ } else {
+ $choices[] = '{}';
+ }
+ }
+ if ($allblank) {
+ return null;
+ }
+ return implode(' ', $choices);
+ }
+
+ public function get_random_guess_score() {
+ $accum = 0;
+
+ foreach ($this->places as $placegroup) {
+ $accum += 1 / count($this->choices[$placegroup]);
+ }
+
+ return $accum / count($this->places);
+ }
+
+ public function clear_wrong_from_response(array $response) {
+ foreach ($this->places as $place => $notused) {
+ if (array_key_exists($this->field($place), $response) &&
+ $response[$this->field($place)] != $this->get_right_choice_for($place)) {
+ $response[$this->field($place)] = '0';
+ }
+ }
+ return $response;
+ }
+
+ public function get_num_parts_right(array $response) {
+ $numright = 0;
+ foreach ($this->places as $place => $notused) {
+ if (!array_key_exists($this->field($place), $response)) {
+ continue;
+ }
+ if ($response[$this->field($place)] == $this->get_right_choice_for($place)) {
+ $numright += 1;
+ }
+ }
+ return array($numright, count($this->places));
+ }
+
+ /**
+ * @param integer $key stem number
+ * @return string the question-type variable name.
+ */
+ public function field($place) {
+ return 'p' . $place;
+ }
+
+ public function get_expected_data() {
+ $vars = array();
+ foreach ($this->places as $place => $notused) {
+ $vars[$this->field($place)] = PARAM_INTEGER;
+ }
+ return $vars;
+ }
+
+ public function get_correct_response() {
+ $response = array();
+ foreach ($this->places as $place => $notused) {
+ $response[$this->field($place)] = $this->get_right_choice_for($place);
+ }
+ return $response;
+ }
+
+ public function get_right_choice_for($place) {
+ $group = $this->places[$place];
+ foreach ($this->choiceorder[$group] as $choicekey => $choiceid) {
+ if ($this->rightchoices[$place] == $choiceid) {
+ return $choicekey;
+ }
+ }
+ }
+
+ public function get_ordered_choices($group) {
+ $choices = array();
+ foreach ($this->choiceorder[$group] as $choicekey => $choiceid) {
+ $choices[$choicekey] = $this->choices[$group][$choiceid];
+ }
+ return $choices;
+ }
+
+ public function is_complete_response(array $response) {
+ $complete = true;
+ foreach ($this->places as $place => $notused) {
+ $complete = $complete && !empty($response[$this->field($place)]);
+ }
+ return $complete;
+ }
+
+ public function is_gradable_response(array $response) {
+ foreach ($this->places as $place => $notused) {
+ if (!empty($response[$this->field($place)])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public function is_same_response(array $prevresponse, array $newresponse) {
+ foreach ($this->places as $place => $notused) {
+ $fieldname = $this->field($place);
+ if (!question_utils::arrays_same_at_key_integer(
+ $prevresponse, $newresponse, $fieldname)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public function get_validation_error(array $response) {
+ if ($this->is_complete_response($response)) {
+ return '';
+ }
+ return get_string('pleaseputananswerineachbox', 'qtype_gapselect');
+ }
+
+ public function grade_response(array $response) {
+ list($right, $total) = $this->get_num_parts_right($response);
+ $fraction = $right / $total;
+ return array($fraction, question_state::graded_state_for_fraction($fraction));
+ }
+
+ public function compute_final_grade($responses, $totaltries) {
+ $totalscore = 0;
+ foreach ($this->places as $place => $notused) {
+ $fieldname = $this->field($place);
+
+ $lastwrongindex = -1;
+ $finallyright = false;
+ foreach ($responses as $i => $response) {
+ if (!array_key_exists($fieldname, $response) ||
+ $response[$fieldname] != $this->get_right_choice_for($place)) {
+ $lastwrongindex = $i;
+ $finallyright = false;
+ } else {
+ $finallyright = true;
+ }
+ }
+
+ if ($finallyright) {
+ $totalscore += max(0, 1 - ($lastwrongindex + 1) * $this->penalty);
+ }
+ }
+
+ return $totalscore / count($this->places);
+ }
+
+ public function classify_response(array $response) {
+ $parts = array();
+ foreach ($this->places as $place => $group) {
+ if (!array_key_exists($this->field($place), $response) ||
+ !$response[$this->field($place)]) {
+ $parts[$place] = question_classified_response::no_response();
+ continue;
+ }
+
+ $fieldname = $this->field($place);
+ $choiceno = $this->choiceorder[$group][$response[$fieldname]];
+ $choice = $this->choices[$group][$choiceno];
+ $parts[$place] = new question_classified_response(
+ $choiceno, html_to_text($this->format_text($choice->text), 0, false),
+ $this->get_right_choice_for($place) == $response[$fieldname]);
+ }
+ return $parts;
+ }
+}
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Question type class for the select missing words question type.
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+require_once($CFG->libdir . '/questionlib.php');
+require_once($CFG->dirroot . '/question/engine/lib.php');
+require_once($CFG->dirroot . '/question/format/xml/format.php');
+
+require_once($CFG->dirroot . '/question/type/gapselect/questiontypebase.php');
+
+/**
+ * The select missing words question type class.
+ *
+ * @copyright 2009 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_gapselect extends qtype_gapselect_base {
+ protected function choice_options_to_feedback($choice) {
+ return $choice['selectgroup'];
+ }
+
+ protected function make_choice($choicedata) {
+ return new qtype_gapselect_choice($choicedata->answer, $choicedata->feedback);
+ }
+
+ protected function feedback_to_choice_options($feedback) {
+ return array('selectgroup' => $feedback);
+ }
+
+
+ protected function choice_group_key() {
+ return 'selectgroup';
+ }
+
+ function import_from_xml($data, $question, $format, $extra=null) {
+ if (!isset($data['@']['type']) || $data['@']['type'] != 'gapselect') {
+ return false;
+ }
+
+ $question = $format->import_headers($data);
+ $question->qtype = 'gapselect';
+
+ $question->shuffleanswers = $format->trans_single(
+ $format->getpath($data, array('#', 'shuffleanswers', 0, '#'), 1));
+
+ if (!empty($data['#']['selectoption'])) {
+ // Modern XML format.
+ $selectoptions = $data['#']['selectoption'];
+ $question->answer = array();
+ $question->selectgroup = array();
+
+ foreach ($data['#']['selectoption'] as $selectoptionxml) {
+ $question->choices[] = array(
+ 'answer' => $format->getpath($selectoptionxml, array('#', 'text', 0, '#'), '', true),
+ 'selectgroup' => $format->getpath($selectoptionxml, array('#', 'group', 0, '#'), 1),
+ );
+ }
+
+ } else {
+ // Legacy format containing PHP serialisation.
+ foreach ($data['#']['answer'] as $answerxml) {
+ $ans = $format->import_answer($answerxml);
+ $question->choices[] = array(
+ 'answer' => $ans->answer,
+ 'selectgroup' => $ans->feedback,
+ );
+ }
+ }
+
+ $format->import_combined_feedback($question, $data, true);
+ $format->import_hints($question, $data, true);
+
+ return $question;
+ }
+
+ function export_to_xml($question, $format, $extra = null) {
+ $output = '';
+
+ $output .= ' <shuffleanswers>' . $question->options->shuffleanswers . "</shuffleanswers>\n";
+
+ $output .= $format->write_combined_feedback($question->options);
+
+ foreach ($question->options->answers as $answer) {
+ $output .= " <selectoption>\n";
+ $output .= $format->writetext($answer->answer, 3);
+ $output .= " <group>{$answer->feedback}</group>\n";
+ $output .= " </selectoption>\n";
+ }
+
+ return $output;
+ }
+
+ /*
+ * Backup the data in the question
+ *
+ * This is used in question/backuplib.php
+ */
+ public function backup($bf, $preferences, $question, $level = 6) {
+ $status = true;
+ $gapselects = get_records("question_gapselect", "questionid", $question, "id");
+
+ //If there are gapselect
+ if ($gapselects) {
+ //Iterate over each gapselect
+ foreach ($gapselects as $gapselect) {
+ $status = fwrite ($bf,start_tag("SDDLS",$level,true));
+ //Print oumultiresponse contents
+ fwrite ($bf,full_tag("SHUFFLEANSWERS",$level+1,false,$gapselect->shuffleanswers));
+ fwrite ($bf,full_tag("CORRECTFEEDBACK",$level+1,false,$gapselect->correctfeedback));
+ fwrite ($bf,full_tag("PARTIALLYCORRECTFEEDBACK",$level+1,false,$gapselect->partiallycorrectfeedback));
+ fwrite ($bf,full_tag("INCORRECTFEEDBACK",$level+1,false,$gapselect->incorrectfeedback));
+ fwrite ($bf,full_tag("SHOWNUMCORRECT",$level+1,false,$gapselect->shownumcorrect));
+ $status = fwrite ($bf,end_tag("SDDLS",$level,true));
+ }
+
+ //Now print question_answers
+ $status = question_backup_answers($bf,$preferences,$question);
+ }
+ return $status;
+ }
+
+ /**
+ * Restores the data in the question (This is used in question/restorelib.php)
+ *
+ */
+ public function restore($old_question_id,$new_question_id,$info,$restore) {
+ $status = true;
+
+ //Get the gapselect array
+ $gapselects = $info['#']['SDDLS'];
+
+ //Iterate over oumultiresponses
+ for($i = 0; $i < sizeof($gapselects); $i++) {
+ $mul_info = $gapselects[$i];
+
+ //Now, build the question_gapselect record structure
+ $gapselect = new stdClass;
+ $gapselect->questionid = $new_question_id;
+ $gapselect->shuffleanswers = isset($mul_info['#']['SHUFFLEANSWERS']['0']['#'])?backup_todb($mul_info['#']['SHUFFLEANSWERS']['0']['#']):'';
+ if (array_key_exists("CORRECTFEEDBACK", $mul_info['#'])) {
+ $gapselect->correctfeedback = backup_todb($mul_info['#']['CORRECTFEEDBACK']['0']['#']);
+ } else {
+ $gapselect->correctfeedback = '';
+ }
+ if (array_key_exists("PARTIALLYCORRECTFEEDBACK", $mul_info['#'])) {
+ $gapselect->partiallycorrectfeedback = backup_todb($mul_info['#']['PARTIALLYCORRECTFEEDBACK']['0']['#']);
+ } else {
+ $gapselect->partiallycorrectfeedback = '';
+ }
+ if (array_key_exists("INCORRECTFEEDBACK", $mul_info['#'])) {
+ $gapselect->incorrectfeedback = backup_todb($mul_info['#']['INCORRECTFEEDBACK']['0']['#']);
+ } else {
+ $gapselect->incorrectfeedback = '';
+ }
+ if (array_key_exists('SHOWNUMCORRECT', $mul_info['#'])) {
+ $gapselect->shownumcorrect = backup_todb($mul_info['#']['SHOWNUMCORRECT']['0']['#']);
+ } else if (array_key_exists('CORRECTRESPONSESFEEDBACK', $mul_info['#'])) {
+ $gapselect->shownumcorrect = backup_todb($mul_info['#']['CORRECTRESPONSESFEEDBACK']['0']['#']);
+ } else {
+ $gapselect->shownumcorrect = 0;
+ }
+
+ $newid = insert_record ("question_gapselect",$gapselect);
+
+ //Do some output
+ if (($i+1) % 50 == 0) {
+ if (!defined('RESTORE_SILENTLY')) {
+ echo ".";
+ if (($i+1) % 1000 == 0) {
+ echo "<br />";
+ }
+ }
+ backup_flush(300);
+ }
+
+ if (!$newid) {
+ $status = false;
+ }
+ }
+ return $status;
+ }
+
+}
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Question type class for the embedded element in question text question types.
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+require_once($CFG->libdir . '/questionlib.php');
+require_once($CFG->dirroot . '/question/engine/lib.php');
+require_once($CFG->dirroot . '/question/format/xml/format.php');
+
+
+/**
+ * The embedded element in question text question type class.
+ *
+ * @copyright 2009 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_gapselect_base extends question_type {
+ /**
+ * Choices are stored in the question_answers table, and any options need to
+ * be put into the feedback field somehow. This method is responsible for
+ * converting all the options to a single string for this purpose. It is used
+ * by {@link save_question_options()}.
+ * @param array $choice the form data relating to this choice.
+ * @return string ready to store in the database.
+ */
+ protected abstract function choice_options_to_feedback($choice);
+
+ public function save_question_options($question) {
+ $result = new stdClass();
+
+ if (!$oldanswers = get_records('question_answers', 'question', $question->id, 'id ASC')) {
+ $oldanswers = array();
+ }
+
+ // Insert all the new answers
+ foreach ($question->choices as $key => $choice) {
+
+ if (trim($choice['answer']) == '') {
+ continue;
+ }
+
+ $feedback = $this->choice_options_to_feedback($choice);
+
+ if ($answer = array_shift($oldanswers)) { // Existing answer, so reuse it
+ $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;
+ }
+ } else {
+ $answer = new stdClass;
+ $answer->answer = $choice['answer'];
+ $answer->question = $question->id;
+ $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;
+ }
+ }
+ }
+
+ // Delete old answer records
+ if (!empty($oldanswers)) {
+ foreach($oldanswers as $oa) {
+ delete_records('question_answers', 'id', $oa->id);
+ }
+ }
+
+ $update = true;
+ $options = get_record('question_'.$this->name(), 'questionid', $question->id);
+ if (!$options) {
+ $update = false;
+ $options = new stdClass;
+ $options->questionid = $question->id;
+ }
+
+ $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;
+ }
+ }
+
+ $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;
+ }
+
+ parent::get_question_options($question);
+ return true;
+ }
+
+ public function delete_question($questionid) {
+ delete_records('question_'.$this->name(), 'questionid', $questionid);
+ return parent::delete_question($questionid);
+ }
+
+ /**
+ * Used by {@link initialise_question_instance()} to set up the choice-specific data.
+ * @param object $choicedata as loaded from the question_answers table.
+ * @return object an appropriate object for representing the choice.
+ */
+ protected abstract function make_choice($choicedata);
+
+ protected function initialise_question_instance(question_definition $question, $questiondata) {
+ parent::initialise_question_instance($question, $questiondata);
+
+ $question->shufflechoices = $questiondata->options->shuffleanswers;
+
+ $question->correctfeedback = $questiondata->options->correctfeedback;
+ $question->partiallycorrectfeedback = $questiondata->options->partiallycorrectfeedback;
+ $question->incorrectfeedback = $questiondata->options->incorrectfeedback;
+ $question->shownumcorrect = $questiondata->options->shownumcorrect;
+
+ $question->choices = array();
+ $choiceindexmap= array();
+
+ // Store the choices in arrays by group.
+ $i = 1;
+ foreach ($questiondata->options->answers as $choicedata) {
+ $choice = $this->make_choice($choicedata);
+
+ if (array_key_exists($choice->choice_group(), $question->choices)) {
+ $question->choices[$choice->choice_group()][] = $choice;
+ } else {
+ $question->choices[$choice->choice_group()][1] = $choice;
+ }
+
+ end($question->choices[$choice->choice_group()]);
+ $choiceindexmap[$i] = array($choice->choice_group(),
+ key($question->choices[$choice->choice_group()]));
+ $i += 1;
+ }
+
+ $question->places = array();
+ $question->textfragments = array();
+ $question->rightchoices = array();
+ // Break up the question text, and store the fragments, places and right answers.
+
+ $bits = preg_split('/\[\[(\d+)]]/', $question->questiontext, null, PREG_SPLIT_DELIM_CAPTURE);
+ $question->textfragments[0] = array_shift($bits);
+ $i = 1;
+
+ while (!empty($bits)) {
+ $choice = array_shift($bits);
+
+ list($group, $choiceindex) = $choiceindexmap[$choice];
+ $question->places[$i] = $group;
+ $question->rightchoices[$i] = $choiceindex;
+
+ $question->textfragments[$i] = array_shift($bits);
+ $i += 1;
+ }
+ }
+
+ protected function make_hint($hint) {
+ return question_hint_with_parts::load_from_record($hint);
+ }
+
+ public function get_random_guess_score($questiondata) {
+ $question = $this->make_question($questiondata);
+ return $question->get_random_guess_score();
+ }
+
+ /**
+ * This function should reverse {@link choice_options_to_feedback()}.
+ * @param string $feedback the data loaded from the database.
+ * @return array the choice options.
+ */
+ protected abstract function feedback_to_choice_options($feedback);
+
+ /**
+ * This method gets the choices (answers)
+ * in a 2 dimentional array.
+ *
+ * @param object $question
+ * @return array of groups
+ */
+ protected function get_array_of_choices($question) {
+ $subquestions = $question->options->answers;
+ $count = 0;
+ foreach ($subquestions as $key=>$subquestion) {
+ $answers[$count]['id'] = $subquestion->id;
+ $answers[$count]['answer'] = $subquestion->answer;
+ $answers[$count]['fraction'] = $subquestion->fraction;
+ $answers[$count] += $this->feedback_to_choice_options($subquestion->feedback);
+ $answers[$count]['choice'] = $count+1;
+ ++$count;
+ }
+ return $answers;
+ }
+
+ /* This method gets the choices (answers) and sort them by groups
+ * in a 2 dimentional array.
+ *
+ * @param object $question
+ * @return array of groups
+ */
+ protected function get_array_of_groups($question, $state) {
+ $answers = $this->get_array_of_choices($question);
+ $arr = array();
+ for($group=1;$group<count($answers);$group++) {
+ $players = $this->get_group_of_players ($question, $state, $answers, $group);
+ if($players) {
+ $arr [$group]= $players;
+ }
+ }
+ return $arr;
+ }
+
+ /**
+ * This method gets the correct answers in a 2 dimentional array.
+ *
+ * @param object $question
+ * @return array of groups
+ */
+ protected function get_correct_answers($question) {
+ $arrayofchoices = $this->get_array_of_choices($question);
+ $arrayofplaceholdeers = $this->get_array_of_placeholders($question);
+
+ $correctplayeers = array();
+ foreach($arrayofplaceholdeers as $ph) {
+ foreach($arrayofchoices as $key=>$choice) {
+ if(($key+1) == $ph) {
+ $correctplayeers[]= $choice;
+ }
+ }
+ }
+ return $correctplayeers;
+ }
+
+ protected function get_array_of_placeholders($question) {
+ $qtext = $question->questiontext;
+ $error = '<b> ERROR</b>: Please check the form for this question. ';
+ if(!$qtext) {
+ echo $error . 'The question text is empty!';
+ return false;
+ }
+
+ //get the slots
+ $slots = $this->getEmbeddedTextArray($question);
+
+ if(!$slots) {
+ echo $error . 'The question text is not in the correct format!';
+ return false;
+ }
+
+ $output = array();
+ foreach ($slots as $slot) {
+ $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;
+ }
+ }
+
+ //shuffle answers within this group
+ if ($question->options->shuffleanswers == 1) {
+ srand($state->attempt);
+ shuffle($goupofanswers);
+ }
+ return $goupofanswers;
+ }
+
+ public function get_possible_responses($questiondata) {
+ $question = $this->make_question($questiondata);
+
+ $parts = array();
+ foreach ($question->places as $place => $group) {
+ $choices = array();
+
+ foreach ($question->choices[$group] as $i => $choice) {
+ $choices[$i] = new question_possible_response(
+ html_to_text($question->format_text($choice->text), 0, false),
+ $question->rightchoices[$place] == $i);
+ }
+ $choices[null] = question_possible_response::no_response();
+
+ $parts[$place] = $choices;
+ }
+
+ return $parts;
+ }
+
+
+}
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Select from drop down list question renderer class.
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once($CFG->dirroot . '/question/type/gapselect/rendererbase.php');
+
+
+/**
+ * Generates the output for select missing words questions.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_gapselect_renderer extends qtype_elements_embedded_in_question_text_renderer {
+ protected function embedded_element(question_attempt $qa, $place, question_display_options $options) {
+ $question = $qa->get_question();
+ $group = $question->places[$place];
+
+ $fieldname = $question->field($place);
+
+ $value = $qa->get_last_qt_var($question->field($place));
+
+ $attributes = array(
+ 'id' => $this->box_id($qa, 'p' . $place, $group),
+ 'class' => 'group' . $group
+ );
+
+ if ($options->readonly) {
+ $attributes['disabled'] = 'disabled';
+ }
+
+ $orderedchoices = $question->get_ordered_choices($group);
+ $selectoptions = array();
+ foreach ($orderedchoices as $orderedchoicevalue => $orderedchoice) {
+ $selectoptions[$orderedchoicevalue] = $orderedchoice->text;
+ }
+
+ $feedbackimage = '';
+ if ($options->correctness) {
+ $response = $qa->get_last_qt_data();
+ if (array_key_exists($fieldname, $response)) {
+ $fraction = (int) ($response[$fieldname] == $question->get_right_choice_for($place));
+ $attributes['class'] = $this->feedback_class($fraction);
+ $feedbackimage = $this->feedback_image($fraction);
+ }
+ }
+
+ return html_writer::select($selectoptions, $qa->get_qt_field_name($fieldname), $value, ' ', $attributes) . ' ' . $feedbackimage;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Generates the output for question types where the question includes embedded interactive elements in the
+ * question text.
+ *
+ * @copyright 2010 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class qtype_elements_embedded_in_question_text_renderer extends qtype_with_combined_feedback_renderer {
+ public function formulation_and_controls(question_attempt $qa,
+ question_display_options $options) {
+
+ $question = $qa->get_question();
+
+ $questiontext = '';
+ foreach ($question->textfragments as $i => $fragment) {
+ if ($i > 0) {
+ $questiontext .= $this->embedded_element($qa, $i, $options);
+ }
+ $questiontext .= $fragment;
+ }
+
+
+ $result = '';
+ $result .= html_writer::tag('div', $question->format_text($questiontext),
+ array('class' => $this->qtext_classname(), 'id' => $qa->get_qt_field_name('')));
+
+ $result .= $this->post_qtext_elements($qa, $options);
+
+ if ($qa->get_state() == question_state::$invalid) {
+ $result .= html_writer::nonempty_tag('div',
+ $question->get_validation_error($qa->get_last_qt_data()),
+ array('class' => 'validationerror'));
+ }
+
+ return $result;
+ }
+
+ protected function qtext_classname() {
+ return 'qtext';
+ }
+
+ protected abstract function embedded_element(question_attempt $qa, $place, question_display_options $options);
+
+ protected function post_qtext_elements(question_attempt $qa, question_display_options $options) {
+ return '';
+ }
+
+ protected function box_id(question_attempt $qa, $place, $group) {
+ return $qa->get_qt_field_name($place) . '_' . $group;
+ }
+
+ public function specific_feedback(question_attempt $qa) {
+ return $this->combined_feedback($qa);
+ }
+
+ public function correct_response(question_attempt $qa) {
+ $question = $qa->get_question();
+
+ $correctanswer = '';
+ foreach ($question->textfragments as $i => $fragment) {
+ if ($i > 0) {
+ $group = $question->places[$i];
+ $choice = $question->choices[$group][$question->rightchoices[$i]];
+ $correctanswer .= '[' . str_replace('-', '‑',
+ $choice->text) . ']';
+ }
+ $correctanswer .= $fragment;
+ }
+
+ if (!empty($correctanswer)) {
+ return get_string('correctansweris', 'qtype_gapselect', $correctanswer);
+ }
+ }
+}
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Contains the helper class for the select missing words question type tests.
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+/**
+ * Test helper class for the select missing words question type.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_gapselect_test_helper {
+ /**
+ * @return qtype_gapselect_question
+ */
+ public static function make_a_gapselect_question() {
+ question_bank::load_question_definition_classes('gapselect');
+ $gapselect = new qtype_gapselect_question();
+
+ test_question_maker::initialise_a_question($gapselect);
+
+ $gapselect->name = 'Selection from drop down list question';
+ $gapselect->questiontext = 'The [[1]] brown [[2]] jumped over the [[3]] dog.';
+ $gapselect->generalfeedback = 'This sentence uses each letter of the alphabet.';
+ $gapselect->qtype = question_bank::get_qtype('gapselect');
+
+ $gapselect->shufflechoices = true;
+
+ test_question_maker::set_standard_combined_feedback_fields($gapselect);
+
+ $gapselect->choices = array(
+ 1 => array(
+ 1 => new qtype_gapselect_choice('quick', 1),
+ 2 => new qtype_gapselect_choice('slow', 1)),
+ 2 => array(
+ 1 => new qtype_gapselect_choice('fox', 2),
+ 2 => new qtype_gapselect_choice('dog', 2)),
+ 3 => array(
+ 1 => new qtype_gapselect_choice('lazy', 3),
+ 2 => new qtype_gapselect_choice('assiduous', 3)),
+ );
+
+ $gapselect->places = array(1 => 1, 2 => 2, 3 => 3);
+ $gapselect->rightchoices = array(1 => 1, 2 => 1, 3 => 1);
+ $gapselect->textfragments = array('The ', ' brown ', ' jumped over the ', ' dog.');
+
+ return $gapselect;
+ }
+
+ /**
+ * @return qtype_gapselect_question
+ */
+ public static function make_a_maths_gapselect_question() {
+ question_bank::load_question_definition_classes('gapselect');
+ $gapselect = new qtype_gapselect_question();
+
+ test_question_maker::initialise_a_question($gapselect);
+
+ $gapselect->name = 'Selection from drop down list question';
+ $gapselect->questiontext = 'Fill in the operators to make this equation work: ' .
+ '7 [[1]] 11 [[2]] 13 [[1]] 17 [[2]] 19 = 3';
+ $gapselect->generalfeedback = 'This sentence uses each letter of the alphabet.';
+ $gapselect->qtype = question_bank::get_qtype('gapselect');
+
+ $gapselect->shufflechoices = true;
+
+ test_question_maker::set_standard_combined_feedback_fields($gapselect);
+
+ $gapselect->choices = array(
+ 1 => array(
+ 1 => new qtype_gapselect_choice('+', 1, true),
+ 2 => new qtype_gapselect_choice('-', 1, true),
+ 3 => new qtype_gapselect_choice('*', 1, true),
+ 4 => new qtype_gapselect_choice('/', 1, true),
+ ));
+
+ $gapselect->places = array(1 => 1, 2 => 1, 3 => 1, 4 => 1);
+ $gapselect->rightchoices = array(1 => 1, 2 => 2, 3 => 1, 4 => 2);
+ $gapselect->textfragments = array('7 ', ' 11 ', ' 13 ', ' 17 ', ' 19 = 3');
+
+ return $gapselect;
+ }
+}
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Unit tests for the select missing words question definition class.
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php');
+require_once($CFG->dirroot . '/question/type/gapselect/simpletest/helper.php');
+
+
+/**
+ * Unit tests for the matching question definition class.
+ *
+ * @copyright 2009 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_gapselect_question_test extends UnitTestCase {
+
+ public function test_get_question_summary() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $this->assertEqual('The [[1]] brown [[2]] jumped over the [[3]] dog.; [[1]] -> {quick / slow}; [[2]] -> {fox / dog}; [[3]] -> {lazy / assiduous}',
+ $gapselect->get_question_summary());
+ }
+
+ public function test_get_question_summary_maths() {
+ $gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
+ $this->assertEqual('Fill in the operators to make this equation work: ' .
+ '7 [[1]] 11 [[2]] 13 [[1]] 17 [[2]] 19 = 3; [[1]] -> {+ / - / * / /}',
+ $gapselect->get_question_summary());
+ }
+
+ public function test_summarise_response() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual('{quick} {fox} {lazy}',
+ $gapselect->summarise_response(array('p1' => '1', 'p2' => '1', 'p3' => '1')));
+ }
+
+ public function test_summarise_response_maths() {
+ $gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual('{+} {-} {+} {-}',
+ $gapselect->summarise_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2')));
+ }
+
+ public function test_get_random_guess_score() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $this->assertEqual(0.5, $gapselect->get_random_guess_score());
+ }
+
+ public function test_get_random_guess_score_maths() {
+ $gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
+ $this->assertEqual(0.25, $gapselect->get_random_guess_score());
+ }
+
+ public function test_get_right_choice_for() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual(1, $gapselect->get_right_choice_for(1));
+ $this->assertEqual(1, $gapselect->get_right_choice_for(2));
+ }
+
+ public function test_get_right_choice_for_maths() {
+ $gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual(1, $gapselect->get_right_choice_for(1));
+ $this->assertEqual(2, $gapselect->get_right_choice_for(2));
+ }
+
+ public function test_clear_wrong_from_response() {
+ $gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $initialresponse = array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1');
+ $this->assertEqual(array('p1' => '1', 'p2' => '0', 'p3' => '1', 'p4' => '0'),
+ $gapselect->clear_wrong_from_response($initialresponse));
+ }
+
+ public function test_get_num_parts_right() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual(array(2, 3),
+ $gapselect->get_num_parts_right(array('p1' => '1', 'p2' => '1', 'p3' => '2')));
+ $this->assertEqual(array(3, 3),
+ $gapselect->get_num_parts_right(array('p1' => '1', 'p2' => '1', 'p3' => '1')));
+ }
+
+ public function test_get_num_parts_right_maths() {
+ $gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual(array(2, 4),
+ $gapselect->get_num_parts_right(array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1')));
+ }
+
+ public function test_get_expected_data() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual(array('p1' => PARAM_INT, 'p2' => PARAM_INT, 'p3' => PARAM_INT),
+ $gapselect->get_expected_data());
+ }
+
+ public function test_get_correct_response() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual(array('p1' => '1', 'p2' => '1', 'p3' => '1'),
+ $gapselect->get_correct_response());
+ }
+
+ public function test_get_correct_response_maths() {
+ $gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'),
+ $gapselect->get_correct_response());
+ }
+
+ public function test_is_same_response() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertTrue($gapselect->is_same_response(
+ array(),
+ array('p1' => '0', 'p2' => '0', 'p3' => '0')));
+
+ $this->assertFalse($gapselect->is_same_response(
+ array(),
+ array('p1' => '1', 'p2' => '0', 'p3' => '0')));
+
+ $this->assertFalse($gapselect->is_same_response(
+ array('p1' => '0', 'p2' => '0', 'p3' => '0'),
+ array('p1' => '1', 'p2' => '0', 'p3' => '0')));
+
+ $this->assertTrue($gapselect->is_same_response(
+ array('p1' => '1', 'p2' => '2', 'p3' => '3'),
+ array('p1' => '1', 'p2' => '2', 'p3' => '3')));
+
+ $this->assertFalse($gapselect->is_same_response(
+ array('p1' => '1', 'p2' => '2', 'p3' => '3'),
+ array('p1' => '1', 'p2' => '2', 'p3' => '2')));
+ }
+ public function test_is_complete_response() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertFalse($gapselect->is_complete_response(array()));
+ $this->assertFalse($gapselect->is_complete_response(
+ array('p1' => '1', 'p2' => '1', 'p3' => '0')));
+ $this->assertFalse($gapselect->is_complete_response(array('p1' => '1')));
+ $this->assertTrue($gapselect->is_complete_response(
+ array('p1' => '1', 'p2' => '1', 'p3' => '1')));
+ }
+
+ public function test_is_gradable_response() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertFalse($gapselect->is_gradable_response(array()));
+ $this->assertFalse($gapselect->is_gradable_response(
+ array('p1' => '0', 'p2' => '0', 'p3' => '0')));
+ $this->assertTrue($gapselect->is_gradable_response(
+ array('p1' => '1', 'p2' => '1', 'p3' => '0')));
+ $this->assertTrue($gapselect->is_gradable_response(array('p1' => '1')));
+ $this->assertTrue($gapselect->is_gradable_response(
+ array('p1' => '1', 'p2' => '1', 'p3' => '1')));
+ }
+
+ public function test_grading() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual(array(1, question_state::$gradedright),
+ $gapselect->grade_response(array('p1' => '1', 'p2' => '1', 'p3' => '1')));
+ $this->assertEqual(array(1/3, question_state::$gradedpartial),
+ $gapselect->grade_response(array('p1' => '1')));
+ $this->assertEqual(array(0, question_state::$gradedwrong),
+ $gapselect->grade_response(array('p1' => '2', 'p2' => '2', 'p3' => '2')));
+ }
+
+ public function test_grading_maths() {
+ $gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual(array(1, question_state::$gradedright),
+ $gapselect->grade_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2')));
+ $this->assertEqual(array(0.5, question_state::$gradedpartial),
+ $gapselect->grade_response(array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1')));
+ $this->assertEqual(array(0, question_state::$gradedwrong),
+ $gapselect->grade_response(array('p1' => '0', 'p2' => '1', 'p3' => '2', 'p4' => '1')));
+ }
+
+ public function test_classify_response() {
+ $gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $gapselect->shufflechoices = false;
+ $gapselect->init_first_step(new question_attempt_step());
+
+ $this->assertEqual(array(
+ 1 => new question_classified_response(1, 'quick', 1),
+ 2 => new question_classified_response(2, 'dog', 0),
+ 3 => new question_classified_response(1, 'lazy', 1),
+ ), $gapselect->classify_response(array('p1' => '1', 'p2' => '2', 'p3' => '1')));
+ $this->assertEqual(array(
+ 1 => question_classified_response::no_response(),
+ 2 => new question_classified_response(1, 'fox', 1),
+ 3 => new question_classified_response(2, 'assiduous', 0),
+ ), $gapselect->classify_response(array('p1' => '0', 'p2' => '1', 'p3' => '2')));
+ }
+}
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Unit tests for the select missing words question question definition class.
+ *
+ * @package qtype_gapselect
+ * @copyright 2010 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php');
+require_once($CFG->dirroot . '/question/type/gapselect/simpletest/helper.php');
+
+
+/**
+ * Unit tests for the select missing words question definition class.
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_gapselect_test extends UnitTestCase {
+ /** @var qtype_gapselect instance of the question type class to test. */
+ protected $qtype;
+
+ public function setUp() {
+ $this->qtype = question_bank::get_qtype('gapselect');;
+ }
+
+ public function tearDown() {
+ $this->qtype = null;
+ }
+
+ public function assert_same_xml($expectedxml, $xml) {
+ $this->assertEqual(str_replace("\r\n", "\n", $expectedxml),
+ str_replace("\r\n", "\n", $xml));
+ }
+
+ /**
+ * @return object the data to construct a question like
+ * {@link qtype_gapselect_test_helper::make_a_gapselect_question()}.
+ */
+ protected function get_test_question_data() {
+ global $USER;
+
+ $gapselect = new stdClass;
+ $gapselect->id = 0;
+ $gapselect->category = 0;
+ $gapselect->parent = 0;
+ $gapselect->questiontextformat = FORMAT_HTML;
+ $gapselect->defaultmark = 1;
+ $gapselect->penalty = 0.3333333;
+ $gapselect->length = 1;
+ $gapselect->stamp = make_unique_id_code();
+ $gapselect->version = make_unique_id_code();
+ $gapselect->hidden = 0;
+ $gapselect->timecreated = time();
+ $gapselect->timemodified = time();
+ $gapselect->createdby = $USER->id;
+ $gapselect->modifiedby = $USER->id;
+
+ $gapselect->name = 'Selection from drop down list question';
+ $gapselect->questiontext = 'The [[1]] brown [[2]] jumped over the [[3]] dog.';
+ $gapselect->generalfeedback = 'This sentence uses each letter of the alphabet.';
+ $gapselect->qtype = 'gapselect';
+
+ $gapselect->options->shuffleanswers = true;
+
+ test_question_maker::set_standard_combined_feedback_fields($gapselect->options);
+
+ $gapselect->options->answers = array(
+ (object) array('answer' => 'quick', 'feedback' => '1'),
+ (object) array('answer' => 'fox', 'feedback' => '2'),
+ (object) array('answer' => 'lazy', 'feedback' => '3'),
+ (object) array('answer' => 'assiduous', 'feedback' => '3'),
+ (object) array('answer' => 'dog', 'feedback' => '2'),
+ (object) array('answer' => 'slow', 'feedback' => '1'),
+ );
+
+ return $gapselect;
+ }
+
+ public function test_name() {
+ $this->assertEqual($this->qtype->name(), 'gapselect');
+ }
+
+ public function test_can_analyse_responses() {
+ $this->assertTrue($this->qtype->can_analyse_responses());
+ }
+
+ public function test_initialise_question_instance() {
+ $qdata = $this->get_test_question_data();
+
+ $expected = qtype_gapselect_test_helper::make_a_gapselect_question();
+ $expected->stamp = $qdata->stamp;
+ $expected->version = $qdata->version;
+
+ $q = $this->qtype->make_question($qdata);
+
+ $this->assertEqual($expected, $q);
+ }
+
+ public function test_get_random_guess_score() {
+ $q = $this->get_test_question_data();
+ $this->assertWithinMargin(0.5, $this->qtype->get_random_guess_score($q), 0.0000001);
+ }
+
+ public function test_get_possible_responses() {
+ $q = $this->get_test_question_data();
+
+ $this->assertEqual(array(
+ 1 => array(
+ 1 => new question_possible_response('quick', 1),
+ 2 => new question_possible_response('slow', 0),
+ null => question_possible_response::no_response()),
+ 2 => array(
+ 1 => new question_possible_response('fox', 1),
+ 2 => new question_possible_response('dog', 0),
+ null => question_possible_response::no_response()),
+ 3 => array(
+ 1 => new question_possible_response('lazy', 1),
+ 2 => new question_possible_response('assiduous', 0),
+ null => question_possible_response::no_response()),
+ ), $this->qtype->get_possible_responses($q));
+ }
+
+ public function test_xml_import() {
+ $xml = ' <question type="gapselect">
+ <name>
+ <text>A select missing words question</text>
+ </name>
+ <questiontext format="moodle_auto_format">
+ <text>Put these in order: [[1]], [[2]], [[3]].</text>
+ </questiontext>
+ <generalfeedback>
+ <text>The answer is Alpha, Beta, Gamma.</text>
+ </generalfeedback>
+ <defaultgrade>3</defaultgrade>
+ <penalty>0.3333333</penalty>
+ <hidden>0</hidden>
+ <shuffleanswers>1</shuffleanswers>
+ <correctfeedback>
+ <text><![CDATA[<p>Your answer is correct.</p>]]></text>
+ </correctfeedback>
+ <partiallycorrectfeedback>
+ <text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
+ </partiallycorrectfeedback>
+ <incorrectfeedback>
+ <text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
+ </incorrectfeedback>
+ <shownumcorrect/>
+ <selectoption>
+ <text>Alpha</text>
+ <group>1</group>
+ </selectoption>
+ <selectoption>
+ <text>Beta</text>
+ <group>1</group>
+ </selectoption>
+ <selectoption>
+ <text>Gamma</text>
+ <group>1</group>
+ </selectoption>
+ <hint>
+ <text>Try again.</text>
+ <shownumcorrect />
+ </hint>
+ <hint>
+ <text>These are the first three letters of the Greek alphabet.</text>
+ <shownumcorrect />
+ <clearwrong />
+ </hint>
+ </question>';
+ $xmldata = xmlize($xml);
+
+ $importer = new qformat_xml();
+ $q = $importer->try_importing_using_qtypes(
+ $xmldata['question'], null, null, 'gapselect');
+
+ $expectedq = new stdClass;
+ $expectedq->qtype = 'gapselect';
+ $expectedq->name = 'A select missing words question';
+ $expectedq->questiontext = 'Put these in order: [[1]], [[2]], [[3]].';
+ $expectedq->questiontextformat = FORMAT_MOODLE;
+ $expectedq->generalfeedback = 'The answer is Alpha, Beta, Gamma.';
+ $expectedq->defaultmark = 3;
+ $expectedq->length = 1;
+ $expectedq->penalty = 0.3333333;
+
+ $expectedq->shuffleanswers = 1;
+ $expectedq->correctfeedback = '<p>Your answer is correct.</p>';
+ $expectedq->partiallycorrectfeedback = '<p>Your answer is partially correct.</p>';
+ $expectedq->shownumcorrect = true;
+ $expectedq->incorrectfeedback = '<p>Your answer is incorrect.</p>';
+
+ $expectedq->choices = array(
+ array('answer' => 'Alpha', 'selectgroup' => 1),
+ array('answer' => 'Beta', 'selectgroup' => 1),
+ array('answer' => 'Gamma', 'selectgroup' => 1),
+ );
+
+ $expectedq->hint = array('Try again.', 'These are the first three letters of the Greek alphabet.');
+ $expectedq->hintshownumcorrect = array(true, true);
+ $expectedq->hintclearwrong = array(false, true);
+
+ $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
+ }
+
+ public function test_xml_export() {
+ $qdata = new stdClass;
+ $qdata->id = 123;
+ $qdata->qtype = 'gapselect';
+ $qdata->name = 'A select missing words question';
+ $qdata->questiontext = 'Put these in order: [[1]], [[2]], [[3]].';
+ $qdata->questiontextformat = FORMAT_MOODLE;
+ $qdata->generalfeedback = 'The answer is Alpha, Beta, Gamma.';
+ $qdata->defaultmark = 3;
+ $qdata->length = 1;
+ $qdata->penalty = 0.3333333;
+ $qdata->hidden = 0;
+
+ $qdata->options->shuffleanswers = 1;
+ $qdata->options->correctfeedback = '<p>Your answer is correct.</p>';
+ $qdata->options->partiallycorrectfeedback = '<p>Your answer is partially correct.</p>';
+ $qdata->options->shownumcorrect = true;
+ $qdata->options->incorrectfeedback = '<p>Your answer is incorrect.</p>';
+
+ $qdata->options->answers = array(
+ new question_answer('Alpha', 0, '1'),
+ new question_answer('Beta', 0, '1'),
+ new question_answer('Gamma', 0, '1'),
+ );
+
+ $qdata->hints = array(
+ new question_hint_with_parts('Try again.', true, false),
+ new question_hint_with_parts('These are the first three letters of the Greek alphabet.', true, true),
+ );
+
+ $exporter = new qformat_xml();
+ $xml = $exporter->writequestion($qdata);
+
+ $expectedxml = '<!-- question: 123 -->
+ <question type="gapselect">
+ <name>
+ <text>A select missing words question</text>
+ </name>
+ <questiontext format="moodle_auto_format">
+ <text>Put these in order: [[1]], [[2]], [[3]].</text>
+ </questiontext>
+ <generalfeedback>
+ <text>The answer is Alpha, Beta, Gamma.</text>
+ </generalfeedback>
+ <defaultgrade>3</defaultgrade>
+ <penalty>0.3333333</penalty>
+ <hidden>0</hidden>
+ <shuffleanswers>1</shuffleanswers>
+ <correctfeedback>
+ <text><![CDATA[<p>Your answer is correct.</p>]]></text>
+ </correctfeedback>
+ <partiallycorrectfeedback>
+ <text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
+ </partiallycorrectfeedback>
+ <incorrectfeedback>
+ <text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
+ </incorrectfeedback>
+ <shownumcorrect/>
+ <selectoption>
+ <text>Alpha</text>
+ <group>1</group>
+ </selectoption>
+ <selectoption>
+ <text>Beta</text>
+ <group>1</group>
+ </selectoption>
+ <selectoption>
+ <text>Gamma</text>
+ <group>1</group>
+ </selectoption>
+ <hint>
+ <text>Try again.</text>
+ <shownumcorrect/>
+ </hint>
+ <hint>
+ <text>These are the first three letters of the Greek alphabet.</text>
+ <shownumcorrect/>
+ <clearwrong/>
+ </hint>
+ </question>
+';
+
+ $this->assert_same_xml($expectedxml, $xml);
+ }
+}
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * This file contains tests that walks a question through the interactive
+ * behaviour.
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php');
+require_once($CFG->dirroot . '/question/type/gapselect/simpletest/helper.php');
+
+
+class qtype_gapselect_walkthrough_test extends qbehaviour_walkthrough_test_base {
+
+
+}
--- /dev/null
+.que.gapselect .qtext {
+ line-height:2em;
+ margin-top: 1px;
+ margin-bottom: 0.5em;
+ display: block;
+}
+
+.que.gapselect .answercontainer {
+ line-height: 2em;
+ margin-bottom:1em;
+ display: block;
+}
+
+.que.gapselect .answertext {
+ padding-bottom: 0.5em;
+}
+
+.que.gapselect .group1 {
+ background-color: #FFFFFF;
+}
+.que.gapselect .group2 {
+ background-color: #DCDCDC;
+}
+.que.gapselect .group3 {
+ background-color: #B0C4DE;
+}
+.que.gapselect .group4 {
+ background-color: #D8BFD8;
+}
+.que.gapselect .group5 {
+ background-color: #87CEFA;
+}
+.que.gapselect .group6 {
+ background-color: #DAA520;
+}
+.que.gapselect .group7 {
+ background-color: #FFD700;
+}
+.que.gapselect .group8 {
+ background-color: #F0E68C;
+}
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+
+/**
+ * Version information for the select missing words question type.
+ *
+ * @package qtype
+ * @subpackage gapselect
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+$plugin->version = 2010042800;
+$plugin->requires = 2007101000;