MDL-20636 Merge branch 'master' into qe2_wip
authorTim Hunt <T.J.Hunt@open.ac.uk>
Thu, 3 Feb 2011 11:07:11 +0000 (11:07 +0000)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Thu, 3 Feb 2011 11:18:40 +0000 (11:18 +0000)
Conflicts:
lib/questionlib.php
question/type/calculated/lang/en/qtype_calculated.php
question/type/questiontype.php

1  2 
lib/moodlelib.php
mod/quiz/locallib.php
question/format/gift/format.php
question/format/gift/simpletest/testgiftformat.php
question/type/numerical/question.php
question/type/shortanswer/question.php

diff --combined lib/moodlelib.php
@@@ -352,6 -352,8 +352,8 @@@ define('FEATURE_COMPLETION_TRACKS_VIEWS
  /** True if module has custom completion rules */
  define('FEATURE_COMPLETION_HAS_RULES', 'completion_has_rules');
  
+ /** True if module has no 'view' page (like label) */
+ define('FEATURE_NO_VIEW_LINK', 'viewlink');
  /** True if module supports outcomes */
  define('FEATURE_IDNUMBER', 'idnumber');
  /** True if module supports groups */
@@@ -792,10 -794,9 +794,9 @@@ function clean_param($param, $type) 
              }
  
          case PARAM_TAG:
-             //as long as magic_quotes_gpc is used, a backslash will be a
-             //problem, so remove *all* backslash.
-             //$param = str_replace('\\', '', $param);
-             //remove some nasties
+             // Please note it is not safe to use the tag name directly anywhere,
+             // it must be processed with s(), urlencode() before embedding anywhere.
+             // remove some nasties
              $param = preg_replace('~[[:cntrl:]]|[<>`]~u', '', $param);
              //convert many whitespace chars into one
              $param = preg_replace('/\s+/', ' ', $param);
              $param = $textlib->substr(trim($param), 0, TAG_MAX_LENGTH);
              return $param;
  
          case PARAM_TAGLIST:
              $tags = explode(',', $param);
              $result = array();
@@@ -2314,6 -2314,14 +2314,14 @@@ function require_login($courseorid = NU
              if ($cm->course != $course->id) {
                  throw new coding_exception('course and cm parameters in require_login() call do not match!!');
              }
+             // make sure we have a $cm from get_fast_modinfo as this contains activity access details
+             if (!($cm instanceof cm_info)) {
+                 // note: nearly all pages call get_fast_modinfo anyway and it does not make any
+                 // db queries so this is not really a performance concern, however it is obviously
+                 // better if you use get_fast_modinfo to get the cm before calling this.
+                 $modinfo = get_fast_modinfo($course);
+                 $cm = $modinfo->get_cm($cm->id);
+             }
              $PAGE->set_cm($cm, $course); // set's up global $COURSE
              $PAGE->set_pagelayout('incourse');
          } else {
          }
      }
  
-     // test visibility
-     if ($cm && !$cm->visible && !has_capability('moodle/course:viewhiddenactivities', $cmcontext)) {
+     // Check visibility of activity to current user; includes visible flag, groupmembersonly,
+     // conditional availability, etc
+     if ($cm && !$cm->uservisible) {
          if ($preventredirect) {
              throw new require_login_exception('Activity is hidden');
          }
          redirect($CFG->wwwroot, get_string('activityiscurrentlyhidden'));
      }
  
-     // groupmembersonly access control
-     if (!empty($CFG->enablegroupmembersonly) and $cm and $cm->groupmembersonly and !has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
-         if (isguestuser() or !groups_has_membership($cm)) {
-             if ($preventredirect) {
-                 throw new require_login_exception('Not member of a group');
-             }
-             print_error('groupmembersonlyerror', 'group', $CFG->wwwroot.'/course/view.php?id='.$cm->course);
-         }
-     }
-     // Conditional activity access control
-     if (!empty($CFG->enableavailability) and $cm) {
-         // TODO: this is going to work with login-as-user, sorry!
-         // We cache conditional access in session
-         if (!isset($SESSION->conditionaccessok)) {
-             $SESSION->conditionaccessok = array();
-         }
-         // If you have been allowed into the module once then you are allowed
-         // in for rest of session, no need to do conditional checks
-         if (!array_key_exists($cm->id, $SESSION->conditionaccessok)) {
-             // Get condition info (does a query for the availability table)
-             require_once($CFG->libdir.'/conditionlib.php');
-             $ci = new condition_info($cm, CONDITION_MISSING_EXTRATABLE);
-             // Check condition for user (this will do a query if the availability
-             // information depends on grade or completion information)
-             if ($ci->is_available($junk) || has_capability('moodle/course:viewhiddenactivities', $cmcontext)) {
-                 $SESSION->conditionaccessok[$cm->id] = true;
-             } else {
-                 print_error('activityiscurrentlyhidden');
-             }
-         }
-     }
      // Finally access granted, update lastaccess times
      user_accesstime_log($course->id);
  }
@@@ -2676,16 -2652,29 +2652,29 @@@ function require_logout() 
   */
  function require_course_login($courseorid, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = false) {
      global $CFG, $PAGE, $SITE;
+     $issite = (is_object($courseorid) and $courseorid->id == SITEID)
+           or (!is_object($courseorid) and $courseorid == SITEID);
+     if ($issite && !empty($cm) && !($cm instanceof cm_info)) {
+         // note: nearly all pages call get_fast_modinfo anyway and it does not make any
+         // db queries so this is not really a performance concern, however it is obviously
+         // better if you use get_fast_modinfo to get the cm before calling this.
+         if (is_object($courseorid)) {
+             $course = $courseorid;
+         } else {
+             $course = clone($SITE);
+         }
+         $modinfo = get_fast_modinfo($course);
+         $cm = $modinfo->get_cm($cm->id);
+     }
      if (!empty($CFG->forcelogin)) {
          // login required for both SITE and courses
          require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
  
-     } else if (!empty($cm) and !$cm->visible) {
+     } else if ($issite && !empty($cm) and !$cm->uservisible) {
          // always login for hidden activities
          require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
  
-     } else if ((is_object($courseorid) and $courseorid->id == SITEID)
-           or (!is_object($courseorid) and $courseorid == SITEID)) {
+     } else if ($issite) {
                //login for SITE not required
          if ($cm and empty($cm->visible)) {
              // hidden activities are not accessible without login
@@@ -3008,203 -2997,6 +2997,6 @@@ function reset_login_count() 
      $SESSION->logincount = 0;
  }
  
- /**
-  * Returns reference to full info about modules in course (including visibility).
-  * Cached and as fast as possible (0 or 1 db query).
-  *
-  * @global object
-  * @global object
-  * @global object
-  * @uses CONTEXT_MODULE
-  * @uses MAX_MODINFO_CACHE_SIZE
-  * @param mixed $course object or 'reset' string to reset caches, modinfo may be updated in db
-  * @param int $userid Defaults to current user id
-  * @return mixed courseinfo object or nothing if resetting
-  */
- function &get_fast_modinfo(&$course, $userid=0) {
-     global $CFG, $USER, $DB;
-     require_once($CFG->dirroot.'/course/lib.php');
-     if (!empty($CFG->enableavailability)) {
-         require_once($CFG->libdir.'/conditionlib.php');
-     }
-     static $cache = array();
-     if ($course === 'reset') {
-         $cache = array();
-         $nothing = null;
-         return $nothing; // we must return some reference
-     }
-     if (empty($userid)) {
-         $userid = $USER->id;
-     }
-     if (array_key_exists($course->id, $cache) and $cache[$course->id]->userid == $userid) {
-         return $cache[$course->id];
-     }
-     if (empty($course->modinfo)) {
-         // no modinfo yet - load it
-         rebuild_course_cache($course->id);
-         $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id));
-     }
-     $modinfo = new stdClass();
-     $modinfo->courseid  = $course->id;
-     $modinfo->userid    = $userid;
-     $modinfo->sections  = array();
-     $modinfo->cms       = array();
-     $modinfo->instances = array();
-     $modinfo->groups    = null; // loaded only when really needed - the only one db query
-     $info = unserialize($course->modinfo);
-     if (!is_array($info)) {
-         // hmm, something is wrong - lets try to fix it
-         rebuild_course_cache($course->id);
-         $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id));
-         $info = unserialize($course->modinfo);
-         if (!is_array($info)) {
-             return $modinfo;
-         }
-     }
-     if ($info) {
-         // detect if upgrade required
-         $first = reset($info);
-         if (!isset($first->id)) {
-             rebuild_course_cache($course->id);
-             $course->modinfo = $DB->get_field('course', 'modinfo', array('id'=>$course->id));
-             $info = unserialize($course->modinfo);
-             if (!is_array($info)) {
-                 return $modinfo;
-             }
-         }
-     }
-     $modlurals = array();
-     // If we haven't already preloaded contexts for the course, do it now
-     preload_course_contexts($course->id);
-     foreach ($info as $mod) {
-         if (empty($mod->name)) {
-             // something is wrong here
-             continue;
-         }
-         // reconstruct minimalistic $cm
-         $cm = new stdClass();
-         $cm->id               = $mod->cm;
-         $cm->instance         = $mod->id;
-         $cm->course           = $course->id;
-         $cm->modname          = $mod->mod;
-         $cm->idnumber         = $mod->idnumber;
-         $cm->name             = $mod->name;
-         $cm->visible          = $mod->visible;
-         $cm->sectionnum       = $mod->section;
-         $cm->groupmode        = $mod->groupmode;
-         $cm->groupingid       = $mod->groupingid;
-         $cm->groupmembersonly = $mod->groupmembersonly;
-         $cm->indent           = $mod->indent;
-         $cm->completion       = $mod->completion;
-         $cm->extra            = isset($mod->extra) ? $mod->extra : '';
-         $cm->icon             = isset($mod->icon) ? $mod->icon : '';
-         $cm->iconcomponent    = isset($mod->iconcomponent) ? $mod->iconcomponent : '';
-         $cm->uservisible      = true;
-         if (!empty($CFG->enableavailability)) {
-             // We must have completion information from modinfo. If it's not
-             // there, cache needs rebuilding
-             if(!isset($mod->availablefrom)) {
-                 debugging('enableavailability option was changed; rebuilding '.
-                     'cache for course '.$course->id);
-                 rebuild_course_cache($course->id,true);
-                 // Re-enter this routine to do it all properly
-                 return get_fast_modinfo($course, $userid);
-             }
-             $cm->availablefrom    = $mod->availablefrom;
-             $cm->availableuntil   = $mod->availableuntil;
-             $cm->showavailability = $mod->showavailability;
-             $cm->conditionscompletion = $mod->conditionscompletion;
-             $cm->conditionsgrade  = $mod->conditionsgrade;
-         }
-         // preload long names plurals and also check module is installed properly
-         if (!isset($modlurals[$cm->modname])) {
-             if (!file_exists("$CFG->dirroot/mod/$cm->modname/lib.php")) {
-                 continue;
-             }
-             $modlurals[$cm->modname] = get_string('modulenameplural', $cm->modname);
-         }
-         $cm->modplural = $modlurals[$cm->modname];
-         $modcontext = get_context_instance(CONTEXT_MODULE,$cm->id);
-         if (!empty($CFG->enableavailability)) {
-             // Unfortunately the next call really wants to call
-             // get_fast_modinfo, but that would be recursive, so we fake up a
-             // modinfo for it already
-             if (empty($minimalmodinfo)) { //TODO: this is suspicious (skodak)
-                 $minimalmodinfo = new stdClass();
-                 $minimalmodinfo->cms = array();
-                 foreach($info as $mod) {
-                     if (empty($mod->name)) {
-                         // something is wrong here
-                         continue;
-                     }
-                     $minimalcm = new stdClass();
-                     $minimalcm->id = $mod->cm;
-                     $minimalcm->name = $mod->name;
-                     $minimalmodinfo->cms[$minimalcm->id]=$minimalcm;
-                 }
-             }
-             // Get availability information
-             $ci = new condition_info($cm);
-             $cm->available = $ci->is_available($cm->availableinfo, true, $userid, $minimalmodinfo);
-         } else {
-             $cm->available = true;
-         }
-         if ((!$cm->visible or !$cm->available) and !has_capability('moodle/course:viewhiddenactivities', $modcontext, $userid)) {
-             $cm->uservisible = false;
-         } else if (!empty($CFG->enablegroupmembersonly) and !empty($cm->groupmembersonly)
-                 and !has_capability('moodle/site:accessallgroups', $modcontext, $userid)) {
-             if (is_null($modinfo->groups)) {
-                 $modinfo->groups = groups_get_user_groups($course->id, $userid);
-             }
-             if (empty($modinfo->groups[$cm->groupingid])) {
-                 $cm->uservisible = false;
-             }
-         }
-         if (!isset($modinfo->instances[$cm->modname])) {
-             $modinfo->instances[$cm->modname] = array();
-         }
-         $modinfo->instances[$cm->modname][$cm->instance] =& $cm;
-         $modinfo->cms[$cm->id] =& $cm;
-         // reconstruct sections
-         if (!isset($modinfo->sections[$cm->sectionnum])) {
-             $modinfo->sections[$cm->sectionnum] = array();
-         }
-         $modinfo->sections[$cm->sectionnum][] = $cm->id;
-         unset($cm);
-     }
-     unset($cache[$course->id]); // prevent potential reference problems when switching users
-     $cache[$course->id] = $modinfo;
-     // Ensure cache does not use too much RAM
-     if (count($cache) > MAX_MODINFO_CACHE_SIZE) {
-         reset($cache);
-         $key = key($cache);
-         unset($cache[$key]);
-     }
-     return $cache[$course->id];
- }
  /**
   * Determines if the currently logged in user is in editing mode.
   * Note: originally this function had $userid parameter - it was not usable anyway
@@@ -7229,9 -7021,8 +7021,9 @@@ function get_plugin_types($fullpaths=tr
                        'webservice'    => 'webservice',
                        'repository'    => 'repository',
                        'portfolio'     => 'portfolio',
 -                      'qtype'         => 'question/type',
 +                      'qbehaviour'    => 'question/behaviour',
                        'qformat'       => 'question/format',
 +                      'qtype'         => 'question/type',
                        'plagiarism'    => 'plagiarism',
                        'theme'         => 'theme'); // this is a bit hacky, themes may be in dataroot too
  
@@@ -8011,14 -7802,13 +7803,13 @@@ function notify_login_failures() 
            GROUP BY ip
              HAVING COUNT(*) >= ?";
      $params = array($CFG->lastnotifyfailure, $CFG->notifyloginthreshold);
-     if ($rs = $DB->get_recordset_sql($sql, $params)) {
-         foreach ($rs as $iprec) {
-             if (!empty($iprec->ip)) {
-                 set_cache_flag('login_failure_by_ip', $iprec->ip, '1', 0);
-             }
+     $rs = $DB->get_recordset_sql($sql, $params);
+     foreach ($rs as $iprec) {
+         if (!empty($iprec->ip)) {
+             set_cache_flag('login_failure_by_ip', $iprec->ip, '1', 0);
          }
-         $rs->close();
      }
+     $rs->close();
  
  /// Get all the INFOs with more than notifyloginthreshold failures since lastnotifyfailure
  /// and insert them into the cache_flags temp table
            GROUP BY info
              HAVING count(*) >= ?";
      $params = array($CFG->lastnotifyfailure, $CFG->notifyloginthreshold);
-     if ($rs = $DB->get_recordset_sql($sql, $params)) {
-         foreach ($rs as $inforec) {
-             if (!empty($inforec->info)) {
-                 set_cache_flag('login_failure_by_info', $inforec->info, '1', 0);
-             }
+     $rs = $DB->get_recordset_sql($sql, $params);
+     foreach ($rs as $inforec) {
+         if (!empty($inforec->info)) {
+             set_cache_flag('login_failure_by_info', $inforec->info, '1', 0);
          }
-         $rs->close();
      }
+     $rs->close();
  
  /// Now, select all the login error logged records belonging to the ips and infos
  /// since lastnotifyfailure, that we have stored in the cache_flags table
      $count = 0;
      $messages = '';
  /// Iterate over the logs recordset
-     if ($rs = $DB->get_recordset_sql($sql, $params)) {
-         foreach ($rs as $log) {
-             $log->time = userdate($log->time);
-             $messages .= get_string('notifyloginfailuresmessage','',$log)."\n";
-             $count++;
-         }
-         $rs->close();
+     $rs = $DB->get_recordset_sql($sql, $params);
+     foreach ($rs as $log) {
+         $log->time = userdate($log->time);
+         $messages .= get_string('notifyloginfailuresmessage','',$log)."\n";
+         $count++;
      }
+     $rs->close();
  
  /// If we haven't run in the last hour and
  /// we have something useful to report and we
diff --combined mod/quiz/locallib.php
@@@ -48,6 -48,7 +48,7 @@@ require_once($CFG->dirroot . '/mod/quiz
  require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
  require_once($CFG->dirroot . '/question/editlib.php');
  require_once($CFG->libdir  . '/eventslib.php');
+ require_once($CFG->libdir . '/filelib.php');
  
  /// Constants ///////////////////////////////////////////////////////////////////
  
@@@ -174,7 -175,6 +175,7 @@@ function quiz_load_attempt($attemptid) 
          return false;
      }
  
 +    // TODO kill this.
      if (!$DB->record_exists('question_sessions', array('attemptid' => $attempt->uniqueid))) {
      /// this attempt has not yet been upgraded to the new model
          quiz_upgrade_states($attempt);
@@@ -917,7 -917,7 +918,7 @@@ function quiz_get_reviewoptions($quiz, 
  
      // Show a link to the comment box only for closed attempts
      if ($attempt->timefinish && has_capability('mod/quiz:grade', $context)) {
-         $options->questioncommentlink = '/mod/quiz/comment.php';
+         $options->questioncommentlink = new moodle_url('/mod/quiz/comment.php', array('attempt' => $attempt->id));
      }
  
      // Whether to display a response history.
@@@ -1032,7 -1032,7 +1033,7 @@@ function quiz_send_confirmation($a) 
      $eventdata->smallmessage      = get_string('emailconfirmsmall', 'quiz', $a);
      $eventdata->contexturl        = $a->quizurl;
      $eventdata->contexturlname    = $a->quizname;
-     
      return message_send($eventdata);
  }
  
index 168dc54,e086691..6d3e4c4
mode 100644,100755..100644
@@@ -288,7 -288,7 +288,7 @@@ class qformat_gift extends qformat_defa
  
          switch ($question->qtype) {
              case DESCRIPTION:
 -                $question->defaultgrade = 0;
 +                $question->defaultmark = 0;
                  $question->length = 0;
                  return $question;
                  break;
                      $question->fraction[$key] = $answer_weight;
                  }  // end foreach answer
  
 -                //$question->defaultgrade = 1;
 +                //$question->defaultmark = 1;
                  //$question->image = "";   // No images with this format
                  return $question;
                  break;
                  list($answer, $wrongfeedback, $rightfeedback) =
                          $this->split_truefalse_comment($answertext, $question->questiontextformat);
  
-                 if ($answer == "T" OR $answer == "TRUE") {
+                 if ($answer['text'] == "T" OR $answer['text'] == "TRUE") {
                      $question->correctanswer = 1;
                      $question->feedbacktrue = $rightfeedback;
                      $question->feedbackfalse = $wrongfeedback;
          return $expout;
      }
  }
 -
@@@ -57,8 -57,8 +57,8 @@@ class qformat_gift_test extends UnitTes
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_MOODLE,
              'qtype' => 'essay',
 -            'defaultgrade' => 1,
 -            'penalty' => 0.1,
 +            'defaultmark' => 1,
 +            'penalty' => 0.3333333,
              'length' => 1,
              'feedback' => array(
                  'text' => '',
@@@ -78,8 -78,8 +78,8 @@@
              'questiontextformat' => FORMAT_MOODLE,
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_MOODLE,
 -            'defaultgrade' => 1,
 -            'penalty' => 0.1,
 +            'defaultmark' => 1,
 +            'penalty' => 0.3333333,
              'length' => 1,
              'qtype' => 'essay',
              'options' => (object) array(
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_HTML,
              'qtype' => 'match',
 -            'defaultgrade' => 1,
 -            'penalty' => 0.1,
 +            'defaultmark' => 1,
 +            'penalty' => 0.3333333,
              'length' => 1,
              'shuffleanswers' => '1',
              'subquestions' => array(
              'questiontextformat' => FORMAT_HTML,
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_HTML,
 -            'defaultgrade' => 1,
 -            'penalty' => 0.1,
 +            'defaultmark' => 1,
 +            'penalty' => 0.3333333,
              'length' => 1,
              'qtype' => 'match',
              'options' => (object) array(
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_MOODLE,
              'qtype' => 'multichoice',
 -            'defaultgrade' => 1,
 -            'penalty' => 0.1,
 +            'defaultmark' => 1,
 +            'penalty' => 0.3333333,
              'length' => 1,
              'single' => 1,
              'shuffleanswers' => '1',
              'questiontextformat' => FORMAT_MOODLE,
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_MOODLE,
 -            'defaultgrade' => 1,
 -            'penalty' => 0.1,
 +            'defaultmark' => 1,
 +            'penalty' => 0.3333333,
              'length' => 1,
              'qtype' => 'multichoice',
              'options' => (object) array(
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_MOODLE,
              'qtype' => 'numerical',
 -            'defaultgrade' => 1,
 -            'penalty' => 0.1,
 +            'defaultmark' => 1,
 +            'penalty' => 0.3333333,
              'length' => 1,
              'answer' => array(
                  '3',
              'questiontextformat' => FORMAT_MOODLE,
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_MOODLE,
 -            'defaultgrade' => 1,
 +            'defaultmark' => 1,
              'penalty' => 1,
              'length' => 1,
              'qtype' => 'numerical',
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_MOODLE,
              'qtype' => 'shortanswer',
 -            'defaultgrade' => 1,
 -            'penalty' => 0.1,
 +            'defaultmark' => 1,
 +            'penalty' => 0.3333333,
              'length' => 1,
              'answer' => array(
                  'Frog',
              'questiontextformat' => FORMAT_MOODLE,
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_MOODLE,
 -            'defaultgrade' => 1,
 +            'defaultmark' => 1,
              'penalty' => 1,
              'length' => 1,
              'qtype' => 'shortanswer',
@@@ -640,7 -640,7 +640,7 @@@ FALSE#42 is the Ultimate Answer.#You ga
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_MOODLE,
              'qtype' => 'truefalse',
 -            'defaultgrade' => 1,
 +            'defaultmark' => 1,
              'penalty' => 1,
              'length' => 1,
              'correctanswer' => 0,
          $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
      }
  
 -            'defaultgrade' => 1,
+     public function test_import_truefalse_true_answer1() {
+         $gift = "// name 0-11
+ ::2-08 TSL::TSL is blablabla.{T}";
+         $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
+         $importer = new qformat_gift();
+         $q = $importer->readquestion($lines);
+         $expectedq = (object) array(
+             'name' => '2-08 TSL',
+             'questiontext' => "TSL is blablabla.",
+             'questiontextformat' => FORMAT_MOODLE,
+             'generalfeedback' => '',
+             'generalfeedbackformat' => FORMAT_MOODLE,
+             'qtype' => 'truefalse',
 -            'defaultgrade' => 1,
++            'defaultmark' => 1,
+             'penalty' => 1,
+             'length' => 1,
+             'correctanswer' => 1,
+             'feedbacktrue' => array(
+                 'text' => '',
+                 'format' => FORMAT_MOODLE,
+                 'files' => array(),
+             ),
+             'feedbackfalse' => array(
+                 'text' => '',
+                 'format' => FORMAT_MOODLE,
+                 'files' => array(),
+             ),
+         );
+         $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
+     }
+     public function test_import_truefalse_true_answer2() {
+         $gift = "// name 0-11
+ ::2-08 TSL::TSL is blablabla.{TRUE}";
+         $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
+         $importer = new qformat_gift();
+         $q = $importer->readquestion($lines);
+         $expectedq = (object) array(
+             'name' => '2-08 TSL',
+             'questiontext' => "TSL is blablabla.",
+             'questiontextformat' => FORMAT_MOODLE,
+             'generalfeedback' => '',
+             'generalfeedbackformat' => FORMAT_MOODLE,
+             'qtype' => 'truefalse',
++            'defaultmark' => 1,
+             'penalty' => 1,
+             'length' => 1,
+             'correctanswer' => 1,
+             'feedbacktrue' => array(
+                 'text' => '',
+                 'format' => FORMAT_MOODLE,
+                 'files' => array(),
+             ),
+             'feedbackfalse' => array(
+                 'text' => '',
+                 'format' => FORMAT_MOODLE,
+                 'files' => array(),
+             ),
+         );
+         $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
+     }
      public function test_export_truefalse() {
          $qdata = (object) array(
              'id' => 666 ,
              'questiontextformat' => FORMAT_MOODLE,
              'generalfeedback' => '',
              'generalfeedbackformat' => FORMAT_MOODLE,
 -            'defaultgrade' => 1,
 +            'defaultmark' => 1,
              'penalty' => 1,
              'length' => 1,
              'qtype' => 'truefalse',
index 7eba5a7,0000000..1e57595
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,151 @@@
-         return array('answer' => PARAM_TRIM);
 +<?php
 +
 +// This file is part of Moodle - http://moodle.org/
 +//
 +// Moodle is free software: you can redistribute it and/or modify
 +// it under the terms of the GNU General Public License as published by
 +// the Free Software Foundation, either version 3 of the License, or
 +// (at your option) any later version.
 +//
 +// Moodle is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 +
 +
 +/**
 + * Numerical question definition class.
 + *
 + * @package qtype_numerical
 + * @copyright 2009 The Open University
 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 + */
 +
 +require_once($CFG->dirroot . '/question/type/numerical/questiontype.php');
 +
 +
 +/**
 + * Represents a numerical question.
 + *
 + * @copyright 2009 The Open University
 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 + */
 +class qtype_numerical_question extends question_graded_by_strategy
 +        implements question_response_answer_comparer {
 +    /** @var array of question_answer. */
 +    public $answers = array();
 +    /** @var qtype_numerical_answer_processor */
 +    public $ap;
 +
 +    public function __construct() {
 +        parent::__construct(new question_first_matching_answer_grading_strategy($this));
 +    }
 +
 +    public function get_expected_data() {
++        return array('answer' => PARAM_RAW_TRIMMED);
 +    }
 +
 +    public function init_first_step(question_attempt_step $step) {
 +        if ($step->has_qt_var('_separators')) {
 +            list($point, $separator) = explode('$', $step->get_qt_var('_separators'));
 +            $this->ap->set_characters($point, $separator);
 +        } else {
 +            $step->set_qt_var('_separators',
 +                    $this->ap->get_point() . '$' . $this->ap->get_separator());
 +        }
 +    }
 +
 +    public function summarise_response(array $response) {
 +        if (isset($response['answer'])) {
 +            return $response['answer'];
 +        } else {
 +            return null;
 +        }
 +    }
 +
 +    public function is_complete_response(array $response) {
 +        return array_key_exists('answer', $response) &&
 +                ($response['answer'] || $response['answer'] === '0' || $response['answer'] === 0);
 +    }
 +
 +    public function get_validation_error(array $response) {
 +        if ($this->is_gradable_response($response)) {
 +            return '';
 +        }
 +        return get_string('pleaseenterananswer', 'qtype_numerical');
 +    }
 +
 +    public function is_same_response(array $prevresponse, array $newresponse) {
 +        return question_utils::arrays_same_at_key_missing_is_blank(
 +                $prevresponse, $newresponse, 'answer');
 +    }
 +
 +    public function get_answers() {
 +        return $this->answers;
 +    }
 +
 +    public function compare_response_with_answer(array $response, question_answer $answer) {
 +        list($value, $unit) = $this->ap->apply_units($response['answer']);
 +        return $answer->within_tolerance($value);
 +    }
 +}
 +
 +
 +/**
 + * Subclass of {@link question_answer} with the extra information required by
 + * the numerical question type.
 + *
 + * @copyright 2009 The Open University
 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 + */
 +class qtype_numerical_answer extends question_answer {
 +    /** @var float allowable margin of error. */
 +    public $tolerance;
 +    /** @var integer|string see {@link get_tolerance_interval()} for the meaning of this value. */
 +    public $tolerancetype = 2;
 +
 +    public function __construct($id, $answer, $fraction, $feedback, $feedbackformat, $tolerance) {
 +        parent::__construct($id, $answer, $fraction, $feedback, $feedbackformat);
 +        $this->tolerance = abs($tolerance);
 +    }
 +
 +    public function get_tolerance_interval() {
 +        if ($this->answer === '*') {
 +            throw new Exception('Cannot work out tolerance interval for answer *.');
 +        }
 +
 +        // We need to add a tiny fraction depending on the set precision to make
 +        // the comparison work correctly, otherwise seemingly equal values can
 +        // yield false. See MDL-3225.
 +        $tolerance = (float) $this->tolerance + pow(10, -1 * ini_get('precision'));
 +
 +        switch ($this->tolerancetype) {
 +            case 1: case 'relative':
 +                $range = abs($this->answer) * $tolerance;
 +                return array($this->answer - $range, $this->answer + $range);
 +
 +            case 2: case 'nominal':
 +                $tolerance = $this->tolerance + pow(10, -1 * ini_get('precision')) *
 +                        max(1, abs($this->answer));
 +                return array($this->answer - $tolerance, $this->answer + $tolerance);
 +
 +            case 3: case 'geometric':
 +                $quotient = 1 + abs($tolerance);
 +                return array($this->answer / $quotient, $this->answer * $quotient);
 +
 +            default:
 +                throw new Exception('Unknown tolerance type ' . $this->tolerancetype);
 +        }
 +    }
 +
 +    public function within_tolerance($value) {
 +        if ($this->answer === '*') {
 +            return true;
 +        }
 +        list($min, $max) = $this->get_tolerance_interval();
 +        return $min <= $value && $value <= $max;
 +    }
 +}
index 8ea182d,0000000..ca36116
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,115 @@@
-         return array('answer' => PARAM_TRIM);
 +<?php
 +
 +// This file is part of Moodle - http://moodle.org/
 +//
 +// Moodle is free software: you can redistribute it and/or modify
 +// it under the terms of the GNU General Public License as published by
 +// the Free Software Foundation, either version 3 of the License, or
 +// (at your option) any later version.
 +//
 +// Moodle is distributed in the hope that it will be useful,
 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +// GNU General Public License for more details.
 +//
 +// You should have received a copy of the GNU General Public License
 +// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 +
 +
 +/**
 + * Short answer question definition class.
 + *
 + * @package qtype_shortanswer
 + * @copyright 2009 The Open University
 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 + */
 +
 +
 +/**
 + * Represents a short answer question.
 + *
 + * @copyright 2009 The Open University
 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 + */
 +class qtype_shortanswer_question extends question_graded_by_strategy
 +        implements question_response_answer_comparer {
 +    /** @var boolean whether answers should be graded case-sensitively. */
 +    public $usecase;
 +    /** @var array of question_answer. */
 +    public $answers = array();
 +
 +    public function __construct() {
 +        parent::__construct(new question_first_matching_answer_grading_strategy($this));
 +    }
 +
 +    public function get_expected_data() {
++        return array('answer' => PARAM_RAW_TRIMMED);
 +    }
 +
 +    public function summarise_response(array $response) {
 +        if (isset($response['answer'])) {
 +            return $response['answer'];
 +        } else {
 +            return null;
 +        }
 +    }
 +
 +    public function is_complete_response(array $response) {
 +        return array_key_exists('answer', $response) &&
 +                ($response['answer'] || $response['answer'] === '0');
 +    }
 +
 +    public function get_validation_error(array $response) {
 +        if ($this->is_gradable_response($response)) {
 +            return '';
 +        }
 +        return get_string('pleaseenterananswer', 'qtype_shortanswer');
 +    }
 +
 +    public function is_same_response(array $prevresponse, array $newresponse) {
 +        return question_utils::arrays_same_at_key_missing_is_blank(
 +                $prevresponse, $newresponse, 'answer');
 +    }
 +
 +    public function get_answers() {
 +        return $this->answers;
 +    }
 +
 +    public function compare_response_with_answer(array $response, question_answer $answer) {
 +        return self::compare_string_with_wildcard($response['answer'], $answer->answer, !$this->usecase);
 +    }
 +
 +    public static function compare_string_with_wildcard($string, $pattern, $ignorecase) {
 +        // Break the string on non-escaped asterisks.
 +        $bits = preg_split('/(?<!\\\\)\*/', $pattern);
 +        // Escape regexp special characters in the bits.
 +        $excapedbits = array();
 +        foreach ($bits as $bit) {
 +            $excapedbits[] = preg_quote(str_replace('\*', '*', $bit));
 +        }
 +        // Put it back together to make the regexp.
 +        $regexp = '|^' . implode('.*', $excapedbits) . '$|u';
 +
 +        // Make the match insensitive if requested to.
 +        if ($ignorecase) {
 +            $regexp .= 'i';
 +        }
 +
 +        return preg_match($regexp, trim($string));
 +    }
 +
 +    public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) {
 +        if ($component == 'question' && $filearea == 'answerfeedback') {
 +            $currentanswer = $qa->get_last_qt_var('answer');
 +            $answer = $qa->get_question()->get_matching_answer(array('answer' => $currentanswer));
 +            $answerid = reset($args); // itemid is answer id.
 +            return $options->feedback && $answerid == $answer->id;
 +
 +        } else if ($component == 'question' && $filearea == 'hint') {
 +            return $this->check_hint_file_access($qa, $options, $args);
 +
 +        } else {
 +            return parent::check_file_access($qa, $options, $component, $filearea, $args, $forcedownload);
 +        }
 +    }
 +}