From c6d11f0f6cc37653146d7e18e58d4b454da9abfd Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Wed, 11 Aug 2021 09:44:27 +0200 Subject: [PATCH] MDL-72041 qformat: Remove WebCT question format WebCT was acquired by Blackboard in 2006, so qformat_webct has been completely removed from Moodle core. --- lib/classes/plugin_manager.php | 3 +- lib/db/upgrade.php | 10 + question/format/upgrade.txt | 5 + .../format/webct/classes/privacy/provider.php | 46 - question/format/webct/format.php | 863 ------------------ .../format/webct/lang/en/qformat_webct.php | 35 - .../format/webct/tests/behat/import.feature | 30 - .../tests/behat/importcalculated.feature | 30 - .../fixtures/sample_calculated_webct.txt | 32 - .../webct/tests/fixtures/sample_webct.txt | 117 --- .../format/webct/tests/webctformat_test.php | 366 -------- question/format/webct/version.php | 32 - version.php | 2 +- 13 files changed, 18 insertions(+), 1553 deletions(-) delete mode 100644 question/format/webct/classes/privacy/provider.php delete mode 100644 question/format/webct/format.php delete mode 100644 question/format/webct/lang/en/qformat_webct.php delete mode 100644 question/format/webct/tests/behat/import.feature delete mode 100644 question/format/webct/tests/behat/importcalculated.feature delete mode 100644 question/format/webct/tests/fixtures/sample_calculated_webct.txt delete mode 100644 question/format/webct/tests/fixtures/sample_webct.txt delete mode 100644 question/format/webct/tests/webctformat_test.php delete mode 100644 question/format/webct/version.php diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php index b8da2e822d1..ae35dac88ff 100644 --- a/lib/classes/plugin_manager.php +++ b/lib/classes/plugin_manager.php @@ -1725,6 +1725,7 @@ class core_plugin_manager { 'block' => array('course_overview', 'messages', 'community', 'participants'), 'cachestore' => array('memcache'), 'enrol' => array('authorize'), + 'qformat' => array('webct'), 'quizaccess' => array('safebrowser'), 'report' => array('search'), 'repository' => array('alfresco'), @@ -1958,7 +1959,7 @@ class core_plugin_manager { 'qformat' => array( 'aiken', 'blackboard_six', 'examview', 'gift', - 'missingword', 'multianswer', 'webct', + 'missingword', 'multianswer', 'xhtml', 'xml' ), diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 1a537a56bdb..9a5cced14a6 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2707,5 +2707,15 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2021072800.01); } + if ($oldversion < 2021090200.01) { + // Remove qformat_webct (unless it has manually been added back). + if (!file_exists($CFG->dirroot . '/question/format/webct/format.php')) { + unset_all_config_for_plugin('qformat_webct'); + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2021090200.01); + } + return true; } diff --git a/question/format/upgrade.txt b/question/format/upgrade.txt index a92a7e8a3e0..5f61493328e 100644 --- a/question/format/upgrade.txt +++ b/question/format/upgrade.txt @@ -1,5 +1,10 @@ This files describes API changes for question import/export format plugins. +=== 4.0 === + +* The WebCT question format has been completely removed (WebCT was acquired by Blackboard in 2006). + + === 3.6 === * Saving question category descriptions (info) is now supported in Moodle XML import/export format. diff --git a/question/format/webct/classes/privacy/provider.php b/question/format/webct/classes/privacy/provider.php deleted file mode 100644 index 3d6446eeb09..00000000000 --- a/question/format/webct/classes/privacy/provider.php +++ /dev/null @@ -1,46 +0,0 @@ -. - -/** - * Privacy Subsystem implementation for qformat_webct. - * - * @package qformat_webct - * @copyright 2018 Andrew Nicols - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace qformat_webct\privacy; - -defined('MOODLE_INTERNAL') || die(); - -/** - * Privacy Subsystem for qformat_webct implementing null_provider. - * - * @copyright 2018 Andrew Nicols - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class provider implements \core_privacy\local\metadata\null_provider { - - /** - * Get the language string identifier with the component's language - * file to explain why this plugin stores no data. - * - * @return string - */ - public static function get_reason() : string { - return 'privacy:metadata'; - } -} diff --git a/question/format/webct/format.php b/question/format/webct/format.php deleted file mode 100644 index 9d1c8155535..00000000000 --- a/question/format/webct/format.php +++ /dev/null @@ -1,863 +0,0 @@ -. - -/** - * Web CT question importer. - * - * @package qformat_webct - * @copyright 2004 ASP Consulting http://www.asp-consulting.net - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - - -defined('MOODLE_INTERNAL') || die(); - -/** - * Manipulate HTML editites in a string. Used by WebCT import. - * @param string $string - * @return string - */ -function unhtmlentities($string) { - $search = array ("']*?>.*?'si", // Remove javascript. - "'<[\/\!]*?[^]*?>'si", // Remove HTML tags. - "'([\r\n])[\s]+'", // Remove spaces. - "'&(quot|#34);'i", // Remove HTML entites. - "'&(amp|#38);'i", - "'&(lt|#60);'i", - "'&(gt|#62);'i", - "'&(nbsp|#160);'i", - "'&(iexcl|#161);'i", - "'&(cent|#162);'i", - "'&(pound|#163);'i", - "'&(copy|#169);'i", - "'&#(\d+);'e"); // Evaluate like PHP. - $replace = array ("", - "", - "\\1", - "\"", - "&", - "<", - "?>", - " ", - chr(161), - chr(162), - chr(163), - chr(169), - "chr(\\1)"); - return preg_replace ($search, $replace, $string); -} - -/** - * Helper function for WebCT import. - * @param unknown_type $formula - */ -function qformat_webct_convert_formula($formula) { - - // Remove empty space, as it would cause problems otherwise. - $formula = str_replace(' ', '', $formula); - - // Remove paranthesis after e,E and *10**. - while (preg_match('~[0-9.](e|E|\\*10\\*\\*)\\([+-]?[0-9]+\\)~', $formula, $regs)) { - $formula = str_replace( - $regs[0], preg_replace('/[)(]/', '', $regs[0]), $formula); - } - - // Replace *10** with e where possible. - while (preg_match('~(^[+-]?|[^eE][+-]|[^0-9eE+-])[0-9.]+\\*10\\*\\*[+-]?[0-9]+([^0-9.eE]|$)~', - $formula, $regs)) { - $formula = str_replace( - $regs[0], str_replace('*10**', 'e', $regs[0]), $formula); - } - - // Replace other 10** with 1e where possible. - while (preg_match('~(^|[^0-9.eE])10\\*\\*[+-]?[0-9]+([^0-9.eE]|$)~', $formula, $regs)) { - $formula = str_replace( - $regs[0], str_replace('10**', '1e', $regs[0]), $formula); - } - - // Replace all other base**exp with the PHP equivalent function pow(base,exp) - // (Pretty tricky to exchange an operator with a function). - while (2 == count($splits = explode('**', $formula, 2))) { - - // Find $base. - if (preg_match('~^(.*[^0-9.eE])?(([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][+-]?[0-9]+)?|\\{[^}]*\\})$~', - $splits[0], $regs)) { - // The simple cases. - $base = $regs[2]; - $splits[0] = $regs[1]; - - } else if (preg_match('~\\)$~', $splits[0])) { - // Find the start of this parenthesis. - $deep = 1; - for ($i = 1; $deep; ++$i) { - if (!preg_match('~^(.*[^[:alnum:]_])?([[:alnum:]_]*([)(])([^)(]*[)(]){'.$i.'})$~', - $splits[0], $regs)) { - print_error('parenthesisinproperstart', 'question', '', $splits[0]); - } - if ('(' == $regs[3]) { - --$deep; - } else if (')' == $regs[3]) { - ++$deep; - } else { - print_error('impossiblechar', 'question', '', $regs[3]); - } - } - $base = $regs[2]; - $splits[0] = $regs[1]; - - } else { - print_error('badbase', 'question', '', $splits[0]); - } - - // Find $exp (similar to above but a little easier). - if (preg_match('~^([+-]?(\\{[^}]\\}|([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][+-]?[0-9]+)?))(.*)~', - $splits[1], $regs)) { - // The simple case. - $exp = $regs[1]; - $splits[1] = $regs[6]; - - } else if (preg_match('~^[+-]?[[:alnum:]_]*\\(~', $splits[1])) { - // Find the end of the parenthesis. - $deep = 1; - for ($i = 1; $deep; ++$i) { - if (!preg_match('~^([+-]?[[:alnum:]_]*([)(][^)(]*){'.$i.'}([)(]))(.*)~', - $splits[1], $regs)) { - print_error('parenthesisinproperclose', 'question', '', $splits[1]); - } - if (')' == $regs[3]) { - --$deep; - } else if ('(' == $regs[3]) { - ++$deep; - } else { - print_error('impossiblechar', 'question'); - } - } - $exp = $regs[1]; - $splits[1] = $regs[4]; - } - - // Replace it! - $formula = "{$splits[0]}pow({$base},{$exp}){$splits[1]}"; - } - - // Nothing more is known to need to be converted. - - return $formula; -} - - -/** - * Web CT question importer. - * - * @copyright 2004 ASP Consulting http://www.asp-consulting.net - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class qformat_webct extends qformat_default { - /** @var string path to the temporary directory. */ - public $tempdir = ''; - - /** - * This plugin provide import - * @return bool true - */ - public function provide_import() { - return true; - } - - public function can_import_file($file) { - $mimetypes = array( - mimeinfo('type', '.txt'), - mimeinfo('type', '.zip') - ); - return in_array($file->get_mimetype(), $mimetypes); - } - - public function mime_type() { - return mimeinfo('type', '.zip'); - } - - /** - * Store an image file in a draft filearea - * @param array $text, if itemid element don't exists it will be created - * @param string tempdir path to root of image tree - * @param string filepathinsidetempdir path to image in the tree - * @param string filename image's name - * @return string new name of the image as it was stored - */ - protected function store_file_for_text_field(&$text, $tempdir, $filepathinsidetempdir, $filename) { - global $USER; - $fs = get_file_storage(); - if (empty($text['itemid'])) { - $text['itemid'] = file_get_unused_draft_itemid(); - } - // As question file areas don't support subdirs, - // convert path to filename. - // So that images with same name can be imported. - $newfilename = clean_param(str_replace('/', '__', $filepathinsidetempdir . '__' . $filename), PARAM_FILE); - $filerecord = array( - 'contextid' => context_user::instance($USER->id)->id, - 'component' => 'user', - 'filearea' => 'draft', - 'itemid' => $text['itemid'], - 'filepath' => '/', - 'filename' => $newfilename, - ); - $fs->create_file_from_pathname($filerecord, $tempdir . '/' . $filepathinsidetempdir . '/' . $filename); - return $newfilename; - } - - /** - * Given an HTML text with references to images files, - * store all images in a draft filearea, - * and return an array with all urls in text recoded, - * format set to FORMAT_HTML, and itemid set to filearea itemid - * @param string text text to parse and recode - * @return array with keys text, format, itemid. - */ - public function text_field($text) { - $data = array(); - // Step one, find all file refs then add to array. - preg_match_all('|]+src="([^"]*)"|i', $text, $out); // Find all src refs. - - $filepaths = array(); - foreach ($out[1] as $path) { - $fullpath = $this->tempdir . '/' . $path; - if (is_readable($fullpath) && !in_array($path, $filepaths)) { - $dirpath = dirname($path); - $filename = basename($path); - $newfilename = $this->store_file_for_text_field($data, $this->tempdir, $dirpath, $filename); - $text = preg_replace("|{$path}|", "@@PLUGINFILE@@/" . $newfilename, $text); - $filepaths[] = $path; - } - - } - $data['text'] = $text; - $data['format'] = FORMAT_HTML; - return $data; - } - - /** - * Does any post-processing that may be desired - * Clean the temporary directory if a zip file was imported - * @return bool success - */ - public function importpostprocess() { - if (!empty($this->tempdir)) { - fulldelete($this->tempdir); - } - return true; - } - - /** - * Return content of all files containing questions, - * as an array one element for each file found, - * For each file, the corresponding element is an array of lines. - * @param string filename name of file - * @return mixed contents array or false on failure - */ - public function readdata($filename) { - - // Find if we are importing a .txt file. - if (strtolower(pathinfo($filename, PATHINFO_EXTENSION)) == 'txt') { - if (!is_readable($filename)) { - $this->error(get_string('filenotreadable', 'error')); - return false; - } - return file($filename); - } - // We are importing a zip file. - // Create name for temporary directory. - $this->tempdir = make_request_directory(); - if (is_readable($filename)) { - if (!copy($filename, $this->tempdir . '/webct.zip')) { - $this->error(get_string('cannotcopybackup', 'question')); - fulldelete($this->tempdir); - return false; - } - $packer = get_file_packer('application/zip'); - if ($packer->extract_to_pathname($this->tempdir . '/webct.zip', $this->tempdir, null, null, true)) { - $dir = $this->tempdir; - if ((($handle = opendir($dir))) == false) { - // The directory could not be opened. - fulldelete($this->tempdir); - return false; - } - // Create arrays to store files and directories. - $dirfiles = array(); - $dirsubdirs = array(); - $slash = '/'; - - // Loop through all directory entries, and construct two temporary arrays containing files and sub directories. - while (false !== ($entry = readdir($handle))) { - if (is_dir($dir. $slash .$entry) && $entry != '..' && $entry != '.') { - $dirsubdirs[] = $dir. $slash .$entry; - } else if ($entry != '..' && $entry != '.') { - $dirfiles[] = $dir. $slash .$entry; - } - } - if ((($handle = opendir($dirsubdirs[0]))) == false) { - // The directory could not be opened. - fulldelete($this->tempdir); - return false; - } - while (false !== ($entry = readdir($handle))) { - if (is_dir($dirsubdirs[0]. $slash .$entry) && $entry != '..' && $entry != '.') { - $dirsubdirs[] = $dirsubdirs[0]. $slash .$entry; - } else if ($entry != '..' && $entry != '.') { - $dirfiles[] = $dirsubdirs[0]. $slash .$entry; - } - } - return file($dirfiles[1]); - } else { - $this->error(get_string('cannotunzip', 'question')); - fulldelete($this->tempdir); - } - } else { - $this->error(get_string('cannotreaduploadfile', 'error')); - fulldelete($this->tempdir); - } - return false; - } - - public function readquestions ($lines) { - $webctnumberregex = - '[+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)((e|E|\\*10\\*\\*)([+-]?[0-9]+|\\([+-]?[0-9]+\\)))?'; - - $questions = array(); - $warnings = array(); - $webctoptions = array(); - - $ignorerestofquestion = false; - - $nlinecounter = 0; - $nquestionstartline = 0; - $bishtmltext = false; - $lines[] = ":EOF:"; // For an easiest processing of the last line. - // We don't call defaultquestion() here, it will be called later. - - foreach ($lines as $line) { - $nlinecounter++; - $line = core_text::convert($line, 'windows-1252', 'utf-8'); - // Processing multiples lines strings. - - if (isset($questiontext) and is_string($questiontext)) { - if (preg_match("~^:~", $line)) { - $questiontext = $this->text_field(trim($questiontext)); - $question->questiontext = $questiontext['text']; - $question->questiontextformat = $questiontext['format']; - if (isset($questiontext['itemid'])) { - $question->questiontextitemid = $questiontext['itemid']; - } - unset($questiontext); - } else { - $questiontext .= str_replace('\:', ':', $line); - continue; - } - } - - if (isset($answertext) and is_string($answertext)) { - if (preg_match("~^:~", $line)) { - $answertext = trim($answertext); - if ($question->qtype == 'multichoice' || $question->qtype == 'match' ) { - $question->answer[$currentchoice] = $this->text_field($answertext); - $question->subanswers[$currentchoice] = $question->answer[$currentchoice]; - - } else { - $question->answer[$currentchoice] = $answertext; - $question->subanswers[$currentchoice] = $answertext; - } - unset($answertext); - } else { - $answertext .= str_replace('\:', ':', $line); - continue; - } - } - - if (isset($responsetext) and is_string($responsetext)) { - if (preg_match("~^:~", $line)) { - $question->subquestions[$currentchoice] = trim($responsetext); - unset($responsetext); - } else { - $responsetext .= str_replace('\:', ':', $line); - continue; - } - } - - if (isset($feedbacktext) and is_string($feedbacktext)) { - if (preg_match("~^:~", $line)) { - $question->feedback[$currentchoice] = $this->text_field(trim($feedbacktext)); - unset($feedbacktext); - } else { - $feedbacktext .= str_replace('\:', ':', $line); - continue; - } - } - - if (isset($generalfeedbacktext) and is_string($generalfeedbacktext)) { - if (preg_match("~^:~", $line)) { - $question->tempgeneralfeedback = trim($generalfeedbacktext); - unset($generalfeedbacktext); - } else { - $generalfeedbacktext .= str_replace('\:', ':', $line); - continue; - } - } - - if (isset($graderinfo) and is_string($graderinfo)) { - if (preg_match("~^:~", $line)) { - $question->graderinfo['text'] = trim($graderinfo); - $question->graderinfo['format'] = FORMAT_HTML; - unset($graderinfo); - } else { - $graderinfo .= str_replace('\:', ':', $line); - continue; - } - } - - $line = trim($line); - - if (preg_match("~^:(TYPE|EOF):~i", $line)) { - // New Question or End of File. - if (isset($question)) { // If previous question exists, complete, check and save it. - - // Setup default value of missing fields. - if (!isset($question->name)) { - $question->name = $this->create_default_question_name( - $question->questiontext, get_string('questionname', 'question')); - } - if (!isset($question->defaultmark)) { - $question->defaultmark = 1; - } - if (!isset($question->image)) { - $question->image = ''; - } - - // Perform sanity checks. - $questionok = true; - if (strlen($question->questiontext) == 0) { - $warnings[] = get_string('missingquestion', 'qformat_webct', $nquestionstartline); - $questionok = false; - } - if (count($question->answer) < 1) { // A question must have at least 1 answer. - $this->error(get_string('missinganswer', 'qformat_webct', $nquestionstartline), '', $question->name); - $questionok = false; - } else { - // Create empty feedback array. - foreach ($question->answer as $key => $dataanswer) { - if (!isset($question->feedback[$key])) { - $question->feedback[$key]['text'] = ''; - $question->feedback[$key]['format'] = FORMAT_HTML; - } - } - // This tempgeneralfeedback allows the code to work with versions from 1.6 to 1.9. - // When question->generalfeedback is undefined, the webct feedback is added to each answer feedback. - if (isset($question->tempgeneralfeedback)) { - if (isset($question->generalfeedback)) { - $generalfeedback = $this->text_field($question->tempgeneralfeedback); - $question->generalfeedback = $generalfeedback['text']; - $question->generalfeedbackformat = $generalfeedback['format']; - if (isset($generalfeedback['itemid'])) { - $question->genralfeedbackitemid = $generalfeedback['itemid']; - } - } else { - foreach ($question->answer as $key => $dataanswer) { - if ($question->tempgeneralfeedback != '') { - $question->feedback[$key]['text'] = $question->tempgeneralfeedback - .'
'.$question->feedback[$key]['text']; - } - } - } - unset($question->tempgeneralfeedback); - } - $maxfraction = -1; - $totalfraction = 0; - foreach ($question->fraction as $fraction) { - if ($fraction > 0) { - $totalfraction += $fraction; - } - if ($fraction > $maxfraction) { - $maxfraction = $fraction; - } - } - switch ($question->qtype) { - case 'shortanswer': - if ($maxfraction != 1) { - $maxfraction = $maxfraction * 100; - $this->error(get_string('wronggrade', 'qformat_webct', $nlinecounter) - .' '.get_string('fractionsnomax', 'question', $maxfraction), '', $question->name);; - $questionok = false; - } - break; - - case 'multichoice': - $question = $this->add_blank_combined_feedback($question); - - if ($question->single) { - if ($maxfraction != 1) { - $maxfraction = $maxfraction * 100; - $this->error(get_string('wronggrade', 'qformat_webct', $nlinecounter) - .' '.get_string('fractionsnomax', 'question', $maxfraction), '', $question->name); - $questionok = false; - } - } else { - $totalfraction = round($totalfraction, 2); - if ($totalfraction != 1) { - $totalfraction = $totalfraction * 100; - $this->error(get_string('wronggrade', 'qformat_webct', $nlinecounter) - .' '.get_string('fractionsaddwrong', 'qtype_multichoice', $totalfraction), - '', $question->name); - $questionok = false; - } - } - break; - - case 'calculated': - foreach ($question->answer as $answer) { - if ($formulaerror = qtype_calculated_find_formula_errors($answer)) { - $warnings[] = "'{$question->name}': ". $formulaerror; - $questionok = false; - } - } - foreach ($question->dataset as $dataset) { - $dataset->itemcount = count($dataset->datasetitem); - } - $question->import_process = true; - break; - case 'match': - // MDL-10680: - // Switch subquestions and subanswers. - $question = $this->add_blank_combined_feedback($question); - foreach ($question->subquestions as $id => $subquestion) { - $temp = $question->subquestions[$id]; - $question->subquestions[$id] = $question->subanswers[$id]; - $question->subanswers[$id] = $temp; - } - if (count($question->answer) < 3) { - // Add a dummy missing question. - $question->name = 'Dummy question added '.$question->name; - $question->answer[] = 'dummy'; - $question->subanswers[] = 'dummy'; - $question->subquestions[] = 'dummy'; - $question->fraction[] = '0.0'; - $question->feedback[] = ''; - } - break; - default: - // No problemo. - } - } - - if ($questionok) { - $questions[] = $question; // Store it. - unset($question); // And prepare a new one. - $question = $this->defaultquestion(); - } - } - $nquestionstartline = $nlinecounter; - } - - // Processing Question Header. - - if (preg_match("~^:TYPE:MC:1(.*)~i", $line, $webctoptions)) { - // Multiple Choice Question with only one good answer. - $question = $this->defaultquestion(); - $question->feedback = array(); - $question->qtype = 'multichoice'; - $question->single = 1; // Only one answer is allowed. - $ignorerestofquestion = false; - continue; - } - - if (preg_match("~^:TYPE:MC:N(.*)~i", $line, $webctoptions)) { - // Multiple Choice Question with several good answers. - $question = $this->defaultquestion(); - $question->feedback = array(); - $question->qtype = 'multichoice'; - $question->single = 0; // Many answers allowed. - $ignorerestofquestion = false; - continue; - } - - if (preg_match("~^:TYPE:S~i", $line)) { - // Short Answer Question. - $question = $this->defaultquestion(); - $question->feedback = array(); - $question->qtype = 'shortanswer'; - $question->usecase = 0; // Ignore case. - $ignorerestofquestion = false; - continue; - } - - if (preg_match("~^:TYPE:C~i", $line)) { - // Calculated Question. - $question = $this->defaultquestion(); - $question->qtype = 'calculated'; - $question->answer = array(); // No problem as they go as :FORMULA: from webct. - $question->units = array(); - $question->dataset = array(); - $question->fraction = array('1.0'); - $question->feedback = array(); - - $currentchoice = -1; - $ignorerestofquestion = false; - continue; - } - - if (preg_match("~^:TYPE:M~i", $line)) { - // Match Question. - $question = $this->defaultquestion(); - $question->qtype = 'match'; - $question->feedback = array(); - $ignorerestofquestion = false; // Match question processing is not debugged. - continue; - } - - if (preg_match("~^:TYPE:P~i", $line)) { - // Paragraph Question. - $question = $this->defaultquestion(); - $question->qtype = 'essay'; - $question->responseformat = 'editor'; - $question->responserequired = 1; - $question->responsefieldlines = 15; - $question->attachments = 0; - $question->attachmentsrequired = 0; - $question->graderinfo = array( - 'text' => '', - 'format' => FORMAT_HTML, - ); - $question->feedback = array(); - $question->generalfeedback = ''; - $question->generalfeedbackformat = FORMAT_HTML; - $question->generalfeedbackfiles = array(); - $question->responsetemplate = $this->text_field(''); - $question->questiontextformat = FORMAT_HTML; - $ignorerestofquestion = false; - // To make us pass the end-of-question sanity checks. - $question->answer = array('dummy'); - $question->fraction = array('1.0'); - continue; - } - - if (preg_match("~^:TYPE:~i", $line)) { - // Unknow question type. - $warnings[] = get_string('unknowntype', 'qformat_webct', $nlinecounter); - unset($question); - $ignorerestofquestion = true; // Question Type not handled by Moodle. - continue; - } - - if ($ignorerestofquestion) { - continue; - } - - if (preg_match("~^:TITLE:(.*)~i", $line, $webctoptions)) { - $name = trim($webctoptions[1]); - $question->name = $this->clean_question_name($name); - continue; - } - - if (preg_match("~^:IMAGE:(.*)~i", $line, $webctoptions)) { - $filename = trim($webctoptions[1]); - if (preg_match("~^http://~i", $filename)) { - $question->image = $filename; - } - continue; - } - - // Need to put the parsing of calculated items here to avoid ambitiuosness: - // if question isn't defined yet there is nothing to do here (avoid notices). - if (!isset($question)) { - continue; - } - if (isset($question->qtype ) && 'calculated' == $question->qtype && preg_match( - "~^:([[:lower:]].*|::.*)-(MIN|MAX|DEC|VAL([0-9]+))::?:?({$webctnumberregex})~", $line, $webctoptions)) { - $datasetname = preg_replace('/^::/', '', $webctoptions[1]); - $datasetvalue = qformat_webct_convert_formula($webctoptions[4]); - switch ($webctoptions[2]) { - case 'MIN': - $question->dataset[$datasetname]->min = $datasetvalue; - break; - case 'MAX': - $question->dataset[$datasetname]->max = $datasetvalue; - break; - case 'DEC': - $datasetvalue = floor($datasetvalue); // Int only! - $question->dataset[$datasetname]->length = max(0, $datasetvalue); - break; - default: - // The VAL case. - $question->dataset[$datasetname]->datasetitem[$webctoptions[3]] = new stdClass(); - $question->dataset[$datasetname]->datasetitem[$webctoptions[3]]->itemnumber = $webctoptions[3]; - $question->dataset[$datasetname]->datasetitem[$webctoptions[3]]->value = $datasetvalue; - break; - } - continue; - } - - $bishtmltext = preg_match("~:H$~i", $line); // True if next lines are coded in HTML. - if (preg_match("~^:QUESTION~i", $line)) { - $questiontext = ''; // Start gathering next lines. - continue; - } - - if (preg_match("~^:ANSWER([0-9]+):([^:]+):([0-9\.\-]+):(.*)~i", $line, $webctoptions)) { // Shortanswer. - $currentchoice = $webctoptions[1]; - $answertext = $webctoptions[2]; // Start gathering next lines. - $question->fraction[$currentchoice] = ($webctoptions[3]/100); - continue; - } - - if (preg_match("~^:ANSWER([0-9]+):([0-9\.\-]+)~i", $line, $webctoptions)) { - $answertext = ''; // Start gathering next lines. - $currentchoice = $webctoptions[1]; - $question->fraction[$currentchoice] = ($webctoptions[2]/100); - continue; - } - - if (preg_match('~^:ANSWER:~i', $line)) { // Essay. - $graderinfo = ''; // Start gathering next lines. - continue; - } - - if (preg_match('~^:FORMULA:(.*)~i', $line, $webctoptions)) { - // Answer for a calculated question. - ++$currentchoice; - $question->answer[$currentchoice] = - qformat_webct_convert_formula($webctoptions[1]); - - // Default settings. - $question->fraction[$currentchoice] = 1.0; - $question->tolerance[$currentchoice] = 0.0; - $question->tolerancetype[$currentchoice] = 2; // Nominal (units in webct). - $question->feedback[$currentchoice]['text'] = ''; - $question->feedback[$currentchoice]['format'] = FORMAT_HTML; - $question->correctanswerlength[$currentchoice] = 4; - - $datasetnames = - question_bank::get_qtype('calculated')->find_dataset_names($webctoptions[1]); - foreach ($datasetnames as $datasetname) { - $question->dataset[$datasetname] = new stdClass(); - $question->dataset[$datasetname]->datasetitem = array(); - $question->dataset[$datasetname]->name = $datasetname; - $question->dataset[$datasetname]->distribution = 'uniform'; - $question->dataset[$datasetname]->status = 'private'; - } - continue; - } - - if (preg_match("~^:L([0-9]+)~i", $line, $webctoptions)) { - $answertext = ''; // Start gathering next lines. - $currentchoice = $webctoptions[1]; - $question->fraction[$currentchoice] = 1; - continue; - } - - if (preg_match("~^:R([0-9]+)~i", $line, $webctoptions)) { - $responsetext = ''; // Start gathering next lines. - $currentchoice = $webctoptions[1]; - continue; - } - - if (preg_match("~^:REASON([0-9]+):?~i", $line, $webctoptions)) { - $feedbacktext = ''; // Start gathering next lines. - $currentchoice = $webctoptions[1]; - continue; - } - if (preg_match("~^:FEEDBACK([0-9]+):?~i", $line, $webctoptions)) { - $generalfeedbacktext = ''; // Start gathering next lines. - $currentchoice = $webctoptions[1]; - continue; - } - if (preg_match('~^:FEEDBACK:(.*)~i', $line, $webctoptions)) { - $generalfeedbacktext = ''; // Start gathering next lines. - continue; - } - if (preg_match('~^:LAYOUT:(.*)~i', $line, $webctoptions)) { - // Ignore since layout in question_multichoice is no more used in Moodle. - // $webctoptions[1] contains either vertical or horizontal. - continue; - } - - if (isset($question->qtype ) && 'calculated' == $question->qtype - && preg_match('~^:ANS-DEC:([1-9][0-9]*)~i', $line, $webctoptions)) { - // We can but hope that this always appear before the ANSTYPE property. - $question->correctanswerlength[$currentchoice] = $webctoptions[1]; - continue; - } - - if (isset($question->qtype )&& 'calculated' == $question->qtype - && preg_match("~^:TOL:({$webctnumberregex})~i", $line, $webctoptions)) { - // We can but hope that this always appear before the TOL property. - $question->tolerance[$currentchoice] = - qformat_webct_convert_formula($webctoptions[1]); - continue; - } - - if (isset($question->qtype )&& 'calculated' == $question->qtype && preg_match('~^:TOLTYPE:percent~i', $line)) { - // Percentage case is handled as relative in Moodle. - $question->tolerance[$currentchoice] /= 100; - $question->tolerancetype[$currentchoice] = 1; // Relative. - continue; - } - - if (preg_match('~^:UNITS:(.+)~i', $line, $webctoptions) - and $webctunits = trim($webctoptions[1])) { - // This is a guess - I really do not know how different webct units are separated... - $webctunits = explode(':', $webctunits); - $unitrec->multiplier = 1.0; // Webct does not seem to support this. - foreach ($webctunits as $webctunit) { - $unitrec->unit = trim($webctunit); - $question->units[] = $unitrec; - } - continue; - } - - if (!empty($question->units) && preg_match('~^:UNITREQ:(.*)~i', $line, $webctoptions) - && !$webctoptions[1]) { - // There are units but units are not required so add the no unit alternative. - // We can but hope that the UNITS property always appear before this property. - $unitrec->unit = ''; - $unitrec->multiplier = 1.0; - $question->units[] = $unitrec; - continue; - } - - if (!empty($question->units) && preg_match('~^:UNITCASE:~i', $line)) { - // This could be important but I was not able to figure out how - // it works so I ignore it for now. - continue; - } - - if (isset($question->qtype )&& 'calculated' == $question->qtype && preg_match('~^:ANSTYPE:dec~i', $line)) { - $question->correctanswerformat[$currentchoice] = '1'; - continue; - } - if (isset($question->qtype )&& 'calculated' == $question->qtype && preg_match('~^:ANSTYPE:sig~i', $line)) { - $question->correctanswerformat[$currentchoice] = '2'; - continue; - } - } - - if (count($warnings) > 0) { - echo '

'.get_string('warningsdetected', 'qformat_webct', count($warnings)).'

    '; - foreach ($warnings as $warning) { - echo "
  • {$warning}
  • "; - } - echo '
'; - } - return $questions; - } -} diff --git a/question/format/webct/lang/en/qformat_webct.php b/question/format/webct/lang/en/qformat_webct.php deleted file mode 100644 index e09303c1f45..00000000000 --- a/question/format/webct/lang/en/qformat_webct.php +++ /dev/null @@ -1,35 +0,0 @@ -. - -/** - * Strings for component 'qformat_webct', language 'en', branch 'MOODLE_20_STABLE' - * - * @package qformat_webct - * @copyright 2010 Helen Foster - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -$string['errorsdetected'] = '{$a} error(s) detected'; -$string['missinganswer'] = 'Too few :ANSWER, :Lx, :Rx statements for question line {$a}. You must define at least 2 possible answers.'; -$string['missingquestion'] = 'Missing question label after line {$a}'; -$string['pluginname'] = 'WebCT format'; -$string['pluginname_help'] = 'WebCT format enables multiple-choice and short answer questions saved in WebCT\'s text-based format to be imported.'; -$string['pluginname_link'] = 'qformat/webct'; -$string['privacy:metadata'] = 'The WebCT question format plugin does not store any personal data.'; -$string['questionnametoolong'] = 'Question name too long at line {$a} (255 char. max). It has been truncated.'; -$string['unknowntype'] = 'Unknown question type after line {$a}'; -$string['warningsdetected'] = '{$a} warning(s) detected'; -$string['wronggrade'] = 'Wrong grade (after line {$a}) :'; diff --git a/question/format/webct/tests/behat/import.feature b/question/format/webct/tests/behat/import.feature deleted file mode 100644 index 1dc5157856b..00000000000 --- a/question/format/webct/tests/behat/import.feature +++ /dev/null @@ -1,30 +0,0 @@ -@qformat @qformat_webct -Feature: Test importing questions from WebCT format. - In order to reuse questions from am obsolete commercial LMS - As an teacher - I need to be able to import them in WebCT format. - - Background: - Given the following "courses" exist: - | fullname | shortname | format | - | Course 1 | C1 | topics | - And the following "users" exist: - | username | firstname | - | teacher | Teacher | - And the following "course enrolments" exist: - | user | course | role | - | teacher | C1 | editingteacher | - And I log in as "teacher" - And I am on "Course 1" course homepage - - @javascript @_file_upload - Scenario: import some WebCT questions - When I navigate to "Question bank > Import" in current page administration - And I set the field "id_format_webct" to "1" - And I upload "question/format/webct/tests/fixtures/sample_webct.txt" file to "Import" filemanager - And I press "id_submitbutton" - Then I should see "Parsing questions from import file." - And I should see "Importing 6 questions from file" - And I should see "What's between orange and green in the spectrum?" - When I press "Continue" - Then I should see "USER-3" diff --git a/question/format/webct/tests/behat/importcalculated.feature b/question/format/webct/tests/behat/importcalculated.feature deleted file mode 100644 index 33e1b9d4ac0..00000000000 --- a/question/format/webct/tests/behat/importcalculated.feature +++ /dev/null @@ -1,30 +0,0 @@ -@qformat @qformat_webct -Feature: Test importing calculated question from WebCT format. - In order to reuse calculated questions from a commercial LMS - As a teacher - I need to be able to import them in WebCT format. - - Background: - Given the following "courses" exist: - | fullname | shortname | format | - | Course 1 | C1 | topics | - And the following "users" exist: - | username | firstname | - | teacher | Teacher | - And the following "course enrolments" exist: - | user | course | role | - | teacher | C1 | editingteacher | - And I log in as "teacher" - And I am on "Course 1" course homepage - - @javascript @_file_upload - Scenario: import a WebCT calculated question - When I navigate to "Question bank > Import" in current page administration - And I set the field "id_format_webct" to "1" - And I upload "question/format/webct/tests/fixtures/sample_calculated_webct.txt" file to "Import" filemanager - And I press "id_submitbutton" - Then I should see "Parsing questions from import file." - And I should see "Importing 1 questions from file" - And I should see "Find the area in m2 of a square with sides of length {l} m." - When I press "Continue" - Then I should see "calculated: Square area" diff --git a/question/format/webct/tests/fixtures/sample_calculated_webct.txt b/question/format/webct/tests/fixtures/sample_calculated_webct.txt deleted file mode 100644 index e408853de9d..00000000000 --- a/question/format/webct/tests/fixtures/sample_calculated_webct.txt +++ /dev/null @@ -1,32 +0,0 @@ -# Start of calculated question: Square area -:TYPE:C -:TITLE:calculated: Square area -:QUESTION:H -Find the area in m2 of a square with sides of length {l} m. -:IMAGE: -:FORMULA:{l}*{l} -:l-MIN:10 -:l-MAX:100 -:l-DEC:0 -:VALUES:10 -:l-VAL1:51 -:l-VAL2:32 -:l-VAL3:57 -:l-VAL4:94 -:l-VAL5:38 -:l-VAL6:54 -:l-VAL7:87 -:l-VAL8:90 -:l-VAL9:78 -:l-VAL10:39 -:ANS-DEC:1 -:TOL: -:TOLTYPE:percent -:UNITREQ:0 -:UNITSPACE:0 -:UNITCASE:0 -:UNITVAL:0 -:ANSTYPE:dec -:FEEDBACK:H -:CAT:Import from WebCT -# End of calculated question: Square area diff --git a/question/format/webct/tests/fixtures/sample_webct.txt b/question/format/webct/tests/fixtures/sample_webct.txt deleted file mode 100644 index e94efe97be4..00000000000 --- a/question/format/webct/tests/fixtures/sample_webct.txt +++ /dev/null @@ -1,117 +0,0 @@ -# Start of question:Question 001 -:TYPE:MC:1:0:A -:TITLE:USER-1 -:QUESTION:H -42 is the Absolute Answer to everything. -:LAYOUT:horizontal -:ANSWERORDER:randomized -:INDICES:letters -:ANSWER1:0:H -True -:REASON1:H -42 is the Ultimate Answer. -:ANSWER2:100:H -False -:REASON2:H -42 is the Ultimate Answer. -:CAT:webct -:ASSESSMENT:Test -# End of question:Question 001 - -# Start of question:Question 002 -:TYPE:MC:1:0:A -:TITLE:USER-2 -:QUESTION:H -What's between orange and green in the spectrum? -:LAYOUT:horizontal -:ANSWERORDER:randomized -:INDICES:letters -:ANSWER1:0:H -red -:REASON1:H -Red is not between orange and green in the spectrum but yellow is. -:ANSWER2:100:H -yellow -:REASON2:H -You gave the right answer. -:ANSWER3:0:H -blue -:REASON3:H -Blue is not between orange and green in the spectrum but yellow is. -:CAT:webct -:ASSESSMENT:Test -# End of question:Question 002 - -# Start of question:Question 003 -:TYPE:MC:N:0:A -:TITLE:USER-3 -:QUESTION:H -What's between orange and green in the spectrum? -:LAYOUT:horizontal -:ANSWERORDER:randomized -:INDICES:letters -:ANSWER1:50:H -yellow -:REASON1:H -True, yellow is between orange and green in the spectrum, -:ANSWER2:0:H -red -:REASON2:H -False, red is not between orange and green in the spectrum, -:ANSWER3:50:H -off-beige -:REASON3:H -True, off-beige is between orange and green in the spectrum, -:ANSWER4:0:H -blue -:REASON4:H -False, red is not between orange and green in the spectrum, -:CAT:webct -:ASSESSMENT:Test -# End of question:Question 003 - -# Start of question:Question 004 -:TYPE:M:short:short:E:0 -:TITLE:Classify the animals. -:QUESTION:H -Classify the animals. -:IMAGE: -:L1 -cat -:R1 -mammal -:L2 -frog -:R2 -amphibian -:L3 -newt -:R3 -amphibian -:CAT:Ch 00 Instr Test Items -:ASSESSMENT:Test -# End of question:Question 004 - -# Start of question:Question 005 -:TYPE:S -:TITLE:USER-5 -:QUESTION:H -Name an amphibian: __________ -:ANSWERS:1 -:ANSWER1:frog:100:0:20:0 -:FEEDBACK1:H -A frog is an amphibian -:CAT:webct -:ASSESSMENT:Test -# End of question:Question 005 - -# Start of question:Question 006 -:TYPE:P -:TITLE:USER-6 -:QUESTION:H:60:5 -How are you? -:ANSWER:H -Blackboard answer for essay questions will be imported as informations for graders. -:CAT:webct -:ASSESSMENT:Test -# End of question:Question 006 diff --git a/question/format/webct/tests/webctformat_test.php b/question/format/webct/tests/webctformat_test.php deleted file mode 100644 index 12783aa6f92..00000000000 --- a/question/format/webct/tests/webctformat_test.php +++ /dev/null @@ -1,366 +0,0 @@ -. - -/** - * Unit tests for Web CT question importer. - * - * @package qformat_webct - * @copyright 2013 Jean-Michel Vedrine - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - - -defined('MOODLE_INTERNAL') || die(); - -global $CFG; -require_once($CFG->libdir . '/questionlib.php'); -require_once($CFG->dirroot . '/question/format.php'); -require_once($CFG->dirroot . '/question/format/webct/format.php'); -require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); - - -/** - * Unit tests for the webct question import format. - * - * @copyright 2013 Jean-Michel Vedrine - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class qformat_webct_test extends question_testcase { - - public function make_test() { - $lines = file(__DIR__ . '/fixtures/sample_webct.txt'); - return $lines; - } - - public function test_import_match() { - - $txt = $this->make_test(); - $importer = new qformat_webct(); - $questions = $importer->readquestions($txt); - $q = $questions[3]; - - $expectedq = new stdClass(); - $expectedq->qtype = 'match'; - $expectedq->name = 'Classify the animals.'; - $expectedq->questiontext = 'Classify the animals.'; - $expectedq->questiontextformat = FORMAT_HTML; - $expectedq->correctfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->partiallycorrectfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->incorrectfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->generalfeedback = ''; - $expectedq->generalfeedbackformat = FORMAT_MOODLE; - $expectedq->defaultmark = 1; - $expectedq->length = 1; - $expectedq->penalty = 0.3333333; - $expectedq->shuffleanswers = get_config('quiz', 'shuffleanswers'); - $expectedq->subquestions = array( - 1 => array('text' => 'cat', 'format' => FORMAT_HTML), - 2 => array('text' => 'frog', 'format' => FORMAT_HTML), - 3 => array('text' => 'newt', 'format' => FORMAT_HTML)); - $expectedq->subanswers = array(1 => 'mammal', 2 => 'amphibian', 3 => 'amphibian'); - - $this->assert(new question_check_specified_fields_expectation($expectedq), $q); - } - - public function test_import_multichoice_single() { - - $txt = $this->make_test(); - - $importer = new qformat_webct(); - $questions = $importer->readquestions($txt); - $q = $questions[1]; - - $expectedq = new stdClass(); - $expectedq->qtype = 'multichoice'; - $expectedq->single = 1; - $expectedq->name = 'USER-2'; - $expectedq->questiontext = 'What\'s between orange and green in the spectrum?'; - $expectedq->questiontextformat = FORMAT_HTML; - $expectedq->correctfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->partiallycorrectfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->incorrectfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->generalfeedback = ''; - $expectedq->generalfeedbackformat = FORMAT_MOODLE; - $expectedq->defaultmark = 1; - $expectedq->length = 1; - $expectedq->penalty = 0.3333333; - $expectedq->shuffleanswers = get_config('quiz', 'shuffleanswers'); - $expectedq->answer = array( - 1 => array( - 'text' => 'red', - 'format' => FORMAT_HTML, - ), - 2 => array( - 'text' => 'yellow', - 'format' => FORMAT_HTML, - ), - 3 => array( - 'text' => 'blue', - 'format' => FORMAT_HTML, - ) - ); - $expectedq->fraction = array( - 1 => 0, - 2 => 1, - 3 => 0, - ); - $expectedq->feedback = array( - 1 => array( - 'text' => 'Red is not between orange and green in the spectrum but yellow is.', - 'format' => FORMAT_HTML, - ), - 2 => array( - 'text' => 'You gave the right answer.', - 'format' => FORMAT_HTML, - ), - 3 => array( - 'text' => 'Blue is not between orange and green in the spectrum but yellow is.', - 'format' => FORMAT_HTML, - ) - ); - - $this->assert(new question_check_specified_fields_expectation($expectedq), $q); - } - - public function test_import_multichoice_multi() { - - $txt = $this->make_test(); - - $importer = new qformat_webct(); - $questions = $importer->readquestions($txt); - $q = $questions[2]; - - $expectedq = new stdClass(); - $expectedq->qtype = 'multichoice'; - $expectedq->single = 0; - $expectedq->name = 'USER-3'; - $expectedq->questiontext = 'What\'s between orange and green in the spectrum?'; - $expectedq->questiontextformat = FORMAT_HTML; - $expectedq->correctfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->partiallycorrectfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->incorrectfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->generalfeedback = ''; - $expectedq->generalfeedbackformat = FORMAT_MOODLE; - $expectedq->defaultmark = 1; - $expectedq->length = 1; - $expectedq->penalty = 0.3333333; - $expectedq->shuffleanswers = get_config('quiz', 'shuffleanswers'); - $expectedq->answer = array( - 1 => array( - 'text' => 'yellow', - 'format' => FORMAT_HTML, - ), - 2 => array( - 'text' => 'red', - 'format' => FORMAT_HTML, - ), - 3 => array( - 'text' => 'off-beige', - 'format' => FORMAT_HTML, - ), - 4 => array( - 'text' => 'blue', - 'format' => FORMAT_HTML, - ) - ); - $expectedq->fraction = array( - 1 => 0.5, - 2 => 0, - 3 => 0.5, - 4 => 0, - ); - $expectedq->feedback = array( - 1 => array( - 'text' => 'True, yellow is between orange and green in the spectrum,', - 'format' => FORMAT_HTML, - ), - 2 => array( - 'text' => 'False, red is not between orange and green in the spectrum,', - 'format' => FORMAT_HTML, - ), - 3 => array( - 'text' => 'True, off-beige is between orange and green in the spectrum,', - 'format' => FORMAT_HTML, - ), - 4 => array( - 'text' => 'False, red is not between orange and green in the spectrum,', - 'format' => FORMAT_HTML, - ) - ); - - $this->assert(new question_check_specified_fields_expectation($expectedq), $q); - } - - public function test_import_truefalse() { - - $txt = $this->make_test(); - - $importer = new qformat_webct(); - $questions = $importer->readquestions($txt); - $q = $questions[0]; - - $expectedq = new stdClass(); - $expectedq->qtype = 'multichoice'; - $expectedq->single = 1; - $expectedq->name = 'USER-1'; - $expectedq->questiontext = '42 is the Absolute Answer to everything.'; - $expectedq->questiontextformat = FORMAT_HTML; - $expectedq->correctfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->partiallycorrectfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->incorrectfeedback = array( - 'text' => '', - 'format' => FORMAT_HTML, - 'files' => array(), - ); - $expectedq->generalfeedback = ''; - $expectedq->generalfeedbackformat = FORMAT_MOODLE; - $expectedq->defaultmark = 1; - $expectedq->length = 1; - $expectedq->shuffleanswers = get_config('quiz', 'shuffleanswers'); - $expectedq->answer = array( - 1 => array( - 'text' => 'True', - 'format' => FORMAT_HTML, - ), - 2 => array( - 'text' => 'False', - 'format' => FORMAT_HTML, - ), - ); - $expectedq->fraction = array( - 1 => 0, - 2 => 1, - ); - $expectedq->feedback = array( - 1 => array( - 'text' => '42 is the Ultimate Answer.', - 'format' => FORMAT_HTML, - ), - 2 => array( - 'text' => '42 is the Ultimate Answer.', - 'format' => FORMAT_HTML, - ), - ); - - $this->assert(new question_check_specified_fields_expectation($expectedq), $q); - } - - public function test_import_fill_in_the_blank() { - - $txt = $this->make_test(); - - $importer = new qformat_webct(); - $questions = $importer->readquestions($txt); - $q = $questions[4]; - - $expectedq = new stdClass(); - $expectedq->qtype = 'shortanswer'; - $expectedq->name = 'USER-5'; - $expectedq->questiontext = 'Name an amphibian: __________'; - $expectedq->questiontextformat = FORMAT_HTML; - $expectedq->generalfeedback = 'A frog is an amphibian'; - $expectedq->generalfeedbackformat = FORMAT_HTML; - $expectedq->defaultmark = 1; - $expectedq->length = 1; - $expectedq->usecase = 0; - $expectedq->answer = array( - 1 => 'frog', - ); - $expectedq->fraction = array( - 1 => 1, - ); - $expectedq->feedback = array( - 1 => array( - 'text' => '', - 'format' => FORMAT_HTML, - ), - ); - - $this->assert(new question_check_specified_fields_expectation($expectedq), $q); - } - - public function test_import_essay() { - - $txt = $this->make_test(); - - $importer = new qformat_webct(); - $questions = $importer->readquestions($txt); - $q = $questions[5]; - - $expectedq = new stdClass(); - $expectedq->qtype = 'essay'; - $expectedq->name = 'USER-6'; - $expectedq->questiontext = 'How are you?'; - $expectedq->questiontextformat = FORMAT_HTML; - $expectedq->generalfeedback = ''; - $expectedq->generalfeedbackformat = FORMAT_HTML; - $expectedq->defaultmark = 1; - $expectedq->length = 1; - $expectedq->responseformat = 'editor'; - $expectedq->responsefieldlines = 15; - $expectedq->attachments = 0; - $expectedq->graderinfo = array( - 'text' => 'Blackboard answer for essay questions will be imported as informations for graders.', - 'format' => FORMAT_HTML, - ); - - $this->assert(new question_check_specified_fields_expectation($expectedq), $q); - } -} diff --git a/question/format/webct/version.php b/question/format/webct/version.php deleted file mode 100644 index ca4efbfd50d..00000000000 --- a/question/format/webct/version.php +++ /dev/null @@ -1,32 +0,0 @@ -. - -/** - * Version information for the Web CT question import format. - * - * @package qformat_webct - * @copyright 2011 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -$plugin->component = 'qformat_webct'; -$plugin->version = 2021052500; - -$plugin->requires = 2021052500; - -$plugin->maturity = MATURITY_STABLE; diff --git a/version.php b/version.php index 498b59374a6..26d0e46c970 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2021090200.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2021090200.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.0dev (Build: 20210902)'; // Human-friendly version name