Merge remote-tracking branch 'moodle/master' into MDL-20636_master_new_question_engine
[moodle.git] / mod / quiz / lib.php
index 5524773..ccac9d5 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
  * This contains functions that are called also from outside the quiz module
  * Functions that are only called by the quiz module itself are in {@link locallib.php}
  *
- * @package mod-quiz
- * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package    mod
+ * @subpackage quiz
+ * @copyright  1999 onwards Martin Dougiamas {@link http://moodle.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-/** Require {@link eventslib.php} */
+
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir . '/eventslib.php');
-/** Require {@link calendar/lib.php} */
 require_once($CFG->dirroot . '/calendar/lib.php');
 
-/// CONSTANTS ///////////////////////////////////////////////////////////////////
 
 /**#@+
- * Options determining how the grades from individual attempts are combined to give
- * the overall grade for a user
+ * Option controlling what options are offered on the quiz settings form.
  */
-define('QUIZ_GRADEHIGHEST', 1);
-define('QUIZ_GRADEAVERAGE', 2);
-define('QUIZ_ATTEMPTFIRST', 3);
-define('QUIZ_ATTEMPTLAST', 4);
-/**#@-*/
-
 define('QUIZ_MAX_ATTEMPT_OPTION', 10);
 define('QUIZ_MAX_QPP_OPTION', 50);
 define('QUIZ_MAX_DECIMAL_OPTION', 5);
 define('QUIZ_MAX_Q_DECIMAL_OPTION', 7);
-
-/**#@+
- * The different review options are stored in the bits of $quiz->review
- * These constants help to extract the options
- *
- * This is more of a mess than you might think necessary, because originally
- * it was though that 3x6 bits were enough, but then they ran out. PHP integers
- * are only reliably 32 bits signed, so the simplest solution was then to
- * add 4x3 more bits.
- */
-/**
- * The first 6 + 4 bits refer to the time immediately after the attempt
- */
-define('QUIZ_REVIEW_IMMEDIATELY', 0x3c003f);
-/**
- * the next 6 + 4 bits refer to the time after the attempt but while the quiz is open
- */
-define('QUIZ_REVIEW_OPEN',       0x3c00fc0);
-/**
- * the final 6 + 4 bits refer to the time after the quiz closes
- */
-define('QUIZ_REVIEW_CLOSED',    0x3c03f000);
-
-// within each group of 6 bits we determine what should be shown
-define('QUIZ_REVIEW_RESPONSES',       1*0x1041); // Show responses
-define('QUIZ_REVIEW_SCORES',          2*0x1041); // Show scores
-define('QUIZ_REVIEW_FEEDBACK',        4*0x1041); // Show question feedback
-define('QUIZ_REVIEW_ANSWERS',         8*0x1041); // Show correct answers
-// Some handling of worked solutions is already in the code but not yet fully supported
-// and not switched on in the user interface.
-define('QUIZ_REVIEW_SOLUTIONS',      16*0x1041); // Show solutions
-define('QUIZ_REVIEW_GENERALFEEDBACK',32*0x1041); // Show question general feedback
-define('QUIZ_REVIEW_OVERALLFEEDBACK', 1*0x4440000); // Show quiz overall feedback
-// Multipliers 2*0x4440000, 4*0x4440000 and 8*0x4440000 are still available
 /**#@-*/
 
 /**
  * If start and end date for the quiz are more than this many seconds apart
  * they will be represented by two separate events in the calendar
  */
-define("QUIZ_MAX_EVENT_LENGTH", 5*24*60*60);   // 5 days maximum
-
-/// FUNCTIONS ///////////////////////////////////////////////////////////////////
+define('QUIZ_MAX_EVENT_LENGTH', 5*24*60*60); // 5 days
 
 /**
  * Given an object containing all the necessary data,
@@ -97,7 +54,6 @@ define("QUIZ_MAX_EVENT_LENGTH", 5*24*60*60);   // 5 days maximum
  * will create a new instance and return the id number
  * of the new instance.
  *
- * @global object
  * @param object $quiz the data that came from the form.
  * @return mixed the id of the new instance on success,
  *          false or a string error message on failure.
@@ -128,8 +84,6 @@ function quiz_add_instance($quiz) {
  * (defined by the form in mod_form.php) this function
  * will update an existing instance with new data.
  *
- * @global stdClass
- * @global object
  * @param object $quiz the data that came from the form.
  * @return mixed true on success, false or a string error message on failure.
  */
@@ -142,11 +96,12 @@ function quiz_update_instance($quiz, $mform) {
         return $result;
     }
 
+    $oldquiz = $DB->get_record('quiz', array('id' => $quiz->instance));
+
     // Repaginate, if asked to.
     if (!$quiz->shufflequestions && !empty($quiz->repaginatenow)) {
         require_once($CFG->dirroot . '/mod/quiz/locallib.php');
-        $quiz->questions = $DB->get_field('quiz', 'questions', array('id' => $quiz->instance));
-        $quiz->questions = quiz_repaginate($quiz->questions, $quiz->questionsperpage);
+        $quiz->questions = quiz_repaginate($oldquiz->questions, $quiz->questionsperpage);
     }
     unset($quiz->repaginatenow);
 
@@ -157,6 +112,14 @@ function quiz_update_instance($quiz, $mform) {
     // Do the processing required after an add or an update.
     quiz_after_add_or_update($quiz);
 
+    if ($oldquiz->grademethod != $quiz->grademethod) {
+        require_once($CFG->dirroot . '/mod/quiz/locallib.php');
+        $quiz->sumgrades = $oldquiz->sumgrades;
+        $quiz->grade = $oldquiz->grade;
+        quiz_update_all_final_grades($quiz);
+        quiz_update_grades($quiz);
+    }
+
     // Delete any previous preview attempts
     quiz_delete_previews($quiz);
 
@@ -168,16 +131,13 @@ function quiz_update_instance($quiz, $mform) {
  * this function will permanently delete the instance
  * and any data that depends on it.
  *
- * @global object
- * @param int $id
- * @return bool
+ * @param int $id the id of the quiz to delete.
+ * @return bool success or failure.
  */
 function quiz_delete_instance($id) {
     global $DB;
 
-    if (!$quiz = $DB->get_record('quiz', array('id' => $id))) {
-        return false;
-    }
+    $quiz = $DB->get_record('quiz', array('id' => $id), '*', MUST_EXIST);
 
     quiz_delete_all_attempts($quiz);
     quiz_delete_all_overrides($quiz);
@@ -186,7 +146,7 @@ function quiz_delete_instance($id) {
     $DB->delete_records('quiz_feedback', array('quizid' => $quiz->id));
 
     $events = $DB->get_records('event', array('modulename' => 'quiz', 'instance' => $quiz->id));
-    foreach($events as $event) {
+    foreach ($events as $event) {
         $event = calendar_event::load($event);
         $event->delete();
     }
@@ -201,21 +161,19 @@ function quiz_delete_instance($id) {
  * Deletes a quiz override from the database and clears any corresponding calendar events
  *
  * @param object $quiz The quiz object.
- * @param integer $overrideid The id of the override being deleted
+ * @param int $overrideid The id of the override being deleted
  * @return bool true on success
  */
 function quiz_delete_override($quiz, $overrideid) {
     global $DB;
 
-    if (!$override = $DB->get_record('quiz_overrides', array('id' => $overrideid))) {
-        return false;
-    }
-    $groupid   = empty($override->groupid)?   0 : $override->groupid;
-    $userid    = empty($override->userid)?    0 : $override->userid;
+    $override = $DB->get_record('quiz_overrides', array('id' => $overrideid), '*', MUST_EXIST);
 
     // Delete the events
-    $events = $DB->get_records('event', array('modulename'=>'quiz', 'instance'=>$quiz->id, 'groupid'=>$groupid, 'userid'=>$userid));
-    foreach($events as $event) {
+    $events = $DB->get_records('event', array('modulename' => 'quiz',
+            'instance' => $quiz->id, 'groupid' => (int)$override->groupid,
+            'userid' => (int)$override->userid));
+    foreach ($events as $event) {
         $eventold = calendar_event::load($event);
         $eventold->delete();
     }
@@ -250,7 +208,7 @@ function quiz_delete_all_overrides($quiz) {
  *   passwords.
  *
  * @param object $quiz The quiz object.
- * @param integer $userid The userid.
+ * @param int $userid The userid.
  * @return object $quiz The updated quiz object.
  */
 function quiz_update_effective_access($quiz, $userid) {
@@ -305,28 +263,28 @@ function quiz_update_effective_access($quiz, $userid) {
         }
         // If there is a user override for a setting, ignore the group override
         if (is_null($override->timeopen) && count($opens)) {
-            $override->timeopen  = min($opens);
+            $override->timeopen = min($opens);
         }
         if (is_null($override->timeclose) && count($closes)) {
-            $override->timeclose  = max($closes);
+            $override->timeclose = max($closes);
         }
         if (is_null($override->timelimit) && count($limits)) {
-            $override->timelimit  = max($limits);
+            $override->timelimit = max($limits);
         }
         if (is_null($override->attempts) && count($attempts)) {
-            $override->attempts  = max($attempts);
+            $override->attempts = max($attempts);
         }
         if (is_null($override->password) && count($passwords)) {
-            $override->password  = array_shift($passwords);
+            $override->password = array_shift($passwords);
             if (count($passwords)) {
-                $override->extrapasswords  = $passwords;
+                $override->extrapasswords = $passwords;
             }
         }
 
     }
 
     // merge with quiz defaults
-    $keys = array('timeopen','timeclose', 'timelimit', 'attempts', 'password', 'extrapasswords');
+    $keys = array('timeopen', 'timeclose', 'timelimit', 'attempts', 'password', 'extrapasswords');
     foreach ($keys as $key) {
         if (isset($override->{$key})) {
             $quiz->{$key} = $override->{$key};
@@ -339,21 +297,49 @@ function quiz_update_effective_access($quiz, $userid) {
 /**
  * Delete all the attempts belonging to a quiz.
  *
- * @global stdClass
- * @global object
  * @param object $quiz The quiz object.
  */
 function quiz_delete_all_attempts($quiz) {
     global $CFG, $DB;
-    require_once($CFG->libdir . '/questionlib.php');
-    $attempts = $DB->get_records('quiz_attempts', array('quiz' => $quiz->id));
-    foreach ($attempts as $attempt) {
-        delete_attempt($attempt->uniqueid);
-    }
+    require_once($CFG->dirroot . '/mod/quiz/locallib.php');
+    question_engine::delete_questions_usage_by_activities(new qubaids_for_quiz($quiz->id));
     $DB->delete_records('quiz_attempts', array('quiz' => $quiz->id));
     $DB->delete_records('quiz_grades', array('quiz' => $quiz->id));
 }
 
+/**
+ * Get the best current grade for a particular user in a quiz.
+ *
+ * @param object $quiz the quiz settings.
+ * @param int $userid the id of the user.
+ * @return float the user's current grade for this quiz, or null if this user does
+ * not have a grade on this quiz.
+ */
+function quiz_get_best_grade($quiz, $userid) {
+    global $DB;
+    $grade = $DB->get_field('quiz_grades', 'grade',
+            array('quiz' => $quiz->id, 'userid' => $userid));
+
+    // Need to detect errors/no result, without catching 0 grades.
+    if ($grade === false) {
+        return null;
+    }
+
+    return $grade + 0; // Convert to number.
+}
+
+/**
+ * Is this a graded quiz? If this method returns true, you can assume that
+ * $quiz->grade and $quiz->sumgrades are non-zero (for example, if you want to
+ * divide by them).
+ *
+ * @param object $quiz a row from the quiz table.
+ * @return bool whether this is a graded quiz.
+ */
+function quiz_has_grades($quiz) {
+    return $quiz->grade >= 0.000005 && $quiz->sumgrades >= 0.000005;
+}
+
 /**
  * Return a small object with summary information about what a
  * user has done with a given particular instance of this module
@@ -361,7 +347,6 @@ function quiz_delete_all_attempts($quiz) {
  * $return->time = the time they did it
  * $return->info = a short text description
  *
- * @global object
  * @param object $course
  * @param object $user
  * @param object $mod
@@ -379,7 +364,7 @@ function quiz_user_outline($course, $user, $mod, $quiz) {
         $grade = reset($grades->items[0]->grades);
     }
 
-    $result = new stdClass;
+    $result = new stdClass();
     $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
 
     //datesubmitted == time created. dategraded == time modified or time overridden
@@ -394,38 +379,6 @@ function quiz_user_outline($course, $user, $mod, $quiz) {
     return $result;
 }
 
-/**
- * Is this a graded quiz? If this method returns true, you can assume that
- * $quiz->grade and $quiz->sumgrades are non-zero (for example, if you want to
- * divide by them).
- *
- * @param object $quiz a row from the quiz table.
- * @return boolean whether this is a graded quiz.
- */
-function quiz_has_grades($quiz) {
-    return $quiz->grade != 0 && $quiz->sumgrades != 0;
-}
-
-/**
- * Get the best current grade for a particular user in a quiz.
- *
- * @param object $quiz the quiz settings.
- * @param integer $userid the id of the user.
- * @return float the user's current grade for this quiz, or NULL if this user does
- * not have a grade on this quiz.
- */
-function quiz_get_best_grade($quiz, $userid) {
-    global $DB;
-    $grade = $DB->get_field('quiz_grades', 'grade', array('quiz' => $quiz->id, 'userid' => $userid));
-
-    // Need to detect errors/no result, without catching 0 scores.
-    if ($grade === false) {
-        return null;
-    }
-
-    return $grade + 0; // Convert to number.
-}
-
 /**
  * Print a detailed representation of what a  user has done with
  * a given particular instance of this module, for user activity reports.
@@ -440,6 +393,7 @@ function quiz_get_best_grade($quiz, $userid) {
 function quiz_user_complete($course, $user, $mod, $quiz) {
     global $DB, $CFG, $OUTPUT;
     require_once("$CFG->libdir/gradelib.php");
+
     $grades = grade_get_grades($course->id, 'mod', 'quiz', $quiz->id, $user->id);
     if (!empty($grades->items[0]->grades)) {
         $grade = reset($grades->items[0]->grades);
@@ -449,18 +403,20 @@ function quiz_user_complete($course, $user, $mod, $quiz) {
         }
     }
 
-    if ($attempts = $DB->get_records('quiz_attempts', array('userid' => $user->id, 'quiz' => $quiz->id), 'attempt')) {
+    if ($attempts = $DB->get_records('quiz_attempts',
+            array('userid' => $user->id, 'quiz' => $quiz->id), 'attempt')) {
         foreach ($attempts as $attempt) {
             echo get_string('attempt', 'quiz').' '.$attempt->attempt.': ';
             if ($attempt->timefinish == 0) {
                 print_string('unfinished');
             } else {
-                echo quiz_format_grade($quiz, $attempt->sumgrades) . '/' . quiz_format_grade($quiz, $quiz->sumgrades);
+                echo quiz_format_grade($quiz, $attempt->sumgrades) . '/' .
+                        quiz_format_grade($quiz, $quiz->sumgrades);
             }
             echo ' - '.userdate($attempt->timemodified).'<br />';
         }
     } else {
-       print_string('noattempts', 'quiz');
+        print_string('noattempts', 'quiz');
     }
 
     return true;
@@ -471,24 +427,21 @@ function quiz_user_complete($course, $user, $mod, $quiz) {
  * This function searches for things that need to be done, such
  * as sending out mail, toggling flags etc ...
  *
- * @global stdClass
  * @return bool true
  */
 function quiz_cron() {
-    global $CFG;
-
     return true;
 }
 
 /**
- * @global object
- * @param integer $quizid the quiz id.
- * @param integer $userid the userid.
+ * @param int $quizid the quiz id.
+ * @param int $userid the userid.
  * @param string $status 'all', 'finished' or 'unfinished' to control
  * @param bool $includepreviews
- * @return an array of all the user's attempts at this quiz. Returns an empty array if there are none.
+ * @return an array of all the user's attempts at this quiz. Returns an empty
+ *      array if there are none.
  */
-function quiz_get_user_attempts($quizid, $userid=0, $status = 'finished', $includepreviews = false) {
+function quiz_get_user_attempts($quizid, $userid, $status = 'finished', $includepreviews = false) {
     global $DB;
     $status_condition = array(
         'all' => '',
@@ -499,47 +452,43 @@ function quiz_get_user_attempts($quizid, $userid=0, $status = 'finished', $inclu
     if (!$includepreviews) {
         $previewclause = ' AND preview = 0';
     }
-    $params=array($quizid);
-    if ($userid){
-        $userclause = ' AND userid = ?';
-        $params[]=$userid;
-    } else {
-        $userclause = '';
-    }
-    if ($attempts = $DB->get_records_select('quiz_attempts',
-            "quiz = ?" .$userclause. $previewclause . $status_condition[$status], $params,
-            'attempt ASC')) {
-        return $attempts;
-    } else {
-        return array();
-    }
+    return $DB->get_records_select('quiz_attempts',
+            'quiz = ? AND userid = ?' . $previewclause . $status_condition[$status],
+            array($quizid, $userid), 'attempt ASC');
 }
 
 /**
  * Return grade for given user or all users.
  *
- * @global stdClass
- * @global object
  * @param int $quizid id of quiz
  * @param int $userid optional user id, 0 means all users
  * @return array array of grades, false if none. These are raw grades. They should
  * be processed with quiz_format_grade for display.
  */
-function quiz_get_user_grades($quiz, $userid=0) {
+function quiz_get_user_grades($quiz, $userid = 0) {
     global $CFG, $DB;
 
     $params = array($quiz->id);
-    $wheresql = '';
+    $usertest = '';
     if ($userid) {
         $params[] = $userid;
-        $wheresql = "AND u.id = ?";
-    }
-    $sql = "SELECT u.id, u.id AS userid, g.grade AS rawgrade, g.timemodified AS dategraded, MAX(a.timefinish) AS datesubmitted
-            FROM {user} u, {quiz_grades} g, {quiz_attempts} a
-            WHERE u.id = g.userid AND g.quiz = ? AND a.quiz = g.quiz AND u.id = a.userid $wheresql
-            GROUP BY u.id, g.grade, g.timemodified";
-
-    return $DB->get_records_sql($sql, $params);
+        $usertest = 'AND u.id = ?';
+    }
+    return $DB->get_records_sql("
+            SELECT
+                u.id,
+                u.id AS userid,
+                qg.grade AS rawgrade,
+                qg.timemodified AS dategraded,
+                MAX(qa.timefinish) AS datesubmitted
+
+            FROM {user} u
+            JOIN {quiz_grades} qg ON u.id = qg.userid
+            JOIN {quiz_attempts} qa ON qa.quiz = qg.quiz AND qa.userid = u.id
+
+            WHERE qg.quiz = ?
+            $usertest
+            GROUP BY u.id, qg.grade, qg.timemodified", $params);
 }
 
 /**
@@ -550,6 +499,9 @@ function quiz_get_user_grades($quiz, $userid=0) {
  * @return float
  */
 function quiz_format_grade($quiz, $grade) {
+    if (is_null($grade)) {
+        return get_string('notyetgraded', 'quiz');
+    }
     return format_float($grade, $quiz->decimalpoints);
 }
 
@@ -561,6 +513,9 @@ function quiz_format_grade($quiz, $grade) {
  * @return float
  */
 function quiz_format_question_grade($quiz, $grade) {
+    if (empty($quiz->questiondecimalpoints)) {
+        $quiz->questiondecimalpoints = -1;
+    }
     if ($quiz->questiondecimalpoints == -1) {
         return format_float($grade, $quiz->decimalpoints);
     } else {
@@ -571,12 +526,10 @@ function quiz_format_question_grade($quiz, $grade) {
 /**
  * Update grades in central gradebook
  *
- * @global stdClass
- * @global object
- * @param object $quiz
- * @param int $userid specific user only, 0 means all
+ * @param object $quiz the quiz settings.
+ * @param int $userid specific user only, 0 means all users.
  */
-function quiz_update_grades($quiz, $userid=0, $nullifnone=true) {
+function quiz_update_grades($quiz, $userid = 0, $nullifnone = true) {
     global $CFG, $DB;
     require_once($CFG->libdir.'/gradelib.php');
 
@@ -586,10 +539,10 @@ function quiz_update_grades($quiz, $userid=0, $nullifnone=true) {
     } else if ($grades = quiz_get_user_grades($quiz, $userid)) {
         quiz_grade_item_update($quiz, $grades);
 
-    } else if ($userid and $nullifnone) {
+    } else if ($userid && $nullifnone) {
         $grade = new stdClass();
-        $grade->userid   = $userid;
-        $grade->rawgrade = NULL;
+        $grade->userid = $userid;
+        $grade->rawgrade = null;
         quiz_grade_item_update($quiz, $grade);
 
     } else {
@@ -599,8 +552,6 @@ function quiz_update_grades($quiz, $userid=0, $nullifnone=true) {
 
 /**
  * Update all grades in gradebook.
- *
- * @global object
  */
 function quiz_upgrade_grades() {
     global $DB;
@@ -630,28 +581,18 @@ function quiz_upgrade_grades() {
 /**
  * Create grade item for given quiz
  *
- * @global stdClass
- * @uses GRADE_TYPE_VALUE
- * @uses GRADE_TYPE_NONE
- * @uses QUIZ_REVIEW_SCORES
- * @uses QUIZ_REVIEW_CLOSED
- * @uses QUIZ_REVIEW_OPEN
- * @uses PARAM_INT
- * @uses GRADE_UPDATE_ITEM_LOCKED
  * @param object $quiz object with extra cmidnumber
  * @param mixed $grades optional array/object of grade(s); 'reset' means reset grades in gradebook
  * @return int 0 if ok, error code otherwise
  */
-function quiz_grade_item_update($quiz, $grades=NULL) {
+function quiz_grade_item_update($quiz, $grades = null) {
     global $CFG, $OUTPUT;
-    if (!function_exists('grade_update')) { //workaround for buggy PHP versions
-        require_once($CFG->libdir.'/gradelib.php');
-    }
+    require_once($CFG->libdir.'/gradelib.php');
 
-    if (array_key_exists('cmidnumber', $quiz)) { //it may not be always present
-        $params = array('itemname'=>$quiz->name, 'idnumber'=>$quiz->cmidnumber);
+    if (array_key_exists('cmidnumber', $quiz)) { // may not be always present
+        $params = array('itemname' => $quiz->name, 'idnumber' => $quiz->cmidnumber);
     } else {
-        $params = array('itemname'=>$quiz->name);
+        $params = array('itemname' => $quiz->name);
     }
 
     if ($quiz->grade > 0) {
@@ -663,18 +604,23 @@ function quiz_grade_item_update($quiz, $grades=NULL) {
         $params['gradetype'] = GRADE_TYPE_NONE;
     }
 
-/* description by TJ:
-1/ If the quiz is set to not show scores while the quiz is still open, and is set to show scores after
-   the quiz is closed, then create the grade_item with a show-after date that is the quiz close date.
-2/ If the quiz is set to not show scores at either of those times, create the grade_item as hidden.
-3/ If the quiz is set to show scores, create the grade_item visible.
-*/
-    if (!($quiz->review & QUIZ_REVIEW_SCORES & QUIZ_REVIEW_CLOSED)
-    and !($quiz->review & QUIZ_REVIEW_SCORES & QUIZ_REVIEW_OPEN)) {
+    // description by TJ:
+    // 1. If the quiz is set to not show grades while the quiz is still open,
+    //    and is set to show grades after the quiz is closed, then create the
+    //    grade_item with a show-after date that is the quiz close date.
+    // 2. If the quiz is set to not show grades at either of those times,
+    //    create the grade_item as hidden.
+    // 3. If the quiz is set to show grades, create the grade_item visible.
+    $openreviewoptions = mod_quiz_display_options::make_from_quiz($quiz,
+            mod_quiz_display_options::LATER_WHILE_OPEN);
+    $closedreviewoptions = mod_quiz_display_options::make_from_quiz($quiz,
+            mod_quiz_display_options::AFTER_CLOSE);
+    if ($openreviewoptions->marks < question_display_options::MARK_AND_MAX &&
+            $closedreviewoptions->marks < question_display_options::MARK_AND_MAX) {
         $params['hidden'] = 1;
 
-    } else if ( ($quiz->review & QUIZ_REVIEW_SCORES & QUIZ_REVIEW_CLOSED)
-           and !($quiz->review & QUIZ_REVIEW_SCORES & QUIZ_REVIEW_OPEN)) {
+    } else if ($openreviewoptions->marks < question_display_options::MARK_AND_MAX &&
+            $closedreviewoptions->marks >= question_display_options::MARK_AND_MAX) {
         if ($quiz->timeclose) {
             $params['hidden'] = $quiz->timeclose;
         } else {
@@ -683,13 +629,14 @@ function quiz_grade_item_update($quiz, $grades=NULL) {
 
     } else {
         // a) both open and closed enabled
-        // b) open enabled, closed disabled - we can not "hide after", grades are kept visible even after closing
+        // b) open enabled, closed disabled - we can not "hide after",
+        //    grades are kept visible even after closing
         $params['hidden'] = 0;
     }
 
     if ($grades  === 'reset') {
         $params['reset'] = true;
-        $grades = NULL;
+        $grades = null;
     }
 
     $gradebook_grades = grade_get_grades($quiz->course, 'mod', 'quiz', $quiz->id);
@@ -699,7 +646,8 @@ function quiz_grade_item_update($quiz, $grades=NULL) {
             $confirm_regrade = optional_param('confirm_regrade', 0, PARAM_INT);
             if (!$confirm_regrade) {
                 $message = get_string('gradeitemislocked', 'grades');
-                $back_link = $CFG->wwwroot . '/mod/quiz/report.php?q=' . $quiz->id . '&amp;mode=overview';
+                $back_link = $CFG->wwwroot . '/mod/quiz/report.php?q=' . $quiz->id .
+                        '&amp;mode=overview';
                 $regrade_link = qualified_me() . '&amp;confirm_regrade=1';
                 echo $OUTPUT->box_start('generalbox', 'notice');
                 echo '<p>'. $message .'</p>';
@@ -720,7 +668,6 @@ function quiz_grade_item_update($quiz, $grades=NULL) {
 /**
  * Delete grade item for given quiz
  *
- * @global stdClass
  * @param object $quiz object
  * @return object quiz
  */
@@ -728,18 +675,8 @@ function quiz_grade_item_delete($quiz) {
     global $CFG;
     require_once($CFG->libdir . '/gradelib.php');
 
-    return grade_update('mod/quiz', $quiz->course, 'mod', 'quiz', $quiz->id, 0, NULL, array('deleted' => 1));
-}
-
-/**
- * @return the options for calculating the quiz grade from the individual attempt grades.
- */
-function quiz_get_grading_options() {
-    return array (
-            QUIZ_GRADEHIGHEST => get_string('gradehighest', 'quiz'),
-            QUIZ_GRADEAVERAGE => get_string('gradeaverage', 'quiz'),
-            QUIZ_ATTEMPTFIRST => get_string('attemptfirst', 'quiz'),
-            QUIZ_ATTEMPTLAST  => get_string('attemptlast', 'quiz'));
+    return grade_update('mod/quiz', $quiz->course, 'mod', 'quiz', $quiz->id, 0,
+            null, array('deleted' => 1));
 }
 
 /**
@@ -747,22 +684,16 @@ function quiz_get_grading_options() {
  *
  * @todo: deprecated - to be deleted in 2.2
  *
- * @param int $quizid
- * @return array
+ * @param int $quizid the quiz id.
+ * @return array of userids.
  */
 function quiz_get_participants($quizid) {
     global $CFG, $DB;
 
-    //Get users from attempts
-    $us_attempts = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
-                                    FROM {user} u,
-                                         {quiz_attempts} a
-                                    WHERE a.quiz = ? and
-                                          u.id = a.userid", array($quizid));
-
-    //Return us_attempts array (it contains an array of unique users)
-    return $us_attempts;
-
+    return $DB->get_records_sql('
+            SELECT DISTINCT userid, userid
+            JOIN {quiz_attempts} qa
+            WHERE a.quiz = ?', array($quizid));
 }
 
 /**
@@ -772,8 +703,6 @@ function quiz_get_participants($quizid) {
  * only quiz events belonging to the course specified are checked.
  * This function is used, in its new format, by restore_refresh_events()
  *
- * @global object
- * @uses QUIZ_MAX_EVENT_LENGTH
  * @param int $courseid
  * @return bool
  */
@@ -781,11 +710,11 @@ function quiz_refresh_events($courseid = 0) {
     global $DB;
 
     if ($courseid == 0) {
-        if (! $quizzes = $DB->get_records('quiz')) {
+        if (!$quizzes = $DB->get_records('quiz')) {
             return true;
         }
     } else {
-        if (! $quizzes = $DB->get_records('quiz', array('course' => $courseid))) {
+        if (!$quizzes = $DB->get_records('quiz', array('course' => $courseid))) {
             return true;
         }
     }
@@ -857,11 +786,12 @@ function quiz_get_recent_mod_activity(&$activities, &$index, $timestart,
     $groupmode       = groups_get_activity_groupmode($cm, $course);
 
     if (is_null($modinfo->groups)) {
-        $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
+        // load all my groups and cache it in modinfo
+        $modinfo->groups = groups_get_user_groups($course->id);
     }
 
     $usersgroups = null;
-    $aname = format_string($cm->name,true);
+    $aname = format_string($cm->name, true);
     foreach ($attempts as $attempt) {
         if ($attempt->userid != $USER->id) {
             if (!$grader) {
@@ -885,9 +815,9 @@ function quiz_get_recent_mod_activity(&$activities, &$index, $timestart,
             }
         }
 
-        $options = quiz_get_reviewoptions($quiz, $attempt, $context);
+        $options = quiz_get_review_options($quiz, $attempt, $context);
 
-        $tmpactivity = new stdClass;
+        $tmpactivity = new stdClass();
 
         $tmpactivity->type       = 'quiz';
         $tmpactivity->cmid       = $cm->id;
@@ -897,7 +827,7 @@ function quiz_get_recent_mod_activity(&$activities, &$index, $timestart,
 
         $tmpactivity->content->attemptid = $attempt->id;
         $tmpactivity->content->attempt   = $attempt->attempt;
-        if (quiz_has_grades($quiz) && $options->scores) {
+        if (quiz_has_grades($quiz) && $options->marks >= question_display_options::MARK_AND_MAX) {
             $tmpactivity->content->sumgrades = quiz_format_grade($quiz, $attempt->sumgrades);
             $tmpactivity->content->maxgrade  = quiz_format_grade($quiz, $quiz->sumgrades);
         } else {
@@ -907,16 +837,14 @@ function quiz_get_recent_mod_activity(&$activities, &$index, $timestart,
 
         $tmpactivity->user->id        = $attempt->userid;
         $tmpactivity->user->firstname = $attempt->firstname;
-        $tmpactivity->user->lastname = $attempt->lastname;
-        $tmpactivity->user->fullname = fullname($attempt, $viewfullnames);
-        $tmpactivity->user->picture  = $attempt->picture;
-        $tmpactivity->user->imagealt = $attempt->imagealt;
-        $tmpactivity->user->email = $attempt->email;
+        $tmpactivity->user->lastname  = $attempt->lastname;
+        $tmpactivity->user->fullname  = fullname($attempt, $viewfullnames);
+        $tmpactivity->user->picture   = $attempt->picture;
+        $tmpactivity->user->imagealt  = $attempt->imagealt;
+        $tmpactivity->user->email     = $attempt->email;
 
         $activities[$index++] = $tmpactivity;
     }
-
-  return;
 }
 
 function quiz_print_recent_mod_activity($activity, $courseid, $detail, $modnames) {
@@ -962,21 +890,13 @@ function quiz_print_recent_mod_activity($activity, $courseid, $detail, $modnames
  * Pre-process the quiz options form data, making any necessary adjustments.
  * Called by add/update instance in this file.
  *
- * @uses QUIZ_REVIEW_OVERALLFEEDBACK
- * @uses QUIZ_REVIEW_CLOSED
- * @uses QUIZ_REVIEW_OPEN
- * @uses QUIZ_REVIEW_IMMEDIATELY
- * @uses QUIZ_REVIEW_GENERALFEEDBACK
- * @uses QUIZ_REVIEW_SOLUTIONS
- * @uses QUIZ_REVIEW_ANSWERS
- * @uses QUIZ_REVIEW_FEEDBACK
- * @uses QUIZ_REVIEW_SCORES
- * @uses QUIZ_REVIEW_RESPONSES
- * @uses QUESTION_ADAPTIVE
  * @param object $quiz The variables set on the form.
- * @return string
  */
-function quiz_process_options(&$quiz) {
+function quiz_process_options($quiz) {
+    global $CFG;
+    require_once($CFG->dirroot . '/mod/quiz/locallib.php');
+    require_once($CFG->libdir . '/questionlib.php');
+
     $quiz->timemodified = time();
 
     // Quiz name.
@@ -1028,131 +948,66 @@ function quiz_process_options(&$quiz) {
         // Check there is nothing in the remaining unused fields.
         if (!empty($quiz->feedbackboundaries)) {
             for ($i = $numboundaries; $i < count($quiz->feedbackboundaries); $i += 1) {
-                if (!empty($quiz->feedbackboundaries[$i]) && trim($quiz->feedbackboundaries[$i]) != '') {
+                if (!empty($quiz->feedbackboundaries[$i]) &&
+                        trim($quiz->feedbackboundaries[$i]) != '') {
                     return get_string('feedbackerrorjunkinboundary', 'quiz', $i + 1);
                 }
             }
         }
         for ($i = $numboundaries + 1; $i < count($quiz->feedbacktext); $i += 1) {
-            if (!empty($quiz->feedbacktext[$i]['text']) && trim($quiz->feedbacktext[$i]['text']) != '') {
+            if (!empty($quiz->feedbacktext[$i]['text']) &&
+                    trim($quiz->feedbacktext[$i]['text']) != '') {
                 return get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1);
             }
         }
-        $quiz->feedbackboundaries[-1] = $quiz->grade + 1; // Needs to be bigger than $quiz->grade because of '<' test in quiz_feedback_for_grade().
+        // Needs to be bigger than $quiz->grade because of '<' test in quiz_feedback_for_grade().
+        $quiz->feedbackboundaries[-1] = $quiz->grade + 1;
         $quiz->feedbackboundaries[$numboundaries] = 0;
         $quiz->feedbackboundarycount = $numboundaries;
     }
 
-    // Settings that get combined to go into the optionflags column.
-    $quiz->optionflags = 0;
-    if (!empty($quiz->adaptive)) {
-        $quiz->optionflags |= QUESTION_ADAPTIVE;
-    }
-
-    // Settings that get combined to go into the review column.
-    $review = 0;
-    if (isset($quiz->responsesimmediately)) {
-        $review += (QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_IMMEDIATELY);
-        unset($quiz->responsesimmediately);
-    }
-    if (isset($quiz->responsesopen)) {
-        $review += (QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_OPEN);
-        unset($quiz->responsesopen);
-    }
-    if (isset($quiz->responsesclosed)) {
-        $review += (QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_CLOSED);
-        unset($quiz->responsesclosed);
-    }
-
-    if (isset($quiz->scoreimmediately)) {
-        $review += (QUIZ_REVIEW_SCORES & QUIZ_REVIEW_IMMEDIATELY);
-        unset($quiz->scoreimmediately);
-    }
-    if (isset($quiz->scoreopen)) {
-        $review += (QUIZ_REVIEW_SCORES & QUIZ_REVIEW_OPEN);
-        unset($quiz->scoreopen);
-    }
-    if (isset($quiz->scoreclosed)) {
-        $review += (QUIZ_REVIEW_SCORES & QUIZ_REVIEW_CLOSED);
-        unset($quiz->scoreclosed);
-    }
-
-    if (isset($quiz->feedbackimmediately)) {
-        $review += (QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_IMMEDIATELY);
-        unset($quiz->feedbackimmediately);
-    }
-    if (isset($quiz->feedbackopen)) {
-        $review += (QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_OPEN);
-        unset($quiz->feedbackopen);
-    }
-    if (isset($quiz->feedbackclosed)) {
-        $review += (QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_CLOSED);
-        unset($quiz->feedbackclosed);
-    }
-
-    if (isset($quiz->answersimmediately)) {
-        $review += (QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_IMMEDIATELY);
-        unset($quiz->answersimmediately);
-    }
-    if (isset($quiz->answersopen)) {
-        $review += (QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_OPEN);
-        unset($quiz->answersopen);
-    }
-    if (isset($quiz->answersclosed)) {
-        $review += (QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_CLOSED);
-        unset($quiz->answersclosed);
-    }
-
-    if (isset($quiz->solutionsimmediately)) {
-        $review += (QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_IMMEDIATELY);
-        unset($quiz->solutionsimmediately);
-    }
-    if (isset($quiz->solutionsopen)) {
-        $review += (QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_OPEN);
-        unset($quiz->solutionsopen);
-    }
-    if (isset($quiz->solutionsclosed)) {
-        $review += (QUIZ_REVIEW_SOLUTIONS & QUIZ_REVIEW_CLOSED);
-        unset($quiz->solutionsclosed);
-    }
+    // Combing the individual settings into the review columns.
+    $quiz->reviewattempt = quiz_review_option_form_to_db($quiz, 'attempt');
+    $quiz->reviewcorrectness = quiz_review_option_form_to_db($quiz, 'correctness');
+    $quiz->reviewmarks = quiz_review_option_form_to_db($quiz, 'marks');
+    $quiz->reviewspecificfeedback = quiz_review_option_form_to_db($quiz, 'specificfeedback');
+    $quiz->reviewgeneralfeedback = quiz_review_option_form_to_db($quiz, 'generalfeedback');
+    $quiz->reviewrightanswer = quiz_review_option_form_to_db($quiz, 'rightanswer');
+    $quiz->reviewoverallfeedback = quiz_review_option_form_to_db($quiz, 'overallfeedback');
+    $quiz->reviewattempt |= mod_quiz_display_options::DURING;
+    $quiz->reviewoverallfeedback &= ~mod_quiz_display_options::DURING;
+}
 
-    if (isset($quiz->generalfeedbackimmediately)) {
-        $review += (QUIZ_REVIEW_GENERALFEEDBACK & QUIZ_REVIEW_IMMEDIATELY);
-        unset($quiz->generalfeedbackimmediately);
-    }
-    if (isset($quiz->generalfeedbackopen)) {
-        $review += (QUIZ_REVIEW_GENERALFEEDBACK & QUIZ_REVIEW_OPEN);
-        unset($quiz->generalfeedbackopen);
-    }
-    if (isset($quiz->generalfeedbackclosed)) {
-        $review += (QUIZ_REVIEW_GENERALFEEDBACK & QUIZ_REVIEW_CLOSED);
-        unset($quiz->generalfeedbackclosed);
-    }
+/**
+ * Helper function for {@link quiz_process_options()}.
+ * @param object $fromform the sumbitted form date.
+ * @param string $field one of the review option field names.
+ */
+function quiz_review_option_form_to_db($fromform, $field) {
+    static $times = array(
+        'during' => mod_quiz_display_options::DURING,
+        'immediately' => mod_quiz_display_options::IMMEDIATELY_AFTER,
+        'open' => mod_quiz_display_options::LATER_WHILE_OPEN,
+        'closed' => mod_quiz_display_options::AFTER_CLOSE,
+    );
 
-    if (isset($quiz->overallfeedbackimmediately)) {
-        $review += (QUIZ_REVIEW_OVERALLFEEDBACK & QUIZ_REVIEW_IMMEDIATELY);
-        unset($quiz->overallfeedbackimmediately);
-    }
-    if (isset($quiz->overallfeedbackopen)) {
-        $review += (QUIZ_REVIEW_OVERALLFEEDBACK & QUIZ_REVIEW_OPEN);
-        unset($quiz->overallfeedbackopen);
-    }
-    if (isset($quiz->overallfeedbackclosed)) {
-        $review += (QUIZ_REVIEW_OVERALLFEEDBACK & QUIZ_REVIEW_CLOSED);
-        unset($quiz->overallfeedbackclosed);
+    $review = 0;
+    foreach ($times as $whenname => $when) {
+        $fieldname = $field . $whenname;
+        if (isset($fromform->$fieldname)) {
+            $review |= $when;
+            unset($fromform->$fieldname);
+        }
     }
 
-    $quiz->review = $review;
+    return $review;
 }
 
 /**
  * This function is called at the end of quiz_add_instance
  * and quiz_update_instance, to do the common processing.
  *
- * @global object
- * @uses QUIZ_MAX_EVENT_LENGTH
  * @param object $quiz the quiz object.
- * @return void|string Void or error message
  */
 function quiz_after_add_or_update($quiz) {
     global $DB;
@@ -1166,15 +1021,19 @@ function quiz_after_add_or_update($quiz) {
     $DB->delete_records('quiz_feedback', array('quizid' => $quiz->id));
 
     for ($i = 0; $i <= $quiz->feedbackboundarycount; $i++) {
-        $feedback = new stdClass;
+        $feedback = new stdClass();
         $feedback->quizid = $quiz->id;
         $feedback->feedbacktext = $quiz->feedbacktext[$i]['text'];
         $feedback->feedbacktextformat = $quiz->feedbacktext[$i]['format'];
         $feedback->mingrade = $quiz->feedbackboundaries[$i];
         $feedback->maxgrade = $quiz->feedbackboundaries[$i - 1];
         $feedback->id = $DB->insert_record('quiz_feedback', $feedback);
-        $feedbacktext = file_save_draft_area_files((int)$quiz->feedbacktext[$i]['itemid'], $context->id, 'mod_quiz', 'feedback', $feedback->id, array('subdirs'=>false, 'maxfiles'=>-1, 'maxbytes'=>0), $quiz->feedbacktext[$i]['text']);
-        $DB->set_field('quiz_feedback', 'feedbacktext', $feedbacktext, array('id'=>$feedback->id));
+        $feedbacktext = file_save_draft_area_files((int)$quiz->feedbacktext[$i]['itemid'],
+                $context->id, 'mod_quiz', 'feedback', $feedback->id,
+                array('subdirs' => false, 'maxfiles' => -1, 'maxbytes' => 0),
+                $quiz->feedbacktext[$i]['text']);
+        $DB->set_field('quiz_feedback', 'feedbacktext', $feedbacktext,
+                array('id' => $feedback->id));
     }
 
     // Update the events relating to this quiz.
@@ -1182,7 +1041,6 @@ function quiz_after_add_or_update($quiz) {
 
     //update related grade item
     quiz_grade_item_update($quiz);
-
 }
 
 /**
@@ -1213,9 +1071,8 @@ function quiz_update_events($quiz, $override = null) {
         // need to add all the overrides
         $overrides = $DB->get_records('quiz_overrides', array('quiz' => $quiz->id));
         // as well as the original quiz (empty override)
-        $overrides[] = new stdClass;
-    }
-    else {
+        $overrides[] = new stdClass();
+    } else {
         // Just do the one override
         $overrides = array($override);
     }
@@ -1230,9 +1087,10 @@ function quiz_update_events($quiz, $override = null) {
         $addopen  = empty($current->id) || !empty($current->timeopen);
         $addclose = empty($current->id) || !empty($current->timeclose);
 
-        $event = new stdClass;
+        $event = new stdClass();
         $event->description = $quiz->intro;
-        $event->courseid    = ($userid) ? 0 : $quiz->course; // Events module won't show user events when the courseid is nonzero
+        // Events module won't show user events when the courseid is nonzero
+        $event->courseid    = ($userid) ? 0 : $quiz->course;
         $event->groupid     = $groupid;
         $event->userid      = $userid;
         $event->modulename  = 'quiz';
@@ -1244,7 +1102,7 @@ function quiz_update_events($quiz, $override = null) {
 
         // Determine the event name
         if ($groupid) {
-            $params = new stdClass;
+            $params = new stdClass();
             $params->quiz = $quiz->name;
             $params->group = groups_get_group_name($groupid);
             if ($params->group === false) {
@@ -1252,9 +1110,8 @@ function quiz_update_events($quiz, $override = null) {
                 continue;
             }
             $eventname = get_string('overridegroupeventname', 'quiz', $params);
-        }
-        else if ($userid) {
-            $params = new stdClass;
+        } else if ($userid) {
+            $params = new stdClass();
             $params->quiz = $quiz->name;
             $eventname = get_string('overrideusereventname', 'quiz', $params);
         } else {
@@ -1265,8 +1122,7 @@ function quiz_update_events($quiz, $override = null) {
                 // Single event for the whole quiz.
                 if ($oldevent = array_shift($oldevents)) {
                     $event->id = $oldevent->id;
-                }
-                else {
+                } else {
                     unset($event->id);
                 }
                 $event->name = $eventname;
@@ -1278,8 +1134,7 @@ function quiz_update_events($quiz, $override = null) {
                 if ($timeopen && $addopen) {
                     if ($oldevent = array_shift($oldevents)) {
                         $event->id = $oldevent->id;
-                    }
-                    else {
+                    } else {
                         unset($event->id);
                     }
                     $event->name = $eventname.' ('.get_string('quizopens', 'quiz').')';
@@ -1289,8 +1144,7 @@ function quiz_update_events($quiz, $override = null) {
                 if ($timeclose && $addclose) {
                     if ($oldevent = array_shift($oldevents)) {
                         $event->id = $oldevent->id;
-                    }
-                    else {
+                    } else {
                         unset($event->id);
                     }
                     $event->name      = $eventname.' ('.get_string('quizcloses', 'quiz').')';
@@ -1320,70 +1174,62 @@ function quiz_get_view_actions() {
  * @return array
  */
 function quiz_get_post_actions() {
-    return array('attempt', 'close attempt', 'preview', 'editquestions', 'delete attempt', 'manualgrade');
+    return array('attempt', 'close attempt', 'preview', 'editquestions',
+            'delete attempt', 'manualgrade');
 }
 
 /**
- * Returns an array of names of quizzes that use this question
- *
- * @param integer $questionid
- * @return array of strings
+ * @param array $questionids of question ids.
+ * @return bool whether any of these questions are used by any instance of this module.
  */
-function quiz_question_list_instances($questionid) {
-    global $CFG, $DB;
-
-    // TODO MDL-5780: we should also consider other questions that are used by
-    // random questions in this quiz, but that is very hard.
-
-    $sql = "SELECT q.id, q.name
-            FROM {quiz} q
-            JOIN {quiz_question_instances} qqi ON q.id = qqi.quiz
-            WHERE qqi.question = ?";
-
-    if ($instances = $DB->get_records_sql_menu($sql, array($questionid))) {
-        return $instances;
-    }
-    return array();
+function quiz_questions_in_use($questionids) {
+    global $DB, $CFG;
+    require_once($CFG->libdir . '/questionlib.php');
+    list($test, $params) = $DB->get_in_or_equal($questionids);
+    return $DB->record_exists_select('quiz_question_instances',
+            'question ' . $test, $params) || question_engine::questions_in_use(
+            $questionids, new qubaid_join('{quiz_attempts} quiza',
+            'quiza.uniqueid', 'quiza.preview = 0'));
 }
 
 /**
  * Implementation of the function for printing the form elements that control
  * whether the course reset functionality affects the quiz.
  *
- * @param $mform form passed by reference
+ * @param $mform the course reset form that is being built.
  */
-function quiz_reset_course_form_definition(&$mform) {
+function quiz_reset_course_form_definition($mform) {
     $mform->addElement('header', 'quizheader', get_string('modulenameplural', 'quiz'));
-    $mform->addElement('advcheckbox', 'reset_quiz_attempts', get_string('removeallquizattempts','quiz'));
+    $mform->addElement('advcheckbox', 'reset_quiz_attempts',
+            get_string('removeallquizattempts', 'quiz'));
 }
 
 /**
  * Course reset form defaults.
- * @return array
+ * @return array the defaults.
  */
 function quiz_reset_course_form_defaults($course) {
-    return array('reset_quiz_attempts'=>1);
+    return array('reset_quiz_attempts' => 1);
 }
 
 /**
  * Removes all grades from gradebook
  *
- * @global stdClass
- * @global object
  * @param int $courseid
  * @param string optional type
  */
 function quiz_reset_gradebook($courseid, $type='') {
     global $CFG, $DB;
 
-    $sql = "SELECT q.*, cm.idnumber as cmidnumber, q.course as courseid
-              FROM {quiz} q, {course_modules} cm, {modules} m
-             WHERE m.name='quiz' AND m.id=cm.module AND cm.instance=q.id AND q.course=?";
+    $quizzes = $DB->get_records_sql("
+            SELECT q.*, cm.idnumber as cmidnumber, q.course as courseid
+            FROM {modules} m
+            JOIN {course_modules} cm ON m.id = cm.module
+            JOIN {quiz} q ON cm.instance = q.id
+            WHERE m.name = 'quiz' AND cm.course = ?", array($courseid));
 
-    if ($quizs = $DB->get_records_sql($sql, array($courseid))) {
-        foreach ($quizs as $quiz) {
-            quiz_grade_item_update($quiz, 'reset');
-        }
+    foreach ($quizzes as $quiz) {
+        quiz_grade_item_update($quiz, 'reset');
     }
 }
 
@@ -1394,8 +1240,6 @@ function quiz_reset_gradebook($courseid, $type='') {
  *
  * Also, move the quiz open and close dates, if the course start date is changing.
  *
- * @global stdClass
- * @global object
  * @param object $data the data submitted from the reset course.
  * @return array status array
  */
@@ -1406,24 +1250,40 @@ function quiz_reset_userdata($data) {
     $componentstr = get_string('modulenameplural', 'quiz');
     $status = array();
 
-    /// Delete attempts.
+    // Delete attempts.
     if (!empty($data->reset_quiz_attempts)) {
-        $quizzes = $DB->get_records('quiz', array('course' => $data->courseid));
-        foreach ($quizzes as $quiz) {
-            quiz_delete_all_attempts($quiz);
-        }
+        require_once($CFG->libdir . '/questionlib.php');
+
+        question_engine::delete_questions_usage_by_activities(new qubaid_join(
+                '{quiz_attempts} quiza JOIN {quiz} quiz ON quiza.quiz = quiz.id',
+                'quiza.uniqueid', 'quiz.course = :quizcourseid',
+                array('quizcourseid' => $data->courseid)));
+
+        $DB->delete_records_select('quiz_attempts',
+                'quiz IN (SELECT id FROM {quiz} WHERE course = ?)', array($data->courseid));
+        $status[] = array(
+            'component' => $componentstr,
+            'item' => get_string('attemptsdeleted', 'quiz'),
+            'error' => false);
 
-        // remove all grades from gradebook
+        // Remove all grades from gradebook
         if (empty($data->reset_gradebook_grades)) {
             quiz_reset_gradebook($data->courseid);
         }
-        $status[] = array('component' => $componentstr, 'item' => get_string('attemptsdeleted', 'quiz'), 'error' => false);
+        $status[] = array(
+            'component' => $componentstr,
+            'item' => get_string('attemptsdeleted', 'quiz'),
+            'error' => false);
     }
 
-    /// updating dates - shift may be negative too
+    // Updating dates - shift may be negative too
     if ($data->timeshift) {
-        shift_course_mod_dates('quiz', array('timeopen', 'timeclose'), $data->timeshift, $data->courseid);
-        $status[] = array('component' => $componentstr, 'item' => get_string('openclosedatesupdated', 'quiz'), 'error' => false);
+        shift_course_mod_dates('quiz', array('timeopen', 'timeclose'),
+                $data->timeshift, $data->courseid);
+        $status[] = array(
+            'component' => $componentstr,
+            'item' => get_string('openclosedatesupdated', 'quiz'),
+            'error' => false);
     }
 
     return $status;
@@ -1433,12 +1293,9 @@ function quiz_reset_userdata($data) {
  * Checks whether the current user is allowed to view a file uploaded in a quiz.
  * Teachers can view any from their courses, students can only view their own.
  *
- * @global object
- * @global object
- * @uses CONTEXT_COURSE
  * @param int $attemptuniqueid int attempt id
  * @param int $questionid int question id
- * @return boolean to indicate access granted or denied
+ * @return bool to indicate access granted or denied
  */
 function quiz_check_file_access($attemptuniqueid, $questionid, $context = null) {
     global $USER, $DB, $CFG;
@@ -1471,9 +1328,10 @@ function quiz_check_file_access($attemptuniqueid, $questionid, $context = null)
     // access granted if the current user submitted this file
     if ($attempt->userid != $USER->id) {
         return false;
-    // access granted if the current user has permission to grade quizzes in this course
     }
-    if (!(has_capability('mod/quiz:viewreports', $context) || has_capability('mod/quiz:grade', $context))) {
+    // access granted if the current user has permission to grade quizzes in this course
+    if (!(has_capability('mod/quiz:viewreports', $context) ||
+            has_capability('mod/quiz:grade', $context))) {
         return false;
     }
 
@@ -1482,15 +1340,12 @@ function quiz_check_file_access($attemptuniqueid, $questionid, $context = null)
 
 /**
  * Prints quiz summaries on MyMoodle Page
- *
- * @global object
- * @global object
  * @param arry $courses
  * @param array $htmlarray
  */
 function quiz_print_overview($courses, &$htmlarray) {
     global $USER, $CFG;
-/// These next 6 Lines are constant in all modules (just change module name)
+    // These next 6 Lines are constant in all modules (just change module name)
     if (empty($courses) || !is_array($courses) || count($courses) == 0) {
         return array();
     }
@@ -1499,43 +1354,50 @@ function quiz_print_overview($courses, &$htmlarray) {
         return;
     }
 
-/// Fetch some language strings outside the main loop.
+    // Fetch some language strings outside the main loop.
     $strquiz = get_string('modulename', 'quiz');
     $strnoattempts = get_string('noattempts', 'quiz');
 
-/// We want to list quizzes that are currently available, and which have a close date.
-/// This is the same as what the lesson does, and the dabate is in MDL-10568.
+    // We want to list quizzes that are currently available, and which have a close date.
+    // This is the same as what the lesson does, and the dabate is in MDL-10568.
     $now = time();
     foreach ($quizzes as $quiz) {
         if ($quiz->timeclose >= $now && $quiz->timeopen < $now) {
-        /// Give a link to the quiz, and the deadline.
+            // Give a link to the quiz, and the deadline.
             $str = '<div class="quiz overview">' .
-                    '<div class="name">' . $strquiz . ': <a ' . ($quiz->visible ? '' : ' class="dimmed"') .
-                    ' href="' . $CFG->wwwroot . '/mod/quiz/view.php?id=' . $quiz->coursemodule . '">' .
+                    '<div class="name">' . $strquiz . ': <a ' .
+                    ($quiz->visible ? '' : ' class="dimmed"') .
+                    ' href="' . $CFG->wwwroot . '/mod/quiz/view.php?id=' .
+                    $quiz->coursemodule . '">' .
                     $quiz->name . '</a></div>';
-            $str .= '<div class="info">' . get_string('quizcloseson', 'quiz', userdate($quiz->timeclose)) . '</div>';
+            $str .= '<div class="info">' . get_string('quizcloseson', 'quiz',
+                    userdate($quiz->timeclose)) . '</div>';
 
-        /// Now provide more information depending on the uers's role.
+            // Now provide more information depending on the uers's role.
             $context = get_context_instance(CONTEXT_MODULE, $quiz->coursemodule);
             if (has_capability('mod/quiz:viewreports', $context)) {
-            /// For teacher-like people, show a summary of the number of student attempts.
+                // For teacher-like people, show a summary of the number of student attempts.
                 // The $quiz objects returned by get_all_instances_in_course have the necessary $cm
                 // fields set to make the following call work.
-                $str .= '<div class="info">' . quiz_num_attempt_summary($quiz, $quiz, true) . '</div>';
-            } else if (has_any_capability(array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), $context)) { // Student
-            /// For student-like people, tell them how many attempts they have made.
-                if (isset($USER->id) && ($attempts = quiz_get_user_attempts($quiz->id, $USER->id))) {
+                $str .= '<div class="info">' .
+                        quiz_num_attempt_summary($quiz, $quiz, true) . '</div>';
+            } else if (has_any_capability(array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),
+                    $context)) { // Student
+                // For student-like people, tell them how many attempts they have made.
+                if (isset($USER->id) &&
+                        ($attempts = quiz_get_user_attempts($quiz->id, $USER->id))) {
                     $numattempts = count($attempts);
-                    $str .= '<div class="info">' . get_string('numattemptsmade', 'quiz', $numattempts) . '</div>';
+                    $str .= '<div class="info">' .
+                            get_string('numattemptsmade', 'quiz', $numattempts) . '</div>';
                 } else {
                     $str .= '<div class="info">' . $strnoattempts . '</div>';
                 }
             } else {
-            /// For ayone else, there is no point listing this quiz, so stop processing.
+                // For ayone else, there is no point listing this quiz, so stop processing.
                 continue;
             }
 
-        /// Add the output for this quiz to the rest.
+            // Add the output for this quiz to the rest.
             $str .= '</div>';
             if (empty($htmlarray[$quiz->course]['quiz'])) {
                 $htmlarray[$quiz->course]['quiz'] = $str;
@@ -1547,12 +1409,14 @@ function quiz_print_overview($courses, &$htmlarray) {
 }
 
 /**
- * Return a textual summary of the number of attemtps that have been made at a particular quiz,
- * returns '' if no attemtps have been made yet, unless $returnzero is passed as true.
+ * Return a textual summary of the number of attempts that have been made at a particular quiz,
+ * returns '' if no attempts have been made yet, unless $returnzero is passed as true.
  *
  * @param object $quiz the quiz object. Only $quiz->id is used at the moment.
- * @param object $cm the cm object. Only $cm->course, $cm->groupmode and $cm->groupingid fields are used at the moment.
- * @param boolean $returnzero if false (default), when no attempts have been made '' is returned instead of 'Attempts: 0'.
+ * @param object $cm the cm object. Only $cm->course, $cm->groupmode and
+ *      $cm->groupingid fields are used at the moment.
+ * @param bool $returnzero if false (default), when no attempts have been
+ *      made '' is returned instead of 'Attempts: 0'.
  * @param int $currentgroup if there is a concept of current group where this method is being called
  *         (e.g. a report) pass it in here. Default 0 which means no current group.
  * @return string a string like "Attempts: 123", "Attemtps 123 (45 from your groups)" or
@@ -1568,7 +1432,8 @@ function quiz_num_attempt_summary($quiz, $cm, $returnzero = false, $currentgroup
                 $a->group = $DB->count_records_sql('SELECT count(1) FROM ' .
                         '{quiz_attempts} qa JOIN ' .
                         '{groups_members} gm ON qa.userid = gm.userid ' .
-                        'WHERE quiz = ? AND preview = 0 AND groupid = ?', array($quiz->id, $currentgroup));
+                        'WHERE quiz = ? AND preview = 0 AND groupid = ?',
+                        array($quiz->id, $currentgroup));
                 return get_string('attemptsnumthisgroup', 'quiz', $a);
             } else if ($groups = groups_get_all_groups($cm->course, $USER->id, $cm->groupingid)) {
                 list($usql, $params) = $DB->get_in_or_equal(array_keys($groups));
@@ -1590,14 +1455,17 @@ function quiz_num_attempt_summary($quiz, $cm, $returnzero = false, $currentgroup
  * to the quiz reports.
  *
  * @param object $quiz the quiz object. Only $quiz->id is used at the moment.
- * @param object $cm the cm object. Only $cm->course, $cm->groupmode and $cm->groupingid fields are used at the moment.
+ * @param object $cm the cm object. Only $cm->course, $cm->groupmode and
+ *      $cm->groupingid fields are used at the moment.
  * @param object $context the quiz context.
- * @param boolean $returnzero if false (default), when no attempts have been made '' is returned instead of 'Attempts: 0'.
+ * @param bool $returnzero if false (default), when no attempts have been made
+ *      '' is returned instead of 'Attempts: 0'.
  * @param int $currentgroup if there is a concept of current group where this method is being called
  *         (e.g. a report) pass it in here. Default 0 which means no current group.
  * @return string HTML fragment for the link.
  */
-function quiz_attempt_summary_link_to_reports($quiz, $cm, $context, $returnzero = false, $currentgroup = 0) {
+function quiz_attempt_summary_link_to_reports($quiz, $cm, $context, $returnzero = false,
+        $currentgroup = 0) {
     global $CFG;
     $summary = quiz_num_attempt_summary($quiz, $cm, $returnzero, $currentgroup);
     if (!$summary) {
@@ -1630,8 +1498,6 @@ function quiz_supports($feature) {
 }
 
 /**
- * @global object
- * @global stdClass
  * @return array all other caps used in module
  */
 function quiz_get_extra_capabilities() {
@@ -1649,9 +1515,9 @@ function quiz_get_extra_capabilities() {
  * available
  *
  * @param navigation_node $quiznode The quiz node within the global navigation
- * @param stdClass $course The course object returned from the DB
- * @param stdClass $module The module object returned from the DB
- * @param stdClass $cm The course module instance returned from the DB
+ * @param object $course The course object returned from the DB
+ * @param object $module The module object returned from the DB
+ * @param object $cm The course module instance returned from the DB
  */
 function quiz_extend_navigation($quiznode, $course, $module, $cm) {
     global $CFG;
@@ -1664,17 +1530,21 @@ function quiz_extend_navigation($quiznode, $course, $module, $cm) {
                 null, null, new pix_icon('i/info', ''));
     }
 
-    if (has_capability('mod/quiz:viewreports', $context)) {
+    if (has_any_capability(array('mod/quiz:viewreports', 'mod/quiz:grade'), $context)) {
         require_once($CFG->dirroot.'/mod/quiz/report/reportlib.php');
         $reportlist = quiz_report_list($context);
 
-        $url = new moodle_url('/mod/quiz/report.php', array('id' => $cm->id, 'mode' => reset($reportlist)));
-        $reportnode = $quiznode->add(get_string('results', 'quiz'), $url, navigation_node::TYPE_SETTING,
+        $url = new moodle_url('/mod/quiz/report.php',
+                array('id' => $cm->id, 'mode' => reset($reportlist)));
+        $reportnode = $quiznode->add(get_string('results', 'quiz'), $url,
+                navigation_node::TYPE_SETTING,
                 null, null, new pix_icon('i/report', ''));
 
         foreach ($reportlist as $report) {
-            $url = new moodle_url('/mod/quiz/report.php', array('id' => $cm->id, 'mode' => $report));
-            $reportnode->add(get_string($report, 'quiz_'.$report), $url, navigation_node::TYPE_SETTING,
+            $url = new moodle_url('/mod/quiz/report.php',
+                    array('id' => $cm->id, 'mode' => $report));
+            $reportnode->add(get_string($report, 'quiz_'.$report), $url,
+                    navigation_node::TYPE_SETTING,
                     null, 'quiz_report_' . $report, new pix_icon('i/item', ''));
         }
     }
@@ -1700,9 +1570,11 @@ function quiz_extend_settings_navigation($settings, $quiznode) {
 
     if (has_capability('mod/quiz:manageoverrides', $PAGE->cm->context)) {
         $url = new moodle_url('/mod/quiz/overrides.php', array('cmid'=>$PAGE->cm->id));
-        $quiznode->add(get_string('groupoverrides', 'quiz'), new moodle_url($url, array('mode'=>'group')),
+        $quiznode->add(get_string('groupoverrides', 'quiz'),
+                new moodle_url($url, array('mode'=>'group')),
                 navigation_node::TYPE_SETTING, null, 'groupoverrides');
-        $quiznode->add(get_string('useroverrides', 'quiz'), new moodle_url($url, array('mode'=>'user')),
+        $quiznode->add(get_string('useroverrides', 'quiz'),
+                new moodle_url($url, array('mode'=>'user')),
                 navigation_node::TYPE_SETTING, null, 'useroverrides');
     }
 
@@ -1714,7 +1586,8 @@ function quiz_extend_settings_navigation($settings, $quiznode) {
     }
 
     if (has_capability('mod/quiz:preview', $PAGE->cm->context)) {
-        $url = new moodle_url('/mod/quiz/startattempt.php', array('cmid'=>$PAGE->cm->id, 'sesskey'=>sesskey()));
+        $url = new moodle_url('/mod/quiz/startattempt.php',
+                array('cmid'=>$PAGE->cm->id, 'sesskey'=>sesskey()));
         $quiznode->add(get_string('preview', 'quiz'), $url, navigation_node::TYPE_SETTING,
                 null, 'mod_quiz_preview', new pix_icon('t/preview', ''));
     }
@@ -1778,16 +1651,13 @@ function quiz_pluginfile($course, $cm, $context, $filearea, $args, $forcedownloa
  * @param bool $forcedownload whether the user must be forced to download the file.
  * @return bool false if file not found, does not return if found - justsend the file
  */
-function quiz_question_pluginfile($course, $context, $component,
-        $filearea, $uniqueid, $questionid, $args, $forcedownload) {
+function mod_quiz_question_pluginfile($course, $context, $component,
+        $filearea, $qubaid, $slot, $args, $forcedownload) {
     global $USER, $CFG;
     require_once($CFG->dirroot . '/mod/quiz/locallib.php');
 
-    $attemptobj = quiz_attempt::create_from_unique_id($uniqueid);
+    $attemptobj = quiz_attempt::create_from_usage_id($qubaid);
     require_login($attemptobj->get_courseid(), false, $attemptobj->get_cm());
-    $questionids = array($questionid);
-    $attemptobj->load_questions($questionids);
-    $attemptobj->load_question_states($questionids);
 
     if ($attemptobj->is_own_attempt() && !$attemptobj->is_finished()) {
         // In the middle of an attempt.
@@ -1802,7 +1672,7 @@ function quiz_question_pluginfile($course, $context, $component,
         $isreviewing = true;
     }
 
-    if (!$attemptobj->check_file_access($questionid, $isreviewing, $context->id,
+    if (!$attemptobj->check_file_access($slot, $isreviewing, $context->id,
             $component, $filearea, $args, $forcedownload)) {
         send_file_not_found();
     }