[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * GIFT format question importer/exporter. 19 * 20 * @package qformat_gift 21 * @copyright 2003 Paul Tsuchido Shew 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 29 /** 30 * The GIFT import filter was designed as an easy to use method 31 * for teachers writing questions as a text file. It supports most 32 * question types and the missing word format. 33 * 34 * Multiple Choice / Missing Word 35 * Who's buried in Grant's tomb?{~Grant ~Jefferson =no one} 36 * Grant is {~buried =entombed ~living} in Grant's tomb. 37 * True-False: 38 * Grant is buried in Grant's tomb.{FALSE} 39 * Short-Answer. 40 * Who's buried in Grant's tomb?{=no one =nobody} 41 * Numerical 42 * When was Ulysses S. Grant born?{#1822:5} 43 * Matching 44 * Match the following countries with their corresponding 45 * capitals.{=Canada->Ottawa =Italy->Rome =Japan->Tokyo} 46 * 47 * Comment lines start with a double backslash (//). 48 * Optional question names are enclosed in double colon(::). 49 * Answer feedback is indicated with hash mark (#). 50 * Percentage answer weights immediately follow the tilde (for 51 * multiple choice) or equal sign (for short answer and numerical), 52 * and are enclosed in percent signs (% %). See docs and examples.txt for more. 53 * 54 * This filter was written through the collaboration of numerous 55 * members of the Moodle community. It was originally based on 56 * the missingword format, which included code from Thomas Robb 57 * and others. Paul Tsuchido Shew wrote this filter in December 2003. 58 * 59 * @copyright 2003 Paul Tsuchido Shew 60 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 61 */ 62 class qformat_gift extends qformat_default { 63 64 public function provide_import() { 65 return true; 66 } 67 68 public function provide_export() { 69 return true; 70 } 71 72 public function export_file_extension() { 73 return '.txt'; 74 } 75 76 protected function answerweightparser(&$answer) { 77 $answer = substr($answer, 1); // Removes initial %. 78 $endposition = strpos($answer, "%"); 79 $answerweight = substr($answer, 0, $endposition); // Gets weight as integer. 80 $answerweight = $answerweight/100; // Converts to percent. 81 $answer = substr($answer, $endposition+1); // Removes comment from answer. 82 return $answerweight; 83 } 84 85 protected function commentparser($answer, $defaultformat) { 86 $bits = explode('#', $answer, 2); 87 $ans = $this->parse_text_with_format(trim($bits[0]), $defaultformat); 88 if (count($bits) > 1) { 89 $feedback = $this->parse_text_with_format(trim($bits[1]), $defaultformat); 90 } else { 91 $feedback = array('text' => '', 'format' => $defaultformat, 'files' => array()); 92 } 93 return array($ans, $feedback); 94 } 95 96 protected function split_truefalse_comment($answer, $defaultformat) { 97 $bits = explode('#', $answer, 3); 98 $ans = $this->parse_text_with_format(trim($bits[0]), $defaultformat); 99 if (count($bits) > 1) { 100 $wrongfeedback = $this->parse_text_with_format(trim($bits[1]), $defaultformat); 101 } else { 102 $wrongfeedback = array('text' => '', 'format' => $defaultformat, 'files' => array()); 103 } 104 if (count($bits) > 2) { 105 $rightfeedback = $this->parse_text_with_format(trim($bits[2]), $defaultformat); 106 } else { 107 $rightfeedback = array('text' => '', 'format' => $defaultformat, 'files' => array()); 108 } 109 return array($ans, $wrongfeedback, $rightfeedback); 110 } 111 112 protected function escapedchar_pre($string) { 113 // Replaces escaped control characters with a placeholder BEFORE processing. 114 115 $escapedcharacters = array("\\:", "\\#", "\\=", "\\{", "\\}", "\\~", "\\n" ); 116 $placeholders = array("&&058;", "&&035;", "&&061;", "&&123;", "&&125;", "&&126;", "&&010"); 117 118 $string = str_replace("\\\\", "&&092;", $string); 119 $string = str_replace($escapedcharacters, $placeholders, $string); 120 $string = str_replace("&&092;", "\\", $string); 121 return $string; 122 } 123 124 protected function escapedchar_post($string) { 125 // Replaces placeholders with corresponding character AFTER processing is done. 126 $placeholders = array("&&058;", "&&035;", "&&061;", "&&123;", "&&125;", "&&126;", "&&010"); 127 $characters = array(":", "#", "=", "{", "}", "~", "\n" ); 128 $string = str_replace($placeholders, $characters, $string); 129 return $string; 130 } 131 132 protected function check_answer_count($min, $answers, $text) { 133 $countanswers = count($answers); 134 if ($countanswers < $min) { 135 $this->error(get_string('importminerror', 'qformat_gift'), $text); 136 return false; 137 } 138 139 return true; 140 } 141 142 protected function parse_text_with_format($text, $defaultformat = FORMAT_MOODLE) { 143 $result = array( 144 'text' => $text, 145 'format' => $defaultformat, 146 'files' => array(), 147 ); 148 if (strpos($text, '[') === 0) { 149 $formatend = strpos($text, ']'); 150 $result['format'] = $this->format_name_to_const(substr($text, 1, $formatend - 1)); 151 if ($result['format'] == -1) { 152 $result['format'] = $defaultformat; 153 } else { 154 $result['text'] = substr($text, $formatend + 1); 155 } 156 } 157 $result['text'] = trim($this->escapedchar_post($result['text'])); 158 return $result; 159 } 160 161 public function readquestion($lines) { 162 // Given an array of lines known to define a question in this format, this function 163 // converts it into a question object suitable for processing and insertion into Moodle. 164 165 $question = $this->defaultquestion(); 166 $comment = null; 167 // Define replaced by simple assignment, stop redefine notices. 168 $giftanswerweightregex = '/^%\-*([0-9]{1,2})\.?([0-9]*)%/'; 169 170 // REMOVED COMMENTED LINES and IMPLODE. 171 foreach ($lines as $key => $line) { 172 $line = trim($line); 173 if (substr($line, 0, 2) == '//') { 174 $lines[$key] = ' '; 175 } 176 } 177 178 $text = trim(implode("\n", $lines)); 179 180 if ($text == '') { 181 return false; 182 } 183 184 // Substitute escaped control characters with placeholders. 185 $text = $this->escapedchar_pre($text); 186 187 // Look for category modifier. 188 if (preg_match('~^\$CATEGORY:~', $text)) { 189 $newcategory = trim(substr($text, 10)); 190 191 // Build fake question to contain category. 192 $question->qtype = 'category'; 193 $question->category = $newcategory; 194 return $question; 195 } 196 197 // Question name parser. 198 if (substr($text, 0, 2) == '::') { 199 $text = substr($text, 2); 200 201 $namefinish = strpos($text, '::'); 202 if ($namefinish === false) { 203 $question->name = false; 204 // Name will be assigned after processing question text below. 205 } else { 206 $questionname = substr($text, 0, $namefinish); 207 $question->name = $this->clean_question_name($this->escapedchar_post($questionname)); 208 $text = trim(substr($text, $namefinish+2)); // Remove name from text. 209 } 210 } else { 211 $question->name = false; 212 } 213 214 // Find the answer section. 215 $answerstart = strpos($text, '{'); 216 $answerfinish = strpos($text, '}'); 217 218 $description = false; 219 if ($answerstart === false && $answerfinish === false) { 220 // No answer means it's a description. 221 $description = true; 222 $answertext = ''; 223 $answerlength = 0; 224 225 } else if ($answerstart === false || $answerfinish === false) { 226 $this->error(get_string('braceerror', 'qformat_gift'), $text); 227 return false; 228 229 } else { 230 $answerlength = $answerfinish - $answerstart; 231 $answertext = trim(substr($text, $answerstart + 1, $answerlength - 1)); 232 } 233 234 // Format the question text, without answer, inserting "_____" as necessary. 235 if ($description) { 236 $questiontext = $text; 237 } else if (substr($text, -1) == "}") { 238 // No blank line if answers follow question, outside of closing punctuation. 239 $questiontext = substr_replace($text, "", $answerstart, $answerlength + 1); 240 } else { 241 // Inserts blank line for missing word format. 242 $questiontext = substr_replace($text, "_____", $answerstart, $answerlength + 1); 243 } 244 245 // Look to see if there is any general feedback. 246 $gfseparator = strrpos($answertext, '####'); 247 if ($gfseparator === false) { 248 $generalfeedback = ''; 249 } else { 250 $generalfeedback = substr($answertext, $gfseparator + 4); 251 $answertext = trim(substr($answertext, 0, $gfseparator)); 252 } 253 254 // Get questiontext format from questiontext. 255 $text = $this->parse_text_with_format($questiontext); 256 $question->questiontextformat = $text['format']; 257 $question->questiontext = $text['text']; 258 259 // Get generalfeedback format from questiontext. 260 $text = $this->parse_text_with_format($generalfeedback, $question->questiontextformat); 261 $question->generalfeedback = $text['text']; 262 $question->generalfeedbackformat = $text['format']; 263 264 // Set question name if not already set. 265 if ($question->name === false) { 266 $question->name = $this->create_default_question_name($question->questiontext, get_string('questionname', 'question')); 267 } 268 269 // Determine question type. 270 $question->qtype = null; 271 272 // Give plugins first try. 273 // Plugins must promise not to intercept standard qtypes 274 // MDL-12346, this could be called from lesson mod which has its own base class =(. 275 if (method_exists($this, 'try_importing_using_qtypes') 276 && ($tryquestion = $this->try_importing_using_qtypes($lines, $question, $answertext))) { 277 return $tryquestion; 278 } 279 280 if ($description) { 281 $question->qtype = 'description'; 282 283 } else if ($answertext == '') { 284 $question->qtype = 'essay'; 285 286 } else if ($answertext{0} == '#') { 287 $question->qtype = 'numerical'; 288 289 } else if (strpos($answertext, '~') !== false) { 290 // Only Multiplechoice questions contain tilde ~. 291 $question->qtype = 'multichoice'; 292 293 } else if (strpos($answertext, '=') !== false 294 && strpos($answertext, '->') !== false) { 295 // Only Matching contains both = and ->. 296 $question->qtype = 'match'; 297 298 } else { // Either truefalse or shortanswer. 299 300 // Truefalse question check. 301 $truefalsecheck = $answertext; 302 if (strpos($answertext, '#') > 0) { 303 // Strip comments to check for TrueFalse question. 304 $truefalsecheck = trim(substr($answertext, 0, strpos($answertext, "#"))); 305 } 306 307 $validtfanswers = array('T', 'TRUE', 'F', 'FALSE'); 308 if (in_array($truefalsecheck, $validtfanswers)) { 309 $question->qtype = 'truefalse'; 310 311 } else { // Must be shortanswer. 312 $question->qtype = 'shortanswer'; 313 } 314 } 315 316 if (!isset($question->qtype)) { 317 $giftqtypenotset = get_string('giftqtypenotset', 'qformat_gift'); 318 $this->error($giftqtypenotset, $text); 319 return false; 320 } 321 322 switch ($question->qtype) { 323 case 'description': 324 $question->defaultmark = 0; 325 $question->length = 0; 326 return $question; 327 328 case 'essay': 329 $question->responseformat = 'editor'; 330 $question->responserequired = 1; 331 $question->responsefieldlines = 15; 332 $question->attachments = 0; 333 $question->attachmentsrequired = 0; 334 $question->graderinfo = array( 335 'text' => '', 'format' => FORMAT_HTML, 'files' => array()); 336 $question->responsetemplate = array( 337 'text' => '', 'format' => FORMAT_HTML); 338 return $question; 339 340 case 'multichoice': 341 if (strpos($answertext, "=") === false) { 342 $question->single = 0; // Multiple answers are enabled if no single answer is 100% correct. 343 } else { 344 $question->single = 1; // Only one answer allowed (the default). 345 } 346 $question = $this->add_blank_combined_feedback($question); 347 348 $answertext = str_replace("=", "~=", $answertext); 349 $answers = explode("~", $answertext); 350 if (isset($answers[0])) { 351 $answers[0] = trim($answers[0]); 352 } 353 if (empty($answers[0])) { 354 array_shift($answers); 355 } 356 357 $countanswers = count($answers); 358 359 if (!$this->check_answer_count(2, $answers, $text)) { 360 return false; 361 } 362 363 foreach ($answers as $key => $answer) { 364 $answer = trim($answer); 365 366 // Determine answer weight. 367 if ($answer[0] == '=') { 368 $answerweight = 1; 369 $answer = substr($answer, 1); 370 371 } else if (preg_match($giftanswerweightregex, $answer)) { // Check for properly formatted answer weight. 372 $answerweight = $this->answerweightparser($answer); 373 374 } else { // Default, i.e., wrong anwer. 375 $answerweight = 0; 376 } 377 list($question->answer[$key], $question->feedback[$key]) = 378 $this->commentparser($answer, $question->questiontextformat); 379 $question->fraction[$key] = $answerweight; 380 } // End foreach answer. 381 382 return $question; 383 384 case 'match': 385 $question = $this->add_blank_combined_feedback($question); 386 387 $answers = explode('=', $answertext); 388 if (isset($answers[0])) { 389 $answers[0] = trim($answers[0]); 390 } 391 if (empty($answers[0])) { 392 array_shift($answers); 393 } 394 395 if (!$this->check_answer_count(2, $answers, $text)) { 396 return false; 397 } 398 399 foreach ($answers as $key => $answer) { 400 $answer = trim($answer); 401 if (strpos($answer, "->") === false) { 402 $this->error(get_string('giftmatchingformat', 'qformat_gift'), $answer); 403 return false; 404 } 405 406 $marker = strpos($answer, '->'); 407 $question->subquestions[$key] = $this->parse_text_with_format( 408 substr($answer, 0, $marker), $question->questiontextformat); 409 $question->subanswers[$key] = trim($this->escapedchar_post( 410 substr($answer, $marker + 2))); 411 } 412 413 return $question; 414 415 case 'truefalse': 416 list($answer, $wrongfeedback, $rightfeedback) = 417 $this->split_truefalse_comment($answertext, $question->questiontextformat); 418 419 if ($answer['text'] == "T" || $answer['text'] == "TRUE") { 420 $question->correctanswer = 1; 421 $question->feedbacktrue = $rightfeedback; 422 $question->feedbackfalse = $wrongfeedback; 423 } else { 424 $question->correctanswer = 0; 425 $question->feedbacktrue = $wrongfeedback; 426 $question->feedbackfalse = $rightfeedback; 427 } 428 429 $question->penalty = 1; 430 431 return $question; 432 433 case 'shortanswer': 434 // Shortanswer question. 435 $answers = explode("=", $answertext); 436 if (isset($answers[0])) { 437 $answers[0] = trim($answers[0]); 438 } 439 if (empty($answers[0])) { 440 array_shift($answers); 441 } 442 443 if (!$this->check_answer_count(1, $answers, $text)) { 444 return false; 445 } 446 447 foreach ($answers as $key => $answer) { 448 $answer = trim($answer); 449 450 // Answer weight. 451 if (preg_match($giftanswerweightregex, $answer)) { // Check for properly formatted answer weight. 452 $answerweight = $this->answerweightparser($answer); 453 } else { // Default, i.e., full-credit anwer. 454 $answerweight = 1; 455 } 456 457 list($answer, $question->feedback[$key]) = $this->commentparser( 458 $answer, $question->questiontextformat); 459 460 $question->answer[$key] = $answer['text']; 461 $question->fraction[$key] = $answerweight; 462 } 463 464 return $question; 465 466 case 'numerical': 467 // Note similarities to ShortAnswer. 468 $answertext = substr($answertext, 1); // Remove leading "#". 469 470 // If there is feedback for a wrong answer, store it for now. 471 if (($pos = strpos($answertext, '~')) !== false) { 472 $wrongfeedback = substr($answertext, $pos); 473 $answertext = substr($answertext, 0, $pos); 474 } else { 475 $wrongfeedback = ''; 476 } 477 478 $answers = explode("=", $answertext); 479 if (isset($answers[0])) { 480 $answers[0] = trim($answers[0]); 481 } 482 if (empty($answers[0])) { 483 array_shift($answers); 484 } 485 486 if (count($answers) == 0) { 487 // Invalid question. 488 $giftnonumericalanswers = get_string('giftnonumericalanswers', 'qformat_gift'); 489 $this->error($giftnonumericalanswers, $text); 490 return false; 491 } 492 493 foreach ($answers as $key => $answer) { 494 $answer = trim($answer); 495 496 // Answer weight. 497 if (preg_match($giftanswerweightregex, $answer)) { // Check for properly formatted answer weight. 498 $answerweight = $this->answerweightparser($answer); 499 } else { // Default, i.e., full-credit anwer. 500 $answerweight = 1; 501 } 502 503 list($answer, $question->feedback[$key]) = $this->commentparser( 504 $answer, $question->questiontextformat); 505 $question->fraction[$key] = $answerweight; 506 $answer = $answer['text']; 507 508 // Calculate Answer and Min/Max values. 509 if (strpos($answer, "..") > 0) { // Optional [min]..[max] format. 510 $marker = strpos($answer, ".."); 511 $max = trim(substr($answer, $marker + 2)); 512 $min = trim(substr($answer, 0, $marker)); 513 $ans = ($max + $min)/2; 514 $tol = $max - $ans; 515 } else if (strpos($answer, ':') > 0) { // Standard [answer]:[errormargin] format. 516 $marker = strpos($answer, ':'); 517 $tol = trim(substr($answer, $marker+1)); 518 $ans = trim(substr($answer, 0, $marker)); 519 } else { // Only one valid answer (zero errormargin). 520 $tol = 0; 521 $ans = trim($answer); 522 } 523 524 if (!(is_numeric($ans) || $ans = '*') || !is_numeric($tol)) { 525 $errornotnumbers = get_string('errornotnumbers'); 526 $this->error($errornotnumbers, $text); 527 return false; 528 } 529 530 // Store results. 531 $question->answer[$key] = $ans; 532 $question->tolerance[$key] = $tol; 533 } 534 535 if ($wrongfeedback) { 536 $key += 1; 537 $question->fraction[$key] = 0; 538 list($notused, $question->feedback[$key]) = $this->commentparser( 539 $wrongfeedback, $question->questiontextformat); 540 $question->answer[$key] = '*'; 541 $question->tolerance[$key] = ''; 542 } 543 544 return $question; 545 546 default: 547 $this->error(get_string('giftnovalidquestion', 'qformat_gift'), $text); 548 return false; 549 550 } 551 } 552 553 protected function repchar($text, $notused = 0) { 554 // Escapes 'reserved' characters # = ~ {) : 555 // Removes new lines. 556 $reserved = array( '\\', '#', '=', '~', '{', '}', ':', "\n", "\r"); 557 $escaped = array('\\\\', '\#', '\=', '\~', '\{', '\}', '\:', '\n', ''); 558 559 $newtext = str_replace($reserved, $escaped, $text); 560 return $newtext; 561 } 562 563 /** 564 * @param int $format one of the FORMAT_ constants. 565 * @return string the corresponding name. 566 */ 567 protected function format_const_to_name($format) { 568 if ($format == FORMAT_MOODLE) { 569 return 'moodle'; 570 } else if ($format == FORMAT_HTML) { 571 return 'html'; 572 } else if ($format == FORMAT_PLAIN) { 573 return 'plain'; 574 } else if ($format == FORMAT_MARKDOWN) { 575 return 'markdown'; 576 } else { 577 return 'moodle'; 578 } 579 } 580 581 /** 582 * @param int $format one of the FORMAT_ constants. 583 * @return string the corresponding name. 584 */ 585 protected function format_name_to_const($format) { 586 if ($format == 'moodle') { 587 return FORMAT_MOODLE; 588 } else if ($format == 'html') { 589 return FORMAT_HTML; 590 } else if ($format == 'plain') { 591 return FORMAT_PLAIN; 592 } else if ($format == 'markdown') { 593 return FORMAT_MARKDOWN; 594 } else { 595 return -1; 596 } 597 } 598 599 public function write_name($name) { 600 return '::' . $this->repchar($name) . '::'; 601 } 602 603 public function write_questiontext($text, $format, $defaultformat = FORMAT_MOODLE) { 604 $output = ''; 605 if ($text != '' && $format != $defaultformat) { 606 $output .= '[' . $this->format_const_to_name($format) . ']'; 607 } 608 $output .= $this->repchar($text, $format); 609 return $output; 610 } 611 612 /** 613 * Outputs the general feedback for the question, if any. This needs to be the 614 * last thing before the }. 615 * @param object $question the question data. 616 * @param string $indent to put before the general feedback. Defaults to a tab. 617 * If this is not blank, a newline is added after the line. 618 */ 619 public function write_general_feedback($question, $indent = "\t") { 620 $generalfeedback = $this->write_questiontext($question->generalfeedback, 621 $question->generalfeedbackformat, $question->questiontextformat); 622 623 if ($generalfeedback) { 624 $generalfeedback = '####' . $generalfeedback; 625 if ($indent) { 626 $generalfeedback = $indent . $generalfeedback . "\n"; 627 } 628 } 629 630 return $generalfeedback; 631 } 632 633 public function writequestion($question) { 634 global $OUTPUT; 635 636 // Start with a comment. 637 $expout = "// question: {$question->id} name: {$question->name}\n"; 638 639 // Output depends on question type. 640 switch($question->qtype) { 641 642 case 'category': 643 // Not a real question, used to insert category switch. 644 $expout .= "\$CATEGORY: $question->category\n"; 645 break; 646 647 case 'description': 648 $expout .= $this->write_name($question->name); 649 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); 650 break; 651 652 case 'essay': 653 $expout .= $this->write_name($question->name); 654 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); 655 $expout .= "{"; 656 $expout .= $this->write_general_feedback($question, ''); 657 $expout .= "}\n"; 658 break; 659 660 case 'truefalse': 661 $trueanswer = $question->options->answers[$question->options->trueanswer]; 662 $falseanswer = $question->options->answers[$question->options->falseanswer]; 663 if ($trueanswer->fraction == 1) { 664 $answertext = 'TRUE'; 665 $rightfeedback = $this->write_questiontext($trueanswer->feedback, 666 $trueanswer->feedbackformat, $question->questiontextformat); 667 $wrongfeedback = $this->write_questiontext($falseanswer->feedback, 668 $falseanswer->feedbackformat, $question->questiontextformat); 669 } else { 670 $answertext = 'FALSE'; 671 $rightfeedback = $this->write_questiontext($falseanswer->feedback, 672 $falseanswer->feedbackformat, $question->questiontextformat); 673 $wrongfeedback = $this->write_questiontext($trueanswer->feedback, 674 $trueanswer->feedbackformat, $question->questiontextformat); 675 } 676 677 $expout .= $this->write_name($question->name); 678 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); 679 $expout .= '{' . $this->repchar($answertext); 680 if ($wrongfeedback) { 681 $expout .= '#' . $wrongfeedback; 682 } else if ($rightfeedback) { 683 $expout .= '#'; 684 } 685 if ($rightfeedback) { 686 $expout .= '#' . $rightfeedback; 687 } 688 $expout .= $this->write_general_feedback($question, ''); 689 $expout .= "}\n"; 690 break; 691 692 case 'multichoice': 693 $expout .= $this->write_name($question->name); 694 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); 695 $expout .= "{\n"; 696 foreach ($question->options->answers as $answer) { 697 if ($answer->fraction == 1 && $question->options->single) { 698 $answertext = '='; 699 } else if ($answer->fraction == 0) { 700 $answertext = '~'; 701 } else { 702 $weight = $answer->fraction * 100; 703 $answertext = '~%' . $weight . '%'; 704 } 705 $expout .= "\t" . $answertext . $this->write_questiontext($answer->answer, 706 $answer->answerformat, $question->questiontextformat); 707 if ($answer->feedback != '') { 708 $expout .= '#' . $this->write_questiontext($answer->feedback, 709 $answer->feedbackformat, $question->questiontextformat); 710 } 711 $expout .= "\n"; 712 } 713 $expout .= $this->write_general_feedback($question); 714 $expout .= "}\n"; 715 break; 716 717 case 'shortanswer': 718 $expout .= $this->write_name($question->name); 719 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); 720 $expout .= "{\n"; 721 foreach ($question->options->answers as $answer) { 722 $weight = 100 * $answer->fraction; 723 $expout .= "\t=%" . $weight . '%' . $this->repchar($answer->answer) . 724 '#' . $this->write_questiontext($answer->feedback, 725 $answer->feedbackformat, $question->questiontextformat) . "\n"; 726 } 727 $expout .= $this->write_general_feedback($question); 728 $expout .= "}\n"; 729 break; 730 731 case 'numerical': 732 $expout .= $this->write_name($question->name); 733 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); 734 $expout .= "{#\n"; 735 foreach ($question->options->answers as $answer) { 736 if ($answer->answer != '' && $answer->answer != '*') { 737 $weight = 100 * $answer->fraction; 738 $expout .= "\t=%" . $weight . '%' . $answer->answer . ':' . 739 (float)$answer->tolerance . '#' . $this->write_questiontext($answer->feedback, 740 $answer->feedbackformat, $question->questiontextformat) . "\n"; 741 } else { 742 $expout .= "\t~#" . $this->write_questiontext($answer->feedback, 743 $answer->feedbackformat, $question->questiontextformat) . "\n"; 744 } 745 } 746 $expout .= $this->write_general_feedback($question); 747 $expout .= "}\n"; 748 break; 749 750 case 'match': 751 $expout .= $this->write_name($question->name); 752 $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat); 753 $expout .= "{\n"; 754 foreach ($question->options->subquestions as $subquestion) { 755 $expout .= "\t=" . $this->write_questiontext($subquestion->questiontext, 756 $subquestion->questiontextformat, $question->questiontextformat) . 757 ' -> ' . $this->repchar($subquestion->answertext) . "\n"; 758 } 759 $expout .= $this->write_general_feedback($question); 760 $expout .= "}\n"; 761 break; 762 763 default: 764 // Check for plugins. 765 if ($out = $this->try_exporting_using_qtypes($question->qtype, $question)) { 766 $expout .= $out; 767 } 768 } 769 770 // Add empty line to delimit questions. 771 $expout .= "\n"; 772 return $expout; 773 } 774 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |