[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/question/format/gift/ -> format.php (source)

   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  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1