MDL-20636 Add missing defined('MOODLE_INTERNAL') || die();
authorTim Hunt <T.J.Hunt@open.ac.uk>
Wed, 23 Feb 2011 15:03:35 +0000 (15:03 +0000)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Wed, 23 Feb 2011 16:00:20 +0000 (16:00 +0000)
294 files changed:
mod/quiz/accessrules.php
mod/quiz/addrandomform.php
mod/quiz/attemptlib.php
mod/quiz/backup/moodle2/backup_quiz_activity_task.class.php
mod/quiz/backup/moodle2/backup_quiz_stepslib.php
mod/quiz/backup/moodle2/restore_quiz_activity_task.class.php
mod/quiz/backup/moodle2/restore_quiz_stepslib.php
mod/quiz/db/access.php
mod/quiz/db/install.php
mod/quiz/db/messages.php
mod/quiz/db/subplugins.php
mod/quiz/db/upgrade.php
mod/quiz/editlib.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/mod_form.php
mod/quiz/override_form.php
mod/quiz/report/attemptsreport.php
mod/quiz/report/default.php
mod/quiz/report/grading/db/access.php
mod/quiz/report/grading/gradingsettings_form.php
mod/quiz/report/grading/report.php
mod/quiz/report/grading/version.php
mod/quiz/report/overview/db/upgrade.php
mod/quiz/report/overview/overview_table.php
mod/quiz/report/overview/overviewsettings_form.php
mod/quiz/report/overview/report.php
mod/quiz/report/overview/version.php
mod/quiz/report/reportlib.php
mod/quiz/report/responses/report.php
mod/quiz/report/responses/responses_table.php
mod/quiz/report/responses/responsessettings_form.php
mod/quiz/report/simpletest/testreportlib.php
mod/quiz/report/statistics/cron.php
mod/quiz/report/statistics/db/access.php
mod/quiz/report/statistics/db/install.php
mod/quiz/report/statistics/db/upgrade.php
mod/quiz/report/statistics/qstats.php
mod/quiz/report/statistics/report.php
mod/quiz/report/statistics/responseanalysis.php
mod/quiz/report/statistics/simpletest/test_qstats.php
mod/quiz/report/statistics/statistics_form.php
mod/quiz/report/statistics/statistics_question_table.php
mod/quiz/report/statistics/statistics_table.php
mod/quiz/report/statistics/version.php
mod/quiz/settings.php
mod/quiz/settingslib.php
mod/quiz/simpletest/testaccessrules.php
mod/quiz/simpletest/testeditlib.php
mod/quiz/simpletest/testlib.php
mod/quiz/simpletest/testlocallib.php
mod/quiz/simpletest/testquizdisplayoptions.php
mod/quiz/version.php
question/behaviour/adaptive/behaviour.php
question/behaviour/adaptive/renderer.php
question/behaviour/adaptive/simpletest/testwalkthrough.php
question/behaviour/adaptivenopenalty/behaviour.php
question/behaviour/adaptivenopenalty/renderer.php
question/behaviour/adaptivenopenalty/simpletest/testwalkthrough.php
question/behaviour/behaviourbase.php
question/behaviour/deferredcbm/behaviour.php
question/behaviour/deferredcbm/renderer.php
question/behaviour/deferredcbm/simpletest/testwalkthrough.php
question/behaviour/deferredfeedback/behaviour.php
question/behaviour/deferredfeedback/renderer.php
question/behaviour/deferredfeedback/simpletest/testwalkthrough.php
question/behaviour/immediatecbm/behaviour.php
question/behaviour/immediatecbm/renderer.php
question/behaviour/immediatecbm/simpletest/testwalkthrough.php
question/behaviour/immediatefeedback/behaviour.php
question/behaviour/immediatefeedback/renderer.php
question/behaviour/immediatefeedback/simpletest/testwalkthrough.php
question/behaviour/informationitem/behaviour.php
question/behaviour/informationitem/renderer.php
question/behaviour/informationitem/simpletest/testwalkthrough.php
question/behaviour/interactive/behaviour.php
question/behaviour/interactive/renderer.php
question/behaviour/interactive/simpletest/testwalkthrough.php
question/behaviour/interactivecountback/behaviour.php
question/behaviour/interactivecountback/renderer.php
question/behaviour/interactivecountback/simpletest/testwalkthrough.php
question/behaviour/manualgraded/behaviour.php
question/behaviour/manualgraded/renderer.php
question/behaviour/manualgraded/simpletest/testwalkthrough.php
question/behaviour/missing/behaviour.php
question/behaviour/missing/renderer.php
question/behaviour/missing/simpletest/testmissingbehaviour.php
question/behaviour/opaque/behaviour.php
question/behaviour/opaque/renderer.php
question/behaviour/opaque/simpletest/testopaquebehaviour.php
question/behaviour/rendererbase.php
question/category_class.php
question/category_form.php
question/editlib.php
question/engine/bank.php
question/engine/datalib.php
question/engine/lib.php
question/engine/renderer.php
question/engine/simpletest/helpers.php
question/engine/simpletest/testdatalib.php
question/engine/simpletest/testquestionattempt.php
question/engine/simpletest/testquestionattemptiterator.php
question/engine/simpletest/testquestionattemptstep.php
question/engine/simpletest/testquestionattemptstepiterator.php
question/engine/simpletest/testquestioncbm.php
question/engine/simpletest/testquestionengine.php
question/engine/simpletest/testquestionstate.php
question/engine/simpletest/testquestionusagebyactivity.php
question/engine/simpletest/testquestionutils.php
question/engine/states.php
question/engine/upgradefromoldqe/upgrade.php
question/export_form.php
question/format.php
question/format/aiken/format.php
question/format/blackboard/format.php
question/format/blackboard_six/format.php
question/format/examview/format.php
question/format/gift/format.php
question/format/gift/simpletest/testgiftformat.php
question/format/learnwise/format.php
question/format/missingword/format.php
question/format/multianswer/format.php
question/format/qti_two/custommediafilter.php
question/format/qti_two/format.php
question/format/qti_two/qt_common.php
question/format/webct/format.php
question/format/xhtml/format.php
question/format/xml/format.php
question/format/xml/simpletest/testxmlformat.php
question/import_form.php
question/move_form.php
question/previewlib.php
question/simpletest/testimportexport.php
question/todo/diffstat.txt
question/todo/questionlib_2.0.diff.txt [deleted file]
question/todo/questionlib_qe.diff.txt [deleted file]
question/todo/top-level_question_changes.patch.txt [deleted file]
question/type/calculated/backup/moodle2/backup_qtype_calculated_plugin.class.php
question/type/calculated/backup/moodle2/restore_qtype_calculated_plugin.class.php
question/type/calculated/datasetdefinitions_form.php
question/type/calculated/datasetitems_form.php
question/type/calculated/db/upgrade.php
question/type/calculated/edit_calculated_form.php
question/type/calculated/lib.php
question/type/calculated/questiontype.php
question/type/calculated/version.php
question/type/calculatedmulti/backup/moodle2/backup_qtype_calculatedmulti_plugin.class.php
question/type/calculatedmulti/backup/moodle2/restore_qtype_calculatedmulti_plugin.class.php
question/type/calculatedmulti/edit_calculatedmulti_form.php
question/type/calculatedmulti/lib.php
question/type/calculatedmulti/questiontype.php
question/type/calculatedsimple/backup/moodle2/backup_qtype_calculatedsimple_plugin.class.php
question/type/calculatedsimple/backup/moodle2/restore_qtype_calculatedsimple_plugin.class.php
question/type/calculatedsimple/edit_calculatedsimple_form.php
question/type/calculatedsimple/lib.php
question/type/calculatedsimple/questiontype.php
question/type/ddwtos/edit_ddwtos_form.php
question/type/ddwtos/question.php
question/type/ddwtos/questiontype.php
question/type/ddwtos/renderer.php
question/type/ddwtos/simpletest/helper.php
question/type/ddwtos/simpletest/testquestion.php
question/type/ddwtos/simpletest/testquestiontype.php
question/type/ddwtos/simpletest/testwalkthrough.php
question/type/ddwtos/version.php
question/type/description/edit_description_form.php
question/type/description/question.php
question/type/description/questiontype.php
question/type/description/renderer.php
question/type/description/simpletest/testquestiontype.php
question/type/edit_question_form.php
question/type/essay/backup/moodle2/backup_qtype_essay_plugin.class.php
question/type/essay/backup/moodle2/restore_qtype_essay_plugin.class.php
question/type/essay/edit_essay_form.php
question/type/essay/lib.php
question/type/essay/question.php
question/type/essay/questiontype.php
question/type/essay/renderer.php
question/type/essay/simpletest/testquestion.php
question/type/essay/simpletest/testquestiontype.php
question/type/essay/version.php
question/type/gapselect/edit_form_base.php
question/type/gapselect/edit_gapselect_form.php
question/type/gapselect/question.php
question/type/gapselect/questionbase.php
question/type/gapselect/questiontype.php
question/type/gapselect/questiontypebase.php
question/type/gapselect/renderer.php
question/type/gapselect/rendererbase.php
question/type/gapselect/simpletest/helper.php
question/type/gapselect/simpletest/testquestion.php
question/type/gapselect/simpletest/testquestiontype.php
question/type/gapselect/simpletest/testwalkthrough.php
question/type/gapselect/version.php
question/type/match/backup/moodle2/backup_qtype_match_plugin.class.php
question/type/match/backup/moodle2/restore_qtype_match_plugin.class.php
question/type/match/db/upgrade.php
question/type/match/edit_match_form.php
question/type/match/lib.php
question/type/match/question.php
question/type/match/questiontype.php
question/type/match/renderer.php
question/type/match/simpletest/testquestion.php
question/type/match/simpletest/testquestiontype.php
question/type/match/simpletest/testwalkthrough.php
question/type/match/version.php
question/type/missingtype/edit_missingtype_form.php
question/type/missingtype/question.php
question/type/missingtype/questiontype.php
question/type/missingtype/renderer.php
question/type/missingtype/simpletest/testmissingtype.php
question/type/multianswer/backup/moodle2/backup_qtype_multianswer_plugin.class.php
question/type/multianswer/backup/moodle2/restore_qtype_multianswer_plugin.class.php
question/type/multianswer/db/upgrade.php
question/type/multianswer/edit_multianswer_form.php
question/type/multianswer/questiontype.php
question/type/multianswer/version.php
question/type/multichoice/backup/moodle2/backup_qtype_multichoice_plugin.class.php
question/type/multichoice/backup/moodle2/restore_qtype_multichoice_plugin.class.php
question/type/multichoice/db/upgrade.php
question/type/multichoice/edit_multichoice_form.php
question/type/multichoice/lib.php
question/type/multichoice/question.php
question/type/multichoice/questiontype.php
question/type/multichoice/renderer.php
question/type/multichoice/simpletest/testquestion.php
question/type/multichoice/simpletest/testquestiontype.php
question/type/multichoice/simpletest/testwalkthrough.php
question/type/multichoice/version.php
question/type/numerical/backup/moodle2/backup_qtype_numerical_plugin.class.php
question/type/numerical/backup/moodle2/restore_qtype_numerical_plugin.class.php
question/type/numerical/db/upgrade.php
question/type/numerical/edit_numerical_form.php
question/type/numerical/lib.php
question/type/numerical/question.php
question/type/numerical/questiontype.php
question/type/numerical/simpletest/testquestiontype.php
question/type/numerical/version.php
question/type/opaque/edit_engine_form.php
question/type/opaque/edit_opaque_form.php
question/type/opaque/editengine.php
question/type/opaque/engines.php
question/type/opaque/file.php
question/type/opaque/locallib.php
question/type/opaque/question.php
question/type/opaque/questiontype.php
question/type/opaque/renderer.php
question/type/opaque/settings.php
question/type/opaque/simpletest/testlocallib.php
question/type/opaque/simpletest/testquestiontype.php
question/type/opaque/testengine.php
question/type/opaque/version.php
question/type/oumultiresponse/edit_oumultiresponse_form.php
question/type/oumultiresponse/question.php
question/type/oumultiresponse/questiontype.php
question/type/oumultiresponse/simpletest/helper.php
question/type/oumultiresponse/simpletest/testquestion.php
question/type/oumultiresponse/simpletest/testquestiontype.php
question/type/oumultiresponse/simpletest/testwalkthrough.php
question/type/oumultiresponse/version.php
question/type/questionbase.php
question/type/questiontype.php
question/type/random/backup/moodle2/restore_qtype_random_plugin.class.php
question/type/random/edit_random_form.php
question/type/random/questiontype.php
question/type/random/simpletest/testquestiontype.php
question/type/randomsamatch/backup/moodle2/backup_qtype_randomsamatch_plugin.class.php
question/type/randomsamatch/backup/moodle2/restore_qtype_randomsamatch_plugin.class.php
question/type/randomsamatch/edit_randomsamatch_form.php
question/type/randomsamatch/questiontype.php
question/type/randomsamatch/version.php
question/type/rendererbase.php
question/type/shortanswer/backup/moodle2/backup_qtype_shortanswer_plugin.class.php
question/type/shortanswer/backup/moodle2/restore_qtype_shortanswer_plugin.class.php
question/type/shortanswer/lib.php
question/type/shortanswer/question.php
question/type/shortanswer/questiontype.php
question/type/shortanswer/renderer.php
question/type/shortanswer/simpletest/testquestion.php
question/type/shortanswer/simpletest/testquestiontype.php
question/type/shortanswer/version.php
question/type/simpletest/testquestionbase.php
question/type/simpletest/testquestiontype.php
question/type/truefalse/backup/moodle2/backup_qtype_truefalse_plugin.class.php
question/type/truefalse/backup/moodle2/restore_qtype_truefalse_plugin.class.php
question/type/truefalse/edit_truefalse_form.php
question/type/truefalse/lib.php
question/type/truefalse/question.php
question/type/truefalse/questiontype.php
question/type/truefalse/renderer.php
question/type/truefalse/simpletest/testquestion.php
question/type/truefalse/simpletest/testquestiontype.php
question/type/truefalse/version.php
question/upgrade.php

index 8063288..a9f9e74 100644 (file)
@@ -16,7 +16,7 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Sets up the tabs used by the quiz pages based on the users capabilites.
+ * Classes to enforce the various access rules that can apply to a quiz.
  *
  * @package    mod
  * @subpackage quiz
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * This class keeps track of the various access rules that apply to a particular
  * quiz, with convinient methods for seeing whether access is allowed.
index 8da771a..8619b19 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir.'/formslib.php');
 
 
index 7ef1a1d..019530f 100644 (file)
@@ -28,9 +28,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page.
-}
+defined('MOODLE_INTERNAL') || die();
 
 
 /**
index 3f286dc..e757b5d 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->dirroot . '/mod/quiz/backup/moodle2/backup_quiz_stepslib.php'); // Because it exists (must)
 
+
 /**
  * quiz backup task that provides all the settings and steps to perform one
  * complete backup of the activity
index 7a05c5a..17fed6c 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-/**
- * Define all the backup steps that will be used by the backup_quiz_activity_task
- */
+
+defined('MOODLE_INTERNAL') || die();
+
 
 /**
- * Define the complete quiz structure for backup, with file and id annotations
+ * Define all the backup steps that will be used by the backup_quiz_activity_task
  */
 class backup_quiz_activity_structure_step extends backup_questions_activity_structure_step {
 
index baedede..98759c4 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+
 defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->dirroot . '/mod/quiz/backup/moodle2/restore_quiz_stepslib.php'); // Because it exists (must)
 
+
 /**
  * quiz restore task that provides all the settings and steps to perform one
  * complete restore of the activity
index 30f8baa..3731c38 100644 (file)
@@ -22,9 +22,9 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-/**
- * Define all the restore steps that will be used by the restore_quiz_activity_task
- */
+
+defined('MOODLE_INTERNAL') || die();
+
 
 /**
  * Structure step to restore one quiz activity
index 50357c0..0212bb6 100644 (file)
@@ -24,6 +24,8 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die();
+
 $capabilities = array(
 
     // Ability to see that the quiz exists, and the basic information
index 75c3ad8..8fcfcda 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Code run after the quiz module database tables have been created.
  */
index 159b0e2..8f16168 100644 (file)
@@ -24,6 +24,8 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die();
+
 $messageproviders = array (
     // Notify teacher that a student has submitted a quiz attempt
     'submission' => array (
index 26181ee..6be5e36 100644 (file)
@@ -24,4 +24,6 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die();
+
 $subplugins = array('quiz' => 'mod/quiz/report');
index 969e452..9837b90 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Quiz module upgrade function.
  * @param string $oldversion the version we are upgrading from.
index 7d2011a..656e5c1 100644 (file)
@@ -30,8 +30,9 @@
  */
 
 
-require_once($CFG->dirroot . '/mod/quiz/locallib.php');
+defined('MOODLE_INTERNAL') || die();
 
+require_once($CFG->dirroot . '/mod/quiz/locallib.php');
 
 define('NUM_QS_TO_SHOW_IN_RANDOM', 3);
 
index 9f6c807..075490b 100644 (file)
@@ -28,9 +28,9 @@
  */
 
 
-/** 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');
 
 
index 824da5d..af15b32 100644 (file)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page.
-}
 
-/**
- * Include those library functions that are also used by core Moodle or other modules
- */
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->dirroot . '/mod/quiz/lib.php');
 require_once($CFG->dirroot . '/mod/quiz/accessrules.php');
 require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
@@ -44,7 +40,6 @@ require_once($CFG->dirroot . '/question/editlib.php');
 require_once($CFG->libdir  . '/eventslib.php');
 require_once($CFG->libdir . '/filelib.php');
 
-/// Constants ///////////////////////////////////////////////////////////////////
 
 /**#@+
  * Options determining how the grades from individual attempts are combined to give
index f424ccb..4a50ef6 100644 (file)
@@ -25,9 +25,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
-}
+defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->dirroot . '/course/moodleform_mod.php');
 require_once($CFG->dirroot . '/mod/quiz/locallib.php');
index a7a5e09..fe84674 100644 (file)
@@ -25,9 +25,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
-}
+defined('MOODLE_INTERNAL') || die();
 
 require_once $CFG->libdir.'/formslib.php';
 
index 93941c8..49f8c37 100644 (file)
@@ -27,6 +27,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir.'/tablelib.php');
 
 
index 9fc3313..c918c81 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Base class for quiz report plugins.
  *
index eac7be2..5491b7a 100644 (file)
@@ -24,6 +24,8 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die();
+
 $capabilities = array(
     // Is the user allowed to see the student's real names while grading?
     'quiz/grading:viewstudentnames' => array(
index a280cd6..14b3f66 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir . '/formslib.php');
 
 
index 8c6a5eb..175bed7 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->dirroot . '/mod/quiz/report/grading/gradingsettings_form.php');
 
 
index 141d920..8178475 100644 (file)
@@ -24,4 +24,6 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die();
+
 $plugin->version  = 2011021400;
index 3eac5b0..ae26886 100644 (file)
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Quiz overview report upgrade function.
  * @param number $oldversion
- */function xmldb_quiz_overview_upgrade($oldversion) {
+ */
+function xmldb_quiz_overview_upgrade($oldversion) {
     global $CFG, $DB;
 
     $dbman = $DB->get_manager();
index 197eb9a..47b557b 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * This is a table subclass for displaying the quiz grades report.
  *
index 85bf30c..f498ff1 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir . '/formslib.php');
 
 
index 924261a..5ec1c98 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->dirroot.'/mod/quiz/report/attemptsreport.php');
 require_once($CFG->dirroot.'/mod/quiz/report/overview/overviewsettings_form.php');
 require_once($CFG->dirroot.'/mod/quiz/report/overview/overview_table.php');
index 3998229..45d1dd1 100644 (file)
@@ -24,4 +24,6 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die();
+
 $plugin->version  = 2011021600;   // The (date) version of this module
index bb3dc96..6676fe9 100644 (file)
@@ -24,6 +24,9 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->dirroot . '/mod/quiz/lib.php');
 
 define('QUIZ_REPORT_DEFAULT_PAGE_SIZE', 30);
index 4fdf6c4..39d9784 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->dirroot.'/mod/quiz/report/attemptsreport.php');
 require_once($CFG->dirroot.'/mod/quiz/report/responses/responsessettings_form.php');
 require_once($CFG->dirroot.'/mod/quiz/report/responses/responses_table.php');
index 829a1ad..6f9b188 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * This is a table subclass for displaying the quiz responses report.
  *
index 1c25bfe..5918119 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir . '/formslib.php');
 
 
index d7f15cf..cc9025f 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../../config.php');
 
 global $CFG;
index 454ee4f..c768fa7 100644 (file)
@@ -24,6 +24,9 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->dirroot . '/question/engine/compatibility.php');
 
 /**
index 88ea302..3fdb468 100644 (file)
@@ -23,6 +23,9 @@
  * @copyright  2008 Jamie Pratt
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
+
+defined('MOODLE_INTERNAL') || die();
+
 $capabilities = array(
     'quiz/statistics:view' => array(
         'captype' => 'read',
index 55f1e82..bb88809 100644 (file)
@@ -24,6 +24,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Post-install script
  */
index fcc8426..a91d2aa 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Quiz statistics report upgrade code.
  */
index 893ec53..421fbff 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * This class has methods to compute the question statistics from the raw data.
  *
index 07863c8..079958c 100644 (file)
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->dirroot . '/mod/quiz/report/statistics/statistics_form.php');
 require_once($CFG->dirroot . '/mod/quiz/report/statistics/statistics_table.php');
 require_once($CFG->dirroot . '/mod/quiz/report/statistics/statistics_question_table.php');
 require_once($CFG->dirroot . '/mod/quiz/report/statistics/qstats.php');
 require_once($CFG->dirroot . '/mod/quiz/report/statistics/responseanalysis.php');
 
+
 /**
  * The quiz statistics report provides summary information about each question in
  * a quiz, compared to the whole quiz. It also provides a drill-down to more
  * detailed information about each question.
  *
- * @copyright 2008 Jamie Pratt
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright  2008 Jamie Pratt
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class quiz_statistics_report extends quiz_default_report {
     /** @var integer Time after which statistics are automatically recomputed. */
index c2c39ca..4a68875 100644 (file)
@@ -26,6 +26,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * This class can store and compute the analysis of the responses to a particular
  * question.
index db4d551..33405bb 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir . '/questionlib.php');
 require_once($CFG->dirroot . '/mod/quiz/report/statistics/qstats.php');
 require_once($CFG->dirroot . '/mod/quiz/locallib.php');
index 2a21a1e..04bc479 100644 (file)
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir . '/formslib.php');
 
+
 /**
  * This is the settings form for the quiz statistics report.
  *
index b372364..29f3b65 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir . '/tablelib.php');
 
 
index aa0be31..6edc82f 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir.'/tablelib.php');
 
 
index bf252a6..2cceaa2 100644 (file)
@@ -24,4 +24,6 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die();
+
 $plugin->version = 2011021500;
index 0ff5ee3..fa07060 100644 (file)
@@ -25,7 +25,7 @@
  */
 
 
-defined('MOODLE_INTERNAL') || die;
+defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->dirroot . '/mod/quiz/lib.php');
 require_once($CFG->dirroot . '/mod/quiz/locallib.php');
index d49aacc..358e1dd 100644 (file)
@@ -26,9 +26,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page.
-}
+defined('MOODLE_INTERNAL') || die();
 
 
 /**
index b925924..05be3ee 100644 (file)
@@ -24,9 +24,8 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page.
-}
+
+defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->dirroot . '/mod/quiz/locallib.php');
 
index d11e224..08e9ca4 100644 (file)
@@ -25,9 +25,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page.
-}
+defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->dirroot . '/mod/quiz/editlib.php');
 
index 9463245..422fa5b 100644 (file)
@@ -25,9 +25,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page.
-}
+defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->dirroot . '/mod/quiz/lib.php');
 
index 8142793..f04b8e1 100644 (file)
@@ -25,9 +25,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page.
-}
+defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->dirroot . '/mod/quiz/locallib.php');
 
index 27a4267..991afdd 100644 (file)
@@ -25,9 +25,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page.
-}
+defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->dirroot . '/mod/quiz/locallib.php');
 
index 26cbfc8..4d85566 100644 (file)
@@ -24,8 +24,8 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die();
+
 $module->version  = 2010122304;   // The (date) version of this module
 $module->requires = 2010080300;   // Requires this Moodle version
 $module->cron     = 0;            // How often should cron check this module (seconds)?
-
-
index 8741deb..0059878 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Question behaviour for the old adaptive mode.
  *
@@ -26,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Question behaviour for adaptive mode.
  *
index c643ef6..dceec94 100644 (file)
@@ -26,6 +26,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Renderer for outputting parts of a question belonging to the legacy
  * adaptive behaviour.
index e9b2f13..bcd7b5e 100644 (file)
@@ -27,6 +27,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 
index d2db72f..2e2898a 100644 (file)
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../adaptive/behaviour.php');
 
+
 /**
  * Question behaviour for adaptive mode, with no penalties.
  *
index 0212e5f..0ff7091 100644 (file)
@@ -26,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../adaptive/renderer.php');
 
 
index d7e2c4b..7db56fc 100644 (file)
@@ -26,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 
index f8219d0..814e322 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * The base class for question behaviours.
  *
index 9598cc7..1a852bc 100644 (file)
@@ -27,6 +27,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../deferredfeedback/behaviour.php');
 
 
index 4200808..8189145 100644 (file)
@@ -26,6 +26,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Renderer for outputting parts of a question belonging to the deferred
  * feedback with certainty based marking behaviour.
index 7d598ec..da3a878 100644 (file)
@@ -26,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 
index a66877e..e39e17f 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Question behaviour for the case when the student's answer is just
  * saved until they submit the whole attempt, and then it is graded.
@@ -27,6 +26,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Question behaviour for deferred feedback.
  *
index 3e6f520..e1e9ae5 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Renderer for outputting parts of a question belonging to the deferred
  * feedback behaviour.
index ceab102..90201ee 100644 (file)
@@ -26,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 
index ae64441..46aead6 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Question behaviour where the student can submit questions one at a
  * time for immediate feedback, with certainty based marking.
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../immediatefeedback/behaviour.php');
 
+
 /**
  * Question behaviour for immediate feedback with CBM.
  *
index 3c943f8..8512fac 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../deferredcbm/renderer.php');
 
 
index 2729f5a..79a3df7 100644 (file)
@@ -26,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 
index 851a7ff..af345af 100644 (file)
@@ -26,6 +26,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Question behaviour for immediate feedback.
  *
index 6074321..9f16b1e 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Renderer for outputting parts of a question belonging to the immediate
  * feedback behaviour.
index 9716773..d1fb516 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * This file contains tests that walks a question through the immediate feedback
  * behaviour.
@@ -27,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 
index cbfcfae..82e729a 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Question behaviour informaiton items.
  *
index 06439d5..9b0c421 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Renderer for outputting parts of a question belonging to the information
  * item behaviour.
index 2268e29..6b7710f 100644 (file)
@@ -26,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 
index c8e818c..fbaa868 100644 (file)
@@ -26,6 +26,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Question behaviour for the interactive model.
  *
index 5dc1777..ac209db 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Renderer for outputting parts of a question belonging to the interactive
  * behaviour.
@@ -27,6 +26,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Interactive behaviour renderer.
  *
index 66dcc27..575b779 100644 (file)
@@ -26,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 
index a5f7279..d1885e0 100644 (file)
@@ -26,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../interactive/behaviour.php');
 
 
index 65234c0..3889ed3 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../interactive/renderer.php');
 
 
index 0774f4b..0a98919 100644 (file)
@@ -26,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 
index 07173aa..16a21cc 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Question behaviour for questions that can only be graded manually.
  *
index 725dc90..8e2a739 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Renderer for outputting parts of a question belonging to the manual
  * graded behaviour.
index 1680b7f..8b4d1e3 100644 (file)
@@ -26,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 
index 5173d4d..94bed44 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Fake question behaviour that is used when the actual qim was not
  * available.
@@ -27,6 +26,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Fake question behaviour that is used when the actual behaviour
  * is not available.
index b4681d3..8fb727d 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Renderer for outputting parts of a question when the actual behaviour
  * used is not available.
index fa3256f..33a1bec 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 require_once(dirname(__FILE__) . '/../behaviour.php');
index 0b8a3ae..c334433 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * This behaviour that is used when the actual qim was not
  * available.
@@ -27,6 +26,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * This behaviour is specifically for use with the Opaque question type.
  *
index dcad989..8965a2b 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Renderer for outputting parts of a question when the actual behaviour
  * used is not available.
index c8f3712..6c5882e 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../../../engine/lib.php');
 require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
 require_once(dirname(__FILE__) . '/../behaviour.php');
index 0a5eeb1..9206520 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Renderer base class for question behaviours.
  *
index 40fc99d..35d0796 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 // number of categories to display on page
 define('QUESTION_PAGE_LENGTH', 25);
 
index dd46738..c1bedac 100644 (file)
@@ -25,9 +25,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
-}
+defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->libdir.'/formslib.php');
 
index 87f6ec3..b26a8ef 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Functions used to show question editing interface
  *
@@ -26,8 +25,9 @@
  */
 
 
-require_once($CFG->libdir . '/questionlib.php');
+defined('MOODLE_INTERNAL') || die();
 
+require_once($CFG->libdir . '/questionlib.php');
 
 define('DEFAULT_QUESTIONS_PER_PAGE', 20);
 
index 19372c2..cc86e52 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * More object oriented wrappers around parts of the Moodle question bank.
  *
@@ -30,6 +29,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * This static class provides access to the other question bank.
  *
index a8b952f..55288b4 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Code for loading and saving quiz attempts to and from the database.
  *
@@ -26,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * This class controls the loading and saving of question engine data to and from
  * the database.
index 35132fc..b48fd9e 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/states.php');
 require_once(dirname(__FILE__) . '/datalib.php');
 require_once(dirname(__FILE__) . '/renderer.php');
index 25f8f41..ce8af0b 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Renderers for outputting parts of the question engine.
  *
@@ -26,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * This renderer controls the overall output of questions. It works with a
  * {@link qbehaviour_renderer} and a {@link qtype_renderer} to output the
index b232385..5fc409a 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * This file contains helper classes for testing the question engine.
  *
@@ -26,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../lib.php');
 
 
index a7b2865..cd9989e 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../lib.php');
 
 
index c965dd2..3e30eeb 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * This file contains tests for the question_attempt class.
  *
@@ -29,6 +28,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../lib.php');
 require_once(dirname(__FILE__) . '/helpers.php');
 
index 0a0eb49..e302e92 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../lib.php');
 require_once(dirname(__FILE__) . '/helpers.php');
 
index c07336c..8c3a4a4 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../lib.php');
 require_once(dirname(__FILE__) . '/helpers.php');
 
index 70e4dae..6f0cd28 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../lib.php');
 require_once(dirname(__FILE__) . '/helpers.php');
 
index 622ef8d..0b8f5c0 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../lib.php');
 
 
index 8f3a7e5..003094f 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../lib.php');
 
 
index a4a2c8b..6492f5b 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../lib.php');
 require_once($CFG->libdir . '/questionlib.php');
 
index 5b3cd06..978c8c8 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../lib.php');
 require_once(dirname(__FILE__) . '/helpers.php');
 
index aa1d46b..66d242c 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once(dirname(__FILE__) . '/../lib.php');
 
 
index 69e414f..00f294e 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * This defines the states a question can be in.
  *
@@ -26,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * An enumeration representing the states a question can be in after a
  * {@link question_attempt_step}.
index 97ae2bd..bd332cb 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * This file contains the code required to upgrade all the attempt data from
  * old versions of Moodle into the tables used by the new question engine.
@@ -27,6 +26,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 global $CFG;
 require_once($CFG->libdir . '/questionlib.php');
 
index dbc1e07..87aa703 100644 (file)
@@ -25,9 +25,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.');
-}
+defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->libdir . '/formslib.php');
 
index 494023d..28ec044 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Base class for question import and export formats.
  *
index da9c6ad..afbf292 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Aiken format - a simple format for creating multiple choice questions (with
  * only one correct choice, and no feedback).
index 911cc01..1b70781 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once ($CFG->libdir . '/xmlize.php');
 
 
index ae5070d..27911f8 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once ($CFG->libdir . '/xmlize.php');
 
 
index 54aba84..1cb59ff 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once ($CFG->libdir . '/xmlize.php');
 
 
index 0b31fb6..6b4fe55 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * The GIFT import filter was designed as an easy to use method
  * for teachers writing questions as a text file. It supports most
index eae4ca2..da630d7 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Unit tests for the Moodle GIFT format.
  *
@@ -25,6 +24,9 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir . '/questionlib.php');
 require_once($CFG->dirroot . '/question/format.php');
 require_once($CFG->dirroot . '/question/format/gift/format.php');
index 039382d..d4d81e2 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Examview question importer.
  *
index a8f09e2..1607f3f 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Missing word question importer.
  *
index ba780a1..5469e00 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Importer that imports a text file containing a single Multianswer question
  * from a text file.
index a6f2202..5476845 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Modified from the original filter/mediaplugin/filter.php
  */
index a5ae7d1..301eb20 100644 (file)
@@ -25,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once("$CFG->dirroot/question/format/qti_two/qt_common.php");
 
 define('CLOZE_TRAILING_TEXT_ID', 9999999);
index fe73ca6..2ccc582 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * gets a list of all the media files for the given course
  *
index d41859b..3395a92 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * Web CT question importer.
  *
index a085e6b..44a30cd 100644 (file)
@@ -25,6 +25,9 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
+
 /**
  * XHTML question exporter.
  *
index 73ff6d1..0f2f464 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Code for exporting questions as Moodle XML.
  *
@@ -26,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->dirroot . '/question/format.php');
 require_once($CFG->libdir . '/xmlize.php');
 
index e22e715..8f89647 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Unit tests for the Moodle XML format.
  *
@@ -25,6 +24,9 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir . '/questionlib.php');
 require_once($CFG->dirroot . '/question/format/xml/format.php');
 
index 7ed0097..da1f231 100644 (file)
@@ -25,9 +25,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
-}
+defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->libdir . '/formslib.php');
 
index b8f3cf4..9101ca1 100644 (file)
@@ -25,9 +25,7 @@
  */
 
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
-}
+defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->libdir . '/formslib.php');
 
index d75ee7c..6a870f3 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Library functions used by question/preview.php.
  *
@@ -26,6 +25,8 @@
  */
 
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir . '/formslib.php');
 
 
index fd5cf3b..5bbd481 100644 (file)
@@ -15,7 +15,6 @@
 // You should have received a copy of the GNU General Public License
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
-
 /**
  * Unit tests for the question import and export system.
  *
@@ -25,6 +24,9 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir . '/questionlib.php');
 require_once($CFG->dirroot . '/question/format.php');
 
index 81b2543..3c2d4e1 100644 (file)
@@ -446,100 +446,3 @@ DONE question/type/truefalse/renderer.php               |  142 +
 DONE question/type/truefalse/simpletest/testquestion.php     |   99 +
 DONE question/type/truefalse/simpletest/testquestiontype.php |   73 +
 DONE question/type/truefalse/version.php                |    4 +-
-
-Missing @package
-================
-
-DONE mod/quiz/addrandomform.php
-DONE mod/quiz/db/access.php
-DONE mod/quiz/db/install.php
-DONE mod/quiz/db/install.xml
-DONE mod/quiz/db/subplugins.php
-DONE mod/quiz/db/upgrade.php
-DONE mod/quiz/grade.php
-DONE mod/quiz/module.js
-DONE mod/quiz/pix/icon.gif
-DONE mod/quiz/pix/navflagged.png
-DONE mod/quiz/report/grading/styles.css
-DONE mod/quiz/report/overview/db/install.xml
-DONE mod/quiz/report/statistics/db/install.xml
-DONE mod/quiz/report/statistics/simpletest/mdl_question.csv
-DONE mod/quiz/report/statistics/simpletest/mdl_question_states.csv
-DONE mod/quiz/settingslib.php
-DONE mod/quiz/styles.css
-DONE mod/quiz/version.php
-DONE question/behaviour/adaptive/lang/en/qbehaviour_adaptive.php
-DONE question/behaviour/adaptivenopenalty/lang/en/qbehaviour_adaptivenopenalty.php
-DONE question/behaviour/deferredcbm/lang/en/qbehaviour_deferredcbm.php
-DONE question/behaviour/deferredfeedback/lang/en/qbehaviour_deferredfeedback.php
-DONE question/behaviour/immediatecbm/lang/en/qbehaviour_immediatecbm.php
-DONE question/behaviour/immediatefeedback/lang/en/qbehaviour_immediatefeedback.php
-DONE question/behaviour/informationitem/lang/en/qbehaviour_informationitem.php
-DONE question/behaviour/interactive/lang/en/qbehaviour_interactive.php
-DONE question/behaviour/interactivecountback/lang/en/qbehaviour_interactivecountback.php
-DONE question/behaviour/manualgraded/lang/en/qbehaviour_manualgraded.php
-DONE question/behaviour/missing/lang/en/qbehaviour_missing.php
-DONE question/behaviour/opaque/lang/en/qbehaviour_opaque.php
-DONE question/category_form.php
-DONE question/export_form.php
-DONE question/flags.js
-DONE question/format/aiken/format.php
-DONE question/import_form.php
-DONE question/qbank.js
-DONE question/qengine.js
-DONE question/type/calculated/datasetitems_form.php
-DONE question/type/calculated/db/upgrade.php
-DONE question/type/calculated/questiontype.php
-DONE question/type/calculated/version.php
-DONE question/type/calculatedmulti/questiontype.php
-DONE question/type/calculatedsimple/questiontype.php
-DONE question/type/ddwtos/version.php
-DONE question/type/description/question.html
-DONE question/type/essay/display.html
-DONE question/type/essay/version.php
-DONE question/type/gapselect/edit_form_base.php
-DONE question/type/gapselect/rendererbase.php
-DONE question/type/match/db/upgrade.php
-DONE question/type/match/version.php
-DONE question/type/multianswer/db/upgrade.php
-DONE question/type/multianswer/version.php
-DONE question/type/multichoice/db/upgrade.php
-DONE question/type/multichoice/version.php
-DONE question/type/numerical/db/upgrade.php
-DONE question/type/numerical/display.html
-DONE question/type/numerical/version.php
-DONE question/type/opaque/lang/en/qtype_opaque.php
-DONE question/type/opaque/settings.php
-DONE question/type/randomsamatch/version.php
-DONE question/type/truefalse/version.php
-
-Missing boiler-plate
-====================
-
-DONE question/addquestion.php
-DONE question/category.php
-DONE question/category_class.php
-DONE question/edit.php
-DONE question/export.php
-DONE question/format/blackboard/format.php
-DONE question/format/blackboard_six/format.php
-DONE question/format/examview/format.php
-DONE question/format/learnwise/format.php
-DONE question/format/missingword/format.php
-DONE question/format/qti_two/custommediafilter.php
-DONE question/format/qti_two/format.php
-DONE question/format/qti_two/qt_common.php
-DONE question/format/webct/format.php
-DONE question/format/xhtml/format.php
-DONE question/format.php
-DONE question/question.php
-DONE question/type/calculated/datasetdefinitions_form.php
-DONE question/type/calculatedmulti/edit_calculatedmulti_form.php
-DONE question/type/ddwtos/script.js
-DONE question/type/multianswer/edit_multianswer_form.php
-DONE question/type/multianswer/questiontype.php
-DONE question/type/numerical/display.html
-DONE question/type/randomsamatch/edit_randomsamatch_form.php
-DONE question/type/randomsamatch/questiontype.php
-DONE question/upgrade.php
-
diff --git a/question/todo/questionlib_2.0.diff.txt b/question/todo/questionlib_2.0.diff.txt
deleted file mode 100644 (file)
index b889602..0000000
+++ /dev/null
@@ -1,807 +0,0 @@
-
- /**
-  * Prints a question
-  *
-  * Simply calls the question type specific print_question() method.
-+ *
-+ * @global array
-  * @param object $question The question to be rendered.
-  * @param object $state    The state to render the question in.
-  * @param integer $number  The number for this question.
-  * @param object $cmoptions  The options specified by the course module
-  * @param object $options  An object specifying the rendering options.
-  */
--function print_question(&$question, &$state, $number, $cmoptions, $options=null) {
-+function print_question(&$question, &$state, $number, $cmoptions, $options=null, $context=null) {
-     global $QTYPES;
--    $QTYPES[$question->qtype]->print_question($question, $state, $number, $cmoptions, $options);
-+    $QTYPES[$question->qtype]->print_question($question, $state, $number, $cmoptions, $options, $context);
- }
- /**
-  * Saves question options
-  *
-  * Simply calls the question type specific save_question_options() method.
-+ *
-+ * @global array
-  */
- function save_question_options($question) {
-     global $QTYPES;
-@@ -2075,8 +2255,9 @@ function sort_categories_by_tree(&$categories, $id = 0, $level = 1) {
-     //If level = 1, we have finished, try to look for non processed categories (bad parent) and sort them too
-     if ($level == 1) {
-         foreach ($keys as $key) {
--            //If not processed and it's a good candidate to start (because its parent doesn't exist in the course)
--            if (!isset($categories[$key]->processed) && !$DB->record_exists('question_categories', array('course'=>$categories[$key]->course, 'id'=>$categories[$key]->parent))) {
-+            // If not processed and it's a good candidate to start (because its parent doesn't exist in the course)
-+            if (!isset($categories[$key]->processed) && !$DB->record_exists(
-+                    'question_categories', array('contextid'=>$categories[$key]->contextid, 'id'=>$categories[$key]->parent))) {
-                 $children[$key] = $categories[$key];
-                 $categories[$key]->processed = true;
-                 $children = $children + sort_categories_by_tree($categories, $children[$key]->id, $level+1);
-@@ -2167,16 +2348,23 @@ function add_indented_names($categories, $nochildrenof = -1) {
-  * @param integer $selected optionally, the id of a category to be selected by default in the dropdown.
-  */
- function question_category_select_menu($contexts, $top = false, $currentcat = 0, $selected = "", $nochildrenof = -1) {
-+    global $OUTPUT;
-     $categoriesarray = question_category_options($contexts, $top, $currentcat, false, $nochildrenof);
-     if ($selected) {
--        $nothing = '';
-+        $choose = '';
-     } else {
--        $nothing = 'choose';
-+        $choose = 'choosedots';
-+    }
-+    $options = array();
-+    foreach($categoriesarray as $group=>$opts) {
-+        $options[] = array($group=>$opts);
-     }
--    choose_from_menu_nested($categoriesarray, 'category', $selected, $nothing);
-+
-+    echo html_writer::select($options, 'category', $selected, $choose);
- }
-@@ -2216,23 +2406,31 @@ function question_edit_url($context) {
- /**
- * Gets the default category in the most specific context.
- * If no categories exist yet then default ones are created in all contexts.
- *
-+ * @global object
- * @param array $contexts  The context objects for this context and all parent contexts.
- * @return object The default category - the category in the course context
- */
- function question_make_default_categories($contexts) {
-     global $DB;
-+    static $preferredlevels = array(
-+        CONTEXT_COURSE => 4,
-+        CONTEXT_MODULE => 3,
-+        CONTEXT_COURSECAT => 2,
-+        CONTEXT_SYSTEM => 1,
-+    );
-     $toreturn = null;
-+    $preferredness = 0;
-     // If it already exists, just return it.
-     foreach ($contexts as $key => $context) {
--        if (!$exists = $DB->record_exists("question_categories", array('contextid'=>$context->id))){
-+        if (!$exists = $DB->record_exists("question_categories", array('contextid'=>$context->id))) {
-             // Otherwise, we need to make one
-             $category = new stdClass;
-             $contextname = print_context_name($context, false, true);
-@@ -2242,19 +2440,20 @@ function question_make_default_categories($contexts) {
-             $category->parent = 0;
-             $category->sortorder = 999; // By default, all categories get this number, and are sorted alphabetically.
-             $category->stamp = make_unique_id_code();
--            if (!$category->id = $DB->insert_record('question_categories', $category)) {
--                print_error('cannotcreatedefaultcat', '', '', print_context_name($context));
--            }
-+            $category->id = $DB->insert_record('question_categories', $category);
-         } else {
-             $category = question_get_default_category($context->id);
-         }
--
--        if ($context->contextlevel == CONTEXT_COURSE){
--            $toreturn = clone($category);
-+        if ($preferredlevels[$context->contextlevel] > $preferredness &&
-+                has_any_capability(array('moodle/question:usemine', 'moodle/question:useall'), $context)) {
-+            $toreturn = $category;
-+            $preferredness = $preferredlevels[$context->contextlevel];
-         }
-     }
--
-+    if (!is_null($toreturn)) {
-+        $toreturn = clone($toreturn);
-+    }
-     return $toreturn;
- }
-@@ -2313,9 +2514,12 @@ function question_category_options($contexts, $top = false, $currentcat = 0, $po
-     if ($popupform){
-         $popupcats = array();
-         foreach ($categoriesarray as $contextstring => $optgroup){
--            $popupcats[] = '--'.$contextstring;
--            $popupcats = array_merge($popupcats, $optgroup);
--            $popupcats[] = '--';
-+            $group = array();
-+            foreach ($optgroup as $key=>$value) {
-+                $key = str_replace($CFG->wwwroot, '', $key);
-+                $group[$key] = $value;
-+            }
-+            $popupcats[] = array($contextstring=>$group);
-         }
-         return $popupcats;
-     } else {
-@@ -2335,7 +2539,7 @@ function question_add_context_in_key($categories){
- function question_add_tops($categories, $pcontexts){
-     $topcats = array();
-     foreach ($pcontexts as $context){
--        $newcat = new object();
-+        $newcat = new stdClass();
-         $newcat->id = "0,$context";
-         $newcat->name = get_string('top');
-         $newcat->parent = -1;
- function get_import_export_formats( $type ) {
-     global $CFG;
--    $fileformats = get_list_of_plugins("question/format");
-+    $fileformats = get_plugin_list("qformat");
-     $fileformatname=array();
-     require_once( "{$CFG->dirroot}/question/format.php" );
--    foreach ($fileformats as $key => $fileformat) {
--        $format_file = $CFG->dirroot . "/question/format/$fileformat/format.php";
--        if (file_exists( $format_file ) ) {
--            require_once( $format_file );
-+    foreach ($fileformats as $fileformat=>$fdir) {
-+        $format_file = "$fdir/format.php";
-+        if (file_exists($format_file) ) {
-+            require_once($format_file);
-         }
-         else {
-             continue;
-@@ -2400,7 +2607,10 @@ function get_import_export_formats( $type ) {
-         if ($provided) {
-             $formatname = get_string($fileformat, 'quiz');
-             if ($formatname == "[[$fileformat]]") {
--                $formatname = $fileformat;  // Just use the raw folder name
-+                $formatname = get_string($fileformat, 'qformat_'.$fileformat);
-+                if ($formatname == "[[$fileformat]]") {
-+                    $formatname = $fileformat;  // Just use the raw folder name
-+                }
-             }
-             $fileformatnames[$fileformat] = $formatname;
-         }
-@@ -2412,50 +2622,39 @@ function get_import_export_formats( $type ) {
- /**
--* Create default export filename
--*
--* @return string   default export filename
--* @param object $course
--* @param object $category
-+* Create a reasonable default file name for exporting questions from a particular
-+* category.
-+* @param object $course the course the questions are in.
-+* @param object $category the question category.
-+* @return string the filename.
- */
--function default_export_filename($course,$category) {
--    //Take off some characters in the filename !!
--    $takeoff = array(" ", ":", "/", "\\", "|");
--    $export_word = str_replace($takeoff,"_",moodle_strtolower(get_string("exportfilename","quiz")));
--    //If non-translated, use "export"
--    if (substr($export_word,0,1) == "[") {
--        $export_word= "export";
--    }
--
--    //Calculate the date format string
--    $export_date_format = str_replace(" ","_",get_string("exportnameformat","quiz"));
--    //If non-translated, use "%Y%m%d-%H%M"
--    if (substr($export_date_format,0,1) == "[") {
--        $export_date_format = "%%Y%%m%%d-%%H%%M";
--    }
--
--    //Calculate the shortname
--    $export_shortname = clean_filename($course->shortname);
--    if (empty($export_shortname) or $export_shortname == '_' ) {
--        $export_shortname = $course->id;
--    }
--
--    //Calculate the category name
--    $export_categoryname = clean_filename($category->name);
--
--    //Calculate the final export filename
--    //The export word
--    $export_name = $export_word."-";
--    //The shortname
--    $export_name .= moodle_strtolower($export_shortname)."-";
--    //The category name
--    $export_name .= moodle_strtolower($export_categoryname)."-";
--    //The date format
--    $export_name .= userdate(time(),$export_date_format,99,false);
--    //Extension is supplied by format later.
-+function question_default_export_filename($course, $category) {
-+    // We build a string that is an appropriate name (questions) from the lang pack,
-+    // then the corse shortname, then the question category name, then a timestamp. 
-+
-+    $base = clean_filename(get_string('exportfilename', 'question'));
-+
-+    $dateformat = str_replace(' ', '_', get_string('exportnameformat', 'question'));
-+    $timestamp = clean_filename(userdate(time(), $dateformat, 99, false));
-+
-+    $shortname = clean_filename($course->shortname);
-+    if ($shortname == '' || $shortname == '_' ) {
-+        $shortname = $course->id;
-+    }
-+
-+    $categoryname = clean_filename(format_string($category->name));
-+
-+    return "{$base}-{$shortname}-{$categoryname}-{$timestamp}";
-     return $export_name;
- }
-+
-+/**
-+ * @package moodlecore
-+ * @subpackage question
-+ * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
-+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-+ */
- class context_to_string_translator{
-     /**
-      * @var array used to translate between contextids and strings for this context.
-@@ -2549,13 +2751,13 @@ function question_has_capability_on($question, $cap, $cachecat = -1){
-     static $questions = array();
-     static $categories = array();
-     static $cachedcat = array();
--    if ($cachecat != -1 && (array_search($cachecat, $cachedcat)===FALSE)){
--        $questions += $DB->get_records('question', array('category'=>$cachecat));
-+    if ($cachecat != -1 && array_search($cachecat, $cachedcat) === false) {
-+        $questions += $DB->get_records('question', array('category' => $cachecat));
-         $cachedcat[] = $cachecat;
-     }
-     if (!is_object($question)){
-         if (!isset($questions[$question])){
--            if (!$questions[$question] = $DB->get_record('question', array('id'=>$question), 'id,category,createdby')) {
-+            if (!$questions[$question] = $DB->get_record('question', array('id' => $question), 'id,category,createdby')) {
-                 print_error('questiondoesnotexist', 'question');
-             }
-         }
-@@ -2567,11 +2769,12 @@ function question_has_capability_on($question, $cap, $cachecat = -1){
-         }
-     }
-     $category = $categories[$question->category];
-+    $context = get_context_instance_by_id($category->contextid);
-     if (array_search($cap, $question_questioncaps)!== FALSE){
--        if (!has_capability('moodle/question:'.$cap.'all', get_context_instance_by_id($category->contextid))){
-+        if (!has_capability('moodle/question:'.$cap.'all', $context)){
-             if ($question->createdby == $USER->id){
--                return has_capability('moodle/question:'.$cap.'mine', get_context_instance_by_id($category->contextid));
-+                return has_capability('moodle/question:'.$cap.'mine', $context);
-             } else {
-                 return false;
-             }
-@@ -2579,7 +2782,7 @@ function question_has_capability_on($question, $cap, $cachecat = -1){
-             return true;
-         }
-     } else {
--        return has_capability('moodle/question:'.$cap, get_context_instance_by_id($category->contextid));
-+        return has_capability('moodle/question:'.$cap, $context);
-     }
- }
-@@ -2594,107 +2797,6 @@ function question_require_capability_on($question, $cap){
-     return true;
- }
--function question_file_links_base_url($courseid){
--    global $CFG;
--    $baseurl = preg_quote("$CFG->wwwroot/file.php", '!');
--    $baseurl .= '('.preg_quote('?file=', '!').')?';//may or may not
--                                     //be using slasharguments, accept either
--    $baseurl .= "/$courseid/";//course directory
--    return $baseurl;
--}
--
--/*
-- * Find all course / site files linked to in a piece of html.
-- * @param string html the html to search
-- * @param int course search for files for courseid course or set to siteid for
-- *              finding site files.
-- * @return array files with keys being files.
-- */
--function question_find_file_links_from_html($html, $courseid){
--    global $CFG;
--    $baseurl = question_file_links_base_url($courseid);
--    $searchfor = '!'.
--                   '(<\s*(a|img)\s[^>]*(href|src)\s*=\s*")'.$baseurl.'([^"]*)"'.
--                   '|'.
--                   '(<\s*(a|img)\s[^>]*(href|src)\s*=\s*\')'.$baseurl.'([^\']*)\''.
--                  '!i';
--    $matches = array();
--    $no = preg_match_all($searchfor, $html, $matches);
--    if ($no){
--        $rawurls = array_filter(array_merge($matches[5], $matches[10]));//array_filter removes empty elements
--        //remove any links that point somewhere they shouldn't
--        foreach (array_keys($rawurls) as $rawurlkey){
--            if (!$cleanedurl = question_url_check($rawurls[$rawurlkey])){
--                unset($rawurls[$rawurlkey]);
--            } else {
--                $rawurls[$rawurlkey] = $cleanedurl;
--            }
--
--        }
--        $urls = array_flip($rawurls);// array_flip removes duplicate files
--                                            // and when we merge arrays will continue to automatically remove duplicates
--    } else {
--        $urls = array();
--    }
--    return $urls;
--}
--
--/**
-- * Check that url doesn't point anywhere it shouldn't
-- *
-- * @param $url string relative url within course files directory
-- * @return mixed boolean false if not OK or cleaned URL as string if OK
-- */
--function question_url_check($url){
--    global $CFG;
--    if ((substr(strtolower($url), 0, strlen($CFG->moddata)) == strtolower($CFG->moddata)) ||
--            (substr(strtolower($url), 0, 10) == 'backupdata')){
--        return false;
--    } else {
--        return clean_param($url, PARAM_PATH);
--    }
--}
--
--/**
-- * Find all course / site files linked to in a piece of html.
-- * @param string html the html to search
-- * @param int course search for files for courseid course or set to siteid for
-- *              finding site files.
-- * @return array files with keys being files.
-- */
--function question_replace_file_links_in_html($html, $fromcourseid, $tocourseid, $url, $destination, &$changed){
--    global $CFG;
--    require_once($CFG->libdir .'/filelib.php');
--    $tourl = get_file_url("$tocourseid/$destination");
--    $fromurl = question_file_links_base_url($fromcourseid).preg_quote($url, '!');
--    $searchfor = array('!(<\s*(a|img)\s[^>]*(href|src)\s*=\s*")'.$fromurl.'(")!i',
--                   '!(<\s*(a|img)\s[^>]*(href|src)\s*=\s*\')'.$fromurl.'(\')!i');
--    $newhtml = preg_replace($searchfor, '\\1'.$tourl.'\\5', $html);
--    if ($newhtml != $html){
--        $changed = true;
--    }
--    return $newhtml;
--}
--
--function get_filesdir_from_context($context){
--    global $DB;
--
--    switch ($context->contextlevel){
--        case CONTEXT_COURSE :
--            $courseid = $context->instanceid;
--            break;
--        case CONTEXT_MODULE :
--            $courseid = $DB->get_field('course_modules', 'course', array('id'=>$context->instanceid));
--            break;
--        case CONTEXT_COURSECAT :
--        case CONTEXT_SYSTEM :
--            $courseid = SITEID;
--            break;
--        default :
--            print_error('invalidcontext');
--    }
--    return $courseid;
--}
- /**
-  * Get the real state - the correct question id and answer - for a random
-  * question.
-@@ -2702,11 +2804,12 @@ function get_filesdir_from_context($context){
-  * @return mixed return integer real question id or false if there was an
-  * error..
-  */
--function question_get_real_state($state){
-+function question_get_real_state($state) {
-+    global $OUTPUT;
-     $realstate = clone($state);
-     $matches = array();
-     if (!preg_match('|^random([0-9]+)-(.*)|', $state->answer, $matches)){
--        notify(get_string('errorrandom', 'quiz_statistics'));
-+        echo $OUTPUT->notification(get_string('errorrandom', 'quiz_statistics'));
-         return false;
-     } else {
-         $realstate->question = $matches[1];
-@@ -2770,4 +2877,389 @@ function question_get_toggleflag_checksum($attemptid, $questionid, $sessionid, $
-     return md5($attemptid . "_" . $user->secret . "_" . $questionid . "_" . $sessionid);
- }
--?>
-+/**
-+ * Adds question bank setting links to the given navigation node if caps are met.
-+ *
-+ * @param navigation_node $navigationnode The navigation node to add the question branch to
-+ * @param stdClass $context
-+ * @return navigation_node Returns the question branch that was added
-+ */
-+function question_extend_settings_navigation(navigation_node $navigationnode, $context) {
-+    global $PAGE;
-+
-+    if ($context->contextlevel == CONTEXT_COURSE) {
-+        $params = array('courseid'=>$context->instanceid);
-+    } else if ($context->contextlevel == CONTEXT_MODULE) {
-+        $params = array('cmid'=>$context->instanceid);
-+    } else {
-+        return;
-+    }
-+
-+    $questionnode = $navigationnode->add(get_string('questionbank','question'), new moodle_url('/question/edit.php', $params), navigation_node::TYPE_CONTAINER);
-+
-+    $contexts = new question_edit_contexts($context);
-+    if ($contexts->have_one_edit_tab_cap('questions')) {
-+        $questionnode->add(get_string('questions', 'quiz'), new moodle_url('/question/edit.php', $params), navigation_node::TYPE_SETTING);
-+    }
-+    if ($contexts->have_one_edit_tab_cap('categories')) {
-+        $questionnode->add(get_string('categories', 'quiz'), new moodle_url('/question/category.php', $params), navigation_node::TYPE_SETTING);
-+    }
-+    if ($contexts->have_one_edit_tab_cap('import')) {
-+        $questionnode->add(get_string('import', 'quiz'), new moodle_url('/question/import.php', $params), navigation_node::TYPE_SETTING);
-+    }
-+    if ($contexts->have_one_edit_tab_cap('export')) {
-+        $questionnode->add(get_string('export', 'quiz'), new moodle_url('/question/export.php', $params), navigation_node::TYPE_SETTING);
-+    }
-+
-+    return $questionnode;
-+}
-+
-+class question_edit_contexts {
-+
-+    public static $CAPS = array(
-+        'editq' => array('moodle/question:add',
-+            'moodle/question:editmine',
-+            'moodle/question:editall',
-+            'moodle/question:viewmine',
-+            'moodle/question:viewall',
-+            'moodle/question:usemine',
-+            'moodle/question:useall',
-+            'moodle/question:movemine',
-+            'moodle/question:moveall'),
-+        'questions'=>array('moodle/question:add',
-+            'moodle/question:editmine',
-+            'moodle/question:editall',
-+            'moodle/question:viewmine',
-+            'moodle/question:viewall',
-+            'moodle/question:movemine',
-+            'moodle/question:moveall'),
-+        'categories'=>array('moodle/question:managecategory'),
-+        'import'=>array('moodle/question:add'),
-+        'export'=>array('moodle/question:viewall', 'moodle/question:viewmine'));
-+
-+    protected $allcontexts;
-+
-+    /**
-+     * @param current context
-+     */
-+    public function question_edit_contexts($thiscontext){
-+        $pcontextids = get_parent_contexts($thiscontext);
-+        $contexts = array($thiscontext);
-+        foreach ($pcontextids as $pcontextid){
-+            $contexts[] = get_context_instance_by_id($pcontextid);
-+        }
-+        $this->allcontexts = $contexts;
-+    }
-+    /**
-+     * @return array all parent contexts
-+     */
-+    public function all(){
-+        return $this->allcontexts;
-+    }
-+    /**
-+     * @return object lowest context which must be either the module or course context
-+     */
-+    public function lowest(){
-+        return $this->allcontexts[0];
-+    }
-+    /**
-+     * @param string $cap capability
-+     * @return array parent contexts having capability, zero based index
-+     */
-+    public function having_cap($cap){
-+        $contextswithcap = array();
-+        foreach ($this->allcontexts as $context){
-+            if (has_capability($cap, $context)){
-+                $contextswithcap[] = $context;
-+            }
-+        }
-+        return $contextswithcap;
-+    }
-+    /**
-+     * @param array $caps capabilities
-+     * @return array parent contexts having at least one of $caps, zero based index
-+     */
-+    public function having_one_cap($caps){
-+        $contextswithacap = array();
-+        foreach ($this->allcontexts as $context){
-+            foreach ($caps as $cap){
-+                if (has_capability($cap, $context)){
-+                    $contextswithacap[] = $context;
-+                    break; //done with caps loop
-+                }
-+            }
-+        }
-+        return $contextswithacap;
-+    }
-+    /**
-+     * @param string $tabname edit tab name
-+     * @return array parent contexts having at least one of $caps, zero based index
-+     */
-+    public function having_one_edit_tab_cap($tabname){
-+        return $this->having_one_cap(self::$CAPS[$tabname]);
-+    }
-+    /**
-+     * Has at least one parent context got the cap $cap?
-+     *
-+     * @param string $cap capability
-+     * @return boolean
-+     */
-+    public function have_cap($cap){
-+        return (count($this->having_cap($cap)));
-+    }
-+
-+    /**
-+     * Has at least one parent context got one of the caps $caps?
-+     *
-+     * @param array $caps capability
-+     * @return boolean
-+     */
-+    public function have_one_cap($caps){
-+        foreach ($caps as $cap) {
-+            if ($this->have_cap($cap)) {
-+                return true;
-+            }
-+        }
-+        return false;
-+    }
-+    /**
-+     * Has at least one parent context got one of the caps for actions on $tabname
-+     *
-+     * @param string $tabname edit tab name
-+     * @return boolean
-+     */
-+    public function have_one_edit_tab_cap($tabname){
-+        return $this->have_one_cap(self::$CAPS[$tabname]);
-+    }
-+    /**
-+     * Throw error if at least one parent context hasn't got the cap $cap
-+     *
-+     * @param string $cap capability
-+     */
-+    public function require_cap($cap){
-+        if (!$this->have_cap($cap)){
-+            print_error('nopermissions', '', '', $cap);
-+        }
-+    }
-+    /**
-+     * Throw error if at least one parent context hasn't got one of the caps $caps
-+     *
-+     * @param array $cap capabilities
-+     */
-+     public function require_one_cap($caps) {
-+        if (!$this->have_one_cap($caps)) {
-+            $capsstring = join($caps, ', ');
-+            print_error('nopermissions', '', '', $capsstring);
-+        }
-+    }
-+
-+    /**
-+     * Throw error if at least one parent context hasn't got one of the caps $caps
-+     *
-+     * @param string $tabname edit tab name
-+     */
-+    public function require_one_edit_tab_cap($tabname){
-+        if (!$this->have_one_edit_tab_cap($tabname)) {
-+            print_error('nopermissions', '', '', 'access question edit tab '.$tabname);
-+        }
-+    }
-+}
-+
-+/**
-+ * Rewrite question url, file_rewrite_pluginfile_urls always build url by
-+ * $file/$contextid/$component/$filearea/$itemid/$pathname_in_text, so we cannot add
-+ * extra questionid and attempted in url by it, so we create quiz_rewrite_question_urls
-+ * to build url here
-+ *
-+ * @param string $text text being processed
-+ * @param string $file the php script used to serve files
-+ * @param int $contextid
-+ * @param string $component component
-+ * @param string $filearea filearea
-+ * @param array $ids other IDs will be used to check file permission
-+ * @param int $itemid
-+ * @param array $options
-+ * @return string
-+ */
-+function quiz_rewrite_question_urls($text, $file, $contextid, $component, $filearea, array $ids, $itemid, array $options=null) {
-+    global $CFG;
-+
-+    $options = (array)$options;
-+    if (!isset($options['forcehttps'])) {
-+        $options['forcehttps'] = false;
-+    }
-+
-+    if (!$CFG->slasharguments) {
-+        $file = $file . '?file=';
-+    }
-+
-+    $baseurl = "$CFG->wwwroot/$file/$contextid/$component/$filearea/";
-+
-+    if (!empty($ids)) {
-+        $baseurl .= (implode('/', $ids) . '/');
-+    }
-+
-+    if ($itemid !== null) {
-+        $baseurl .= "$itemid/";
-+    }
-+
-+    if ($options['forcehttps']) {
-+        $baseurl = str_replace('http://', 'https://', $baseurl);
-+    }
-+
-+    return str_replace('@@PLUGINFILE@@/', $baseurl, $text);
-+}
-+
-+/**
-+ * Called by pluginfile.php to serve files related to the 'question' core
-+ * component and for files belonging to qtypes.
-+ *
-+ * For files that relate to questions in a question_attempt, then we delegate to
-+ * a function in the component that owns the attempt (for example in the quiz,
-+ * or in core question preview) to get necessary inforation.
-+ *
-+ * (Note that, at the moment, all question file areas relate to questions in
-+ * attempts, so the If at the start of the last paragraph is always true.)
-+ *
-+ * Does not return, either calls send_file_not_found(); or serves the file.
-+ *
-+ * @param object $course course settings object
-+ * @param object $context context object
-+ * @param string $component the name of the component we are serving files for.
-+ * @param string $filearea the name of the file area.
-+ * @param array $args the remaining bits of the file path.
-+ * @param bool $forcedownload whether the user must be forced to download the file.
-+ */
-+function question_pluginfile($course, $context, $component, $filearea, $args, $forcedownload) {
-+    global $DB, $CFG;
-+
-+    list($context, $course, $cm) = get_context_info_array($context->id);
-+    require_login($course, false, $cm);
-+
-+    if ($filearea === 'export') {
-+        require_once($CFG->dirroot . '/question/editlib.php');
-+        $contexts = new question_edit_contexts($context);
-+        // check export capability
-+        $contexts->require_one_edit_tab_cap('export');
-+        $category_id = (int)array_shift($args);
-+        $format      = array_shift($args);
-+        $cattofile   = array_shift($args);
-+        $contexttofile = array_shift($args);
-+        $filename    = array_shift($args);
-+
-+        // load parent class for import/export
-+        require_once($CFG->dirroot . '/question/format.php');
-+        require_once($CFG->dirroot . '/question/editlib.php');
-+        require_once($CFG->dirroot . '/question/format/' . $format . '/format.php');
-+
-+        $classname = 'qformat_' . $format;
-+        if (!class_exists($classname)) {
-+            send_file_not_found();
-+        }
-+
-+        $qformat = new $classname();
-+
-+        if (!$category = $DB->get_record('question_categories', array('id' => $category_id))) {
-+            send_file_not_found();
-+        }
-+
-+        $qformat->setCategory($category);
-+        $qformat->setContexts($contexts->having_one_edit_tab_cap('export'));
-+        $qformat->setCourse($course);
-+
-+        if ($cattofile == 'withcategories') {
-+            $qformat->setCattofile(true);
-+        } else {
-+            $qformat->setCattofile(false);
-+        }
-+
-+        if ($contexttofile == 'withcontexts') {
-+            $qformat->setContexttofile(true);
-+        } else {
-+            $qformat->setContexttofile(false);
-+        }
-+
-+        if (!$qformat->exportpreprocess()) {
-+            send_file_not_found();
-+            print_error('exporterror', 'question', $thispageurl->out());
-+        }
-+
-+        // export data to moodle file pool
-+        if (!$content = $qformat->exportprocess(true)) {
-+            send_file_not_found();
-+        }
-+
-+        //DEBUG
-+        //echo '<textarea cols=90 rows=20>';
-+        //echo $content;
-+        //echo '</textarea>';
-+        //die;
-+        send_file($content, $filename, 0, 0, true, true, $qformat->mime_type());
-+    }
-+
-+    $attemptid = (int)array_shift($args);
-+    $questionid = (int)array_shift($args);
-+
-+
-+    if ($attemptid === 0) {
-+        // preview
-+        require_once($CFG->dirroot . '/question/previewlib.php');
-+        return question_preview_question_pluginfile($course, $context,
-+                $component, $filearea, $attemptid, $questionid, $args, $forcedownload);
-+
-+    } else {
-+        $module = $DB->get_field('question_attempts', 'modulename',
-+                array('id' => $attemptid));
-+
-+        $dir = get_component_directory($module);
-+        if (!file_exists("$dir/lib.php")) {
-+            send_file_not_found();
-+        }
-+        include_once("$dir/lib.php");
-+
-+        $filefunction = $module . '_question_pluginfile';
-+        if (!function_exists($filefunction)) {
-+            send_file_not_found();
-+        }
-+
-+        $filefunction($course, $context, $component, $filearea, $attemptid, $questionid,
-+                $args, $forcedownload);
-+
-+        send_file_not_found();
-+    }
-+}
-+
-+/**
-+ * Final test for whether a studnet should be allowed to see a particular file.
-+ * This delegates the decision to the question type plugin.
-+ *
-+ * @param object $question The question to be rendered.
-+ * @param object $state    The state to render the question in.
-+ * @param object $options  An object specifying the rendering options.
-+ * @param string $component the name of the component we are serving files for.
-+ * @param string $filearea the name of the file area.
-+ * @param array $args the remaining bits of the file path.
-+ * @param bool $forcedownload whether the user must be forced to download the file.
-+ */
-+function question_check_file_access($question, $state, $options, $contextid, $component,
-+        $filearea, $args, $forcedownload) {
-+    global $QTYPES;
-+    return $QTYPES[$question->qtype]->check_file_access($question, $state, $options, $contextid, $component,
-+            $filearea, $args, $forcedownload);
-+}
-+
-+/**
-+ * Create url for question export
-+ *
-+ * @param int $contextid, current context
-+ * @param int $categoryid, categoryid
-+ * @param string $format
-+ * @param string $withcategories
-+ * @param string $ithcontexts
-+ * @param moodle_url export file url
-+ */
-+function question_make_export_url($contextid, $categoryid, $format, $withcategories, $withcontexts, $filename) {
-+    global $CFG;
-+    $urlbase = "$CFG->httpswwwroot/pluginfile.php";
-+    return moodle_url::make_file_url($urlbase, "/$contextid/question/export/{$categoryid}/{$format}/{$withcategories}/{$withcontexts}/{$filename}", true);
-+}
diff --git a/question/todo/questionlib_qe.diff.txt b/question/todo/questionlib_qe.diff.txt
deleted file mode 100644 (file)
index 31ce5d9..0000000
+++ /dev/null
@@ -1,1720 +0,0 @@
-diff --git a/lib/questionlib.php b/lib/questionlib.php
-index 04752b2..3211e92 100644
---- a/lib/questionlib.php
-+++ b/lib/questionlib.php
-@@ -1,4 +1,20 @@
--<?php  // $Id$
-+<?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/>.
-+
- /**
-  * Code for handling and processing questions
-  *
-@@ -10,34 +26,18 @@
-  * TODO: separate those functions which form part of the API
-  *       from the helper functions.
-  *
-- * @author Martin Dougiamas and many others. This has recently been completely
-- *         rewritten by Alex Smith, Julian Sedding and Gustav Delius as part of
-- *         the Serving Mathematics project
-- *         {@link http://maths.york.ac.uk/serving_maths}
-- * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
-- * @package question
-+ * @package moodlecore
-+ * @subpackage questionbank
-+ * @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
-+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-  */
--/// CONSTANTS ///////////////////////////////////
--/**#@+
-- * The different types of events that can create question states
-- */
--define('QUESTION_EVENTOPEN', '0');      // The state was created by Moodle
--define('QUESTION_EVENTNAVIGATE', '1');  // The responses were saved because the student navigated to another page (this is not currently used)
--define('QUESTION_EVENTSAVE', '2');      // The student has requested that the responses should be saved but not submitted or validated
--define('QUESTION_EVENTGRADE', '3');     // Moodle has graded the responses. A SUBMIT event can be changed to a GRADE event by Moodle.
--define('QUESTION_EVENTDUPLICATE', '4'); // The responses submitted were the same as previously
--define('QUESTION_EVENTVALIDATE', '5');  // The student has requested a validation. This causes the responses to be saved as well, but not graded.
--define('QUESTION_EVENTCLOSEANDGRADE', '6'); // Moodle has graded the responses. A CLOSE event can be changed to a CLOSEANDGRADE event by Moodle.
--define('QUESTION_EVENTSUBMIT', '7');    // The student response has been submitted but it has not yet been marked
--define('QUESTION_EVENTCLOSE', '8');     // The response has been submitted and the session has been closed, either because the student requested it or because Moodle did it (e.g. because of a timelimit). The responses have not been graded.
--define('QUESTION_EVENTMANUALGRADE', '9');   // Grade was entered by teacher
--
--define('QUESTION_EVENTS_GRADED', QUESTION_EVENTGRADE.','.
--                    QUESTION_EVENTCLOSEANDGRADE.','.
--                    QUESTION_EVENTMANUALGRADE);
--/**#@-*/
-+require_once($CFG->dirroot . '/question/engine/lib.php');
-+require_once($CFG->dirroot . '/question/type/questiontype.php');
-+
-+
-+/// CONSTANTS ///////////////////////////////////
- /**#@+
-  * The core question types.
-@@ -59,7 +59,7 @@ define("ESSAY",         "essay");
-  * Constant determines the number of answer boxes supplied in the editing
-  * form for multiple choice and similar question types.
-  */
--define("QUESTION_NUMANS", "10");
-+define("QUESTION_NUMANS", 10);
- /**
-  * Constant determines the number of answer boxes supplied in the editing
-@@ -78,22 +78,10 @@ define("QUESTION_NUMANS_ADD", 3);
- /**
-  * The options used when popping up a question preview window in Javascript.
-  */
--define('QUESTION_PREVIEW_POPUP_OPTIONS', 'scrollbars=yes,resizable=yes,width=700,height=540');
-+define('QUESTION_PREVIEW_POPUP_OPTIONS', 'scrollbars=yes,resizable=yes,width=800,height=600');
- /**#@+
-- * Option flags for ->optionflags
-- * The options are read out via bitwise operation using these constants
-- */
--/**
-- * Whether the questions is to be run in adaptive mode. If this is not set then
-- * a question closes immediately after the first submission of responses. This
-- * is how question is Moodle always worked before version 1.5
-- */
--define('QUESTION_ADAPTIVE', 1);
--
--/**
-  * options used in forms that move files.
-- *
-  */
- define('QUESTION_FILENOTHINGSELECTED', 0);
- define('QUESTION_FILEDONOTHING', 1);
-@@ -103,64 +91,13 @@ define('QUESTION_FILEMOVELINKSONLY', 4);
- /**#@-*/
--/// QTYPES INITIATION //////////////////
--// These variables get initialised via calls to question_register_questiontype
--// as the question type classes are included.
--global $QTYPES, $QTYPE_MANUAL, $QTYPE_EXCLUDE_FROM_RANDOM;
--/**
-- * Array holding question type objects
-- */
--$QTYPES = array();
- /**
-- * String in the format "'type1','type2'" that can be used in SQL clauses like
-- * "WHERE q.type IN ($QTYPE_MANUAL)".
-+ * @global array holding question type objects
-+ * @deprecated
-  */
--$QTYPE_MANUAL = '';
--/**
-- * String in the format "'type1','type2'" that can be used in SQL clauses like
-- * "WHERE q.type NOT IN ($QTYPE_EXCLUDE_FROM_RANDOM)".
-- */
--$QTYPE_EXCLUDE_FROM_RANDOM = '';
--
--/**
-- * Add a new question type to the various global arrays above.
-- *
-- * @param object $qtype An instance of the new question type class.
-- */
--function question_register_questiontype($qtype) {
--    global $QTYPES, $QTYPE_MANUAL, $QTYPE_EXCLUDE_FROM_RANDOM;
--
--    $name = $qtype->name();
--    $QTYPES[$name] = $qtype;
--    if ($qtype->is_manual_graded()) {
--        if ($QTYPE_MANUAL) {
--            $QTYPE_MANUAL .= ',';
--        }
--        $QTYPE_MANUAL .= "'$name'";
--    }
--    if (!$qtype->is_usable_by_random()) {
--        if ($QTYPE_EXCLUDE_FROM_RANDOM) {
--            $QTYPE_EXCLUDE_FROM_RANDOM .= ',';
--        }
--        $QTYPE_EXCLUDE_FROM_RANDOM .= "'$name'";
--    }
--}
--
--require_once("$CFG->dirroot/question/type/questiontype.php");
-+global $QTYPES;
-+$QTYPES = question_bank::get_all_qtypes();
--// Load the questiontype.php file for each question type
--// These files in turn call question_register_questiontype()
--// with a new instance of each qtype class.
--$qtypenames= get_list_of_plugins('question/type');
--foreach($qtypenames as $qtypename) {
--    // Instanciates all plug-in question types
--    $qtypefilepath= "$CFG->dirroot/question/type/$qtypename/questiontype.php";
--
--    // echo "Loading $qtypename<br/>"; // Uncomment for debugging
--    if (is_readable($qtypefilepath)) {
--        require_once($qtypefilepath);
--    }
--}
- /**
-  * An array of question type names translated to the user's language, suitable for use when
-@@ -173,11 +110,10 @@ foreach($qtypenames as $qtypename) {
-  * @return array an array of question type names translated to the user's language.
-  */
- function question_type_menu() {
--    global $QTYPES;
-     static $menu_options = null;
-     if (is_null($menu_options)) {
-         $menu_options = array();
--        foreach ($QTYPES as $name => $qtype) {
-+        foreach (question_bank::get_all_qtypes() as $name => $qtype) {
-             $menuname = $qtype->menu_name();
-             if ($menuname) {
-                 $menu_options[$name] = $menuname;
-@@ -187,86 +123,58 @@ function question_type_menu() {
-     return $menu_options;
- }
--/// OTHER CLASSES /////////////////////////////////////////////////////////
--
--/**
-- * This holds the options that are set by the course module
-- */
--class cmoptions {
--    /**
--    * Whether a new attempt should be based on the previous one. If true
--    * then a new attempt will start in a state where all responses are set
--    * to the last responses from the previous attempt.
--    */
--    var $attemptonlast = false;
--
--    /**
--    * Various option flags. The flags are accessed via bitwise operations
--    * using the constants defined in the CONSTANTS section above.
--    */
--    var $optionflags = QUESTION_ADAPTIVE;
--
--    /**
--    * Determines whether in the calculation of the score for a question
--    * penalties for earlier wrong responses within the same attempt will
--    * be subtracted.
--    */
--    var $penaltyscheme = true;
--
--    /**
--    * The maximum time the user is allowed to answer the questions withing
--    * an attempt. This is measured in minutes so needs to be multiplied by
--    * 60 before compared to timestamps. If set to 0 no timelimit will be applied
--    */
--    var $timelimit = 0;
--
--    /**
--    * Timestamp for the closing time. Responses submitted after this time will
--    * be saved but no credit will be given for them.
--    */
--    var $timeclose = 9999999999;
--
--    /**
--    * The id of the course from withing which the question is currently being used
--    */
--    var $course = SITEID;
--
--    /**
--    * Whether the answers in a multiple choice question should be randomly
--    * shuffled when a new attempt is started.
--    */
--    var $shuffleanswers = true;
--
--    /**
--    * The number of decimals to be shown when scores are printed
--    */
--    var $decimalpoints = 2;
--}
--
--
- /// FUNCTIONS //////////////////////////////////////////////////////
- /**
-  * Returns an array of names of activity modules that use this question
-  *
-+ * @deprecated since Moodle 2.1. Use {@link questions_in_use} instead.
-+
-  * @param object $questionid
-  * @return array of strings
-  */
- function question_list_instances($questionid) {
-+    throw new coding_exception('question_list_instances has been deprectated. Please use questions_in_use instead.');
-+}
-+
-+/**
-+ * @param array $questionids of question ids.
-+ * @return boolean whether any of these questions are being used by any part of Moodle.
-+ */
-+function questions_in_use($questionids) {
-     global $CFG;
--    $instances = array();
--    $modules = get_records('modules');
--    foreach ($modules as $module) {
--        $fullmod = $CFG->dirroot . '/mod/' . $module->name;
--        if (file_exists($fullmod . '/lib.php')) {
--            include_once($fullmod . '/lib.php');
--            $fn = $module->name.'_question_list_instances';
-+
-+    if (question_engine::questions_in_use($questionids)) {
-+        return true;
-+    }
-+
-+    foreach (get_records('modules') as $module) {
-+        $lib = $CFG->dirroot . '/mod/' . $module->name . '/lib.php';
-+        if (file_exists($lib)) {
-+            include_once($lib);
-+
-+            $fn = $module->name . '_questions_in_use';
-             if (function_exists($fn)) {
--                $instances = $instances + $fn($questionid);
-+                if ($fn($questionids)) {
-+                    return true;
-+                }
-+            } else {
-+
-+                // Fallback for legacy modules.
-+                $fn = $module->name.'_question_list_instances';
-+                foreach ($questionids as $questionid) {
-+                    if (function_exists($fn)) {
-+                        $instances = $fn($questionid);
-+                        if (!empty($instances)) {
-+                            return true;
-+                        }
-+                    }
-+                }
-             }
-         }
-     }
--    return $instances;
-+
-+    return false;
- }
- /**
-@@ -298,39 +206,42 @@ function question_context_has_any_questions($context) {
-  * @return object ->gradeoptionsfull full array ->gradeoptions +ve only
-  */
- function get_grade_options() {
--    // define basic array of grades
-+    // define basic array of grades. This list comprises all fractions of the form:
-+    // a. p/q for q <= 6, 0 <= p <= q
-+    // b. p/10 for 0 <= p <= 10
-+    // c. 1/q for 1 <= q <= 10
-+    // d. 1/20
-     $grades = array(
--        1.00,
--        0.90,
--        0.83333,
--        0.80,
--        0.75,
--        0.70,
--        0.66666,
--        0.60,
--        0.50,
--        0.40,
--        0.33333,
--        0.30,
--        0.25,
--        0.20,
--        0.16666,
--        0.142857,
--        0.125,
--        0.11111,
--        0.10,
--        0.05,
--        0);
-+        1.0000000,
-+        0.9000000,
-+        0.8333333,
-+        0.8000000,
-+        0.7500000,
-+        0.7000000,
-+        0.6666667,
-+        0.6000000,
-+        0.5000000,
-+        0.4000000,
-+        0.3333333,
-+        0.3000000,
-+        0.2500000,
-+        0.2000000,
-+        0.1666667,
-+        0.1428571,
-+        0.1250000,
-+        0.1111111,
-+        0.1000000,
-+        0.0500000,
-+        0.0000000);
-     // iterate through grades generating full range of options
-     $gradeoptionsfull = array();
-     $gradeoptions = array();
-     foreach ($grades as $grade) {
-         $percentage = 100 * $grade;
--        $neggrade = -$grade;
--        $gradeoptions["$grade"] = "$percentage %";
--        $gradeoptionsfull["$grade"] = "$percentage %";
--        $gradeoptionsfull["$neggrade"] = -$percentage." %";
-+        $gradeoptions["$grade"] = $percentage . '%';
-+        $gradeoptionsfull["$grade"] = $percentage . '%';
-+        $gradeoptionsfull['' . (-$grade)] = (-$percentage) . '%';
-     }
-     $gradeoptionsfull["0"] = $gradeoptions["0"] = get_string("none");
-@@ -386,60 +297,43 @@ function match_grade_options($gradeoptionsfull, $grade, $matchgrades='error') {
- }
- /**
-- * Tests whether a category is in use by any activity module
-- *
-- * @return boolean
-- * @param integer $categoryid
-- * @param boolean $recursive Whether to examine category children recursively
-+ * @deprecated Since Moodle 2.1. Use {@link question_category_in_use} instead.
-+ * @param integer $categoryid a question category id.
-+ * @param boolean $recursive whether to check child categories too.
-+ * @return boolean whether any question in this category is in use.
-  */
- function question_category_isused($categoryid, $recursive = false) {
--
--    //Look at each question in the category
--    if ($questions = get_records('question', 'category', $categoryid)) {
--        foreach ($questions as $question) {
--            if (count(question_list_instances($question->id))) {
--                return true;
--            }
--        }
--    }
--
--    //Look under child categories recursively
--    if ($recursive) {
--        if ($children = get_records('question_categories', 'parent', $categoryid)) {
--            foreach ($children as $child) {
--                if (question_category_isused($child->id, $recursive)) {
--                    return true;
--                }
--            }
--        }
--    }
--
--    return false;
-+    throw new coding_exception('question_category_isused has been deprectated. Please use question_category_in_use instead.');
- }
- /**
-- * Deletes all data associated to an attempt from the database
-+ * Tests whether any question in a category is used by any part of Moodle.
-  *
-- * @param integer $attemptid The id of the attempt being deleted
-+ * @param integer $categoryid a question category id.
-+ * @param boolean $recursive whether to check child categories too.
-+ * @return boolean whether any question in this category is in use.
-  */
--function delete_attempt($attemptid) {
--    global $QTYPES;
-+function question_category_in_use($categoryid, $recursive = false) {
--    $states = get_records('question_states', 'attempt', $attemptid);
--    if ($states) {
--        $stateslist = implode(',', array_keys($states));
--
--        // delete question-type specific data
--        foreach ($QTYPES as $qtype) {
--            $qtype->delete_states($stateslist);
-+    // Look at each question in the category
-+    if ($questions = get_records('question', 'category', $categoryid)) {
-+        if (questions_in_use($questions)) {
-+            return true;
-         }
-     }
-+    if (!$recursive) {
-+        return false;
-+    }
--    // delete entries from all other question tables
--    // It is important that this is done only after calling the questiontype functions
--    delete_records("question_states", "attempt", $attemptid);
--    delete_records("question_sessions", "attemptid", $attemptid);
--    delete_records("question_attempts", "id", $attemptid);
-+    // Look under child categories recursively
-+    if ($children = get_records('question_categories', 'parent', $categoryid)) {
-+        foreach ($children as $child) {
-+            if (question_category_in_use($child->id, $recursive)) {
-+                return true;
-+            }
-+        }
-+    }
-+    return false;
- }
- /**
-@@ -448,7 +342,7 @@ function delete_attempt($attemptid) {
-  * It will not delete a question if it is used by an activity module
-  * @param object $question  The question being deleted
-  */
--function delete_question($questionid) {
-+function question_delete_question($questionid) {
-     global $QTYPES;
-     if (!$question = get_record('question', 'id', $questionid)) {
-@@ -459,12 +353,17 @@ function delete_question($questionid) {
-     }
-     // Do not delete a question if it is used by an activity module
--    if (count(question_list_instances($questionid))) {
-+    if (questions_in_use(array($questionid))) {
-         return;
-     }
--    // delete questiontype-specific data
-+    // Check permissions.
-     question_require_capability_on($question, 'edit');
-+
-+    $dm = new question_engine_data_mapper();
-+    $dm->delete_previews($questionid);
-+
-+    // Delete questiontype-specific data
-     if ($question) {
-         if (isset($QTYPES[$question->qtype])) {
-             $QTYPES[$question->qtype]->delete_question($questionid);
-@@ -473,26 +372,11 @@ function delete_question($questionid) {
-         echo "Question with id $questionid does not exist.<br />";
-     }
--    if ($states = get_records('question_states', 'question', $questionid)) {
--        $stateslist = implode(',', array_keys($states));
--
--        // delete questiontype-specific data
--        foreach ($QTYPES as $qtype) {
--            $qtype->delete_states($stateslist);
--        }
--    }
--
--    // delete entries from all other question tables
--    // It is important that this is done only after calling the questiontype functions
--    delete_records("question_answers", "question", $questionid);
--    delete_records("question_states", "question", $questionid);
--    delete_records("question_sessions", "questionid", $questionid);
--
--    // Now recursively delete all child questions
-+    // Recursively delete all child questions
-     if ($children = get_records('question', 'parent', $questionid)) {
-         foreach ($children as $child) {
-             if ($child->id != $questionid) {
--                delete_question($child->id);
-+                question_delete_question($child->id);
-             }
-         }
-     }
-@@ -517,7 +401,7 @@ function question_delete_course($course, $feedback=true) {
-     //Cache some strings
-     $strcatdeleted = get_string('unusedcategorydeleted', 'quiz');
-     $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
--    $categoriescourse = get_records('question_categories', 'contextid', $coursecontext->id, 'parent', 'id, parent, name');
-+    $categoriescourse = get_records('question_categories', 'contextid', $coursecontext->id, 'parent', 'id, parent, name, contextid');
-     if ($categoriescourse) {
-@@ -531,7 +415,7 @@ function question_delete_course($course, $feedback=true) {
-             //deleting questions
-             if ($questions = get_records("question", "category", $category->id)) {
-                 foreach ($questions as $question) {
--                    delete_question($question->id);
-+                    question_delete_question($question->id);
-                 }
-                 delete_records("question", "category", $category->id);
-             }
-@@ -578,7 +462,7 @@ function question_delete_course_category($category, $newcategory, $feedback=true
-                     // Try to delete each question.
-                     foreach ($questions as $question) {
--                        delete_question($question->id);
-+                        question_delete_question($question->id);
-                     }
-                     // Check to see if there were any questions that were kept because they are
-@@ -676,7 +560,7 @@ function question_delete_activity($cm, $feedback=true) {
-     //Cache some strings
-     $strcatdeleted = get_string('unusedcategorydeleted', 'quiz');
-     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
--    if ($categoriesmods = get_records('question_categories', 'contextid', $modcontext->id, 'parent', 'id, parent, name')){
-+    if ($categoriesmods = get_records('question_categories', 'contextid', $modcontext->id, 'parent', 'id, parent, name, contextid')){
-         //Sort categories following their tree (parent-child) relationships
-         //this will make the feedback more readable
-         $categoriesmods = sort_categories_by_tree($categoriesmods);
-@@ -687,7 +571,7 @@ function question_delete_activity($cm, $feedback=true) {
-             //deleting questions
-             if ($questions = get_records("question", "category", $category->id)) {
-                 foreach ($questions as $question) {
--                    delete_question($question->id);
-+                    question_delete_question($question->id);
-                 }
-                 delete_records("question", "category", $category->id);
-             }
-@@ -751,725 +635,148 @@ function questionbank_navigation_tabs(&$row, $contexts, $querystring) {
- }
- /**
-- * Private function to factor common code out of get_question_options().
-- *
-- * @param object $question the question to tidy.
-- * @return boolean true if successful, else false.
-+ * Generate the URL for starting a new preview of a given question with the given options.
-+ * @param integer $questionid the question to preview.
-+ * @param string $preferredbehaviour the behaviour to use for the preview.
-+ * @param float $maxmark the maximum to mark the question out of.
-+ * @param question_display_options $displayoptions the display options to use.
-+ * @return string the URL.
-  */
--function _tidy_question(&$question) {
--    global $QTYPES;
--    if (!array_key_exists($question->qtype, $QTYPES)) {
--        $question->qtype = 'missingtype';
--        $question->questiontext = '<p>' . get_string('warningmissingtype', 'quiz') . '</p>' . $question->questiontext;
--    }
--    $question->name_prefix = question_make_name_prefix($question->id);
--    return $QTYPES[$question->qtype]->get_question_options($question);
--}
--
--/**
-- * Updates the question objects with question type specific
-- * information by calling {@link get_question_options()}
-+function question_preview_url($questionid, $preferredbehaviour, $maxmark, $displayoptions) {
-+    global $CFG;
-+    return $CFG->wwwroot . '/question/preview.php?id=' . $questionid .
-+                '&behaviour=' . $preferredbehaviour .
-+                '&maxmark=' . $maxmark .
-+                '&correctness=' . $displayoptions->correctness .
-+                '&marks=' . $displayoptions->marks .
-+                '&markdp=' . $displayoptions->markdp .
-+                '&feedback=' . (bool) $displayoptions->feedback .
-+                '&generalfeedback=' . (bool) $displayoptions->generalfeedback .
-+                '&rightanswer=' . (bool) $displayoptions->rightanswer .
-+                '&history=' . (bool) $displayoptions->history;
-+}
-+
-+/**
-+ * Given a list of ids, load the basic information about a set of questions from the questions table.
-+ * The $join and $extrafields arguments can be used together to pull in extra data.
-+ * See, for example, the usage in mod/quiz/attemptlib.php, and
-+ * read the code below to see how the SQL is assembled. Throws exceptions on error.
-  *
-- * Can be called either with an array of question objects or with a single
-- * question object.
-+ * @global object
-+ * @global object
-+ * @param array $questionids array of question ids.
-+ * @param string $extrafields extra SQL code to be added to the query.
-+ * @param string $join extra SQL code to be added to the query.
-+ * @param array $extraparams values for any placeholders in $join.
-+ * You are strongly recommended to use named placeholder.
-  *
-- * @param mixed $questions Either an array of question objects to be updated
-- *         or just a single question object
-- * @return bool Indicates success or failure.
-+ * @return array partially complete question objects. You need to call get_question_options
-+ * on them before they can be properly used.
-  */
--function get_question_options(&$questions) {
--    if (is_array($questions)) { // deal with an array of questions
--        foreach ($questions as $i => $notused) {
--            if (!_tidy_question($questions[$i])) {
--                return false;
--            }
--        }
--        return true;
--    } else { // deal with single question
--        return _tidy_question($questions);
--    }
--}
--
--/**
--* Loads the most recent state of each question session from the database
--* or create new one.
--*
--* For each question the most recent session state for the current attempt
--* is loaded from the question_states table and the question type specific data and
--* responses are added by calling {@link restore_question_state()} which in turn
--* calls {@link restore_session_and_responses()} for each question.
--* If no states exist for the question instance an empty state object is
--* created representing the start of a session and empty question
--* type specific information and responses are created by calling
--* {@link create_session_and_responses()}.
--*
--* @return array           An array of state objects representing the most recent
--*                         states of the question sessions.
--* @param array $questions The questions for which sessions are to be restored or
--*                         created.
--* @param object $cmoptions
--* @param object $attempt  The attempt for which the question sessions are
--*                         to be restored or created.
--* @param mixed either the id of a previous attempt, if this attmpt is
--*                         building on a previous one, or false for a clean attempt.
--*/
--function get_question_states(&$questions, $cmoptions, $attempt, $lastattemptid = false) {
--    global $CFG, $QTYPES;
--
--    // get the question ids
--    $ids = array_keys($questions);
--    $questionlist = implode(',', $ids);
--
--    // The question field must be listed first so that it is used as the
--    // array index in the array returned by get_records_sql
--    $statefields = 'n.questionid as question, s.id, s.attempt, s.originalquestion, ' .
--            's.seq_number, s.answer, s.timestamp, s.event, s.grade, s.raw_grade, ' .
--            's.penalty, n.sumpenalty, n.manualcomment';
--    // Load the newest states for the questions
--    $sql = "SELECT $statefields".
--           "  FROM {$CFG->prefix}question_states s,".
--           "       {$CFG->prefix}question_sessions n".
--           " WHERE s.id = n.newest".
--           "   AND n.attemptid = '$attempt->uniqueid'".
--           "   AND n.questionid IN ($questionlist)";
--    $states = get_records_sql($sql);
--
--    // Load the newest graded states for the questions
--    $sql = "SELECT $statefields".
--           "  FROM {$CFG->prefix}question_states s,".
--           "       {$CFG->prefix}question_sessions n".
--           " WHERE s.id = n.newgraded".
--           "   AND n.attemptid = '$attempt->uniqueid'".
--           "   AND n.questionid IN ($questionlist)";
--    $gradedstates = get_records_sql($sql);
--
--    // loop through all questions and set the last_graded states
--    foreach ($ids as $i) {
--        if (isset($states[$i])) {
--            restore_question_state($questions[$i], $states[$i]);
--            if (isset($gradedstates[$i])) {
--                restore_question_state($questions[$i], $gradedstates[$i]);
--                $states[$i]->last_graded = $gradedstates[$i];
--            } else {
--                $states[$i]->last_graded = clone($states[$i]);
--            }
--        } else {
--            if ($lastattemptid) {
--                // If the new attempt is to be based on this previous attempt.
--                // Find the responses from the previous attempt and save them to the new session
--
--                // Load the last graded state for the question. Note, $statefields is
--                // the same as above, except that we don't want n.manualcomment.
--                $statefields = 'n.questionid as question, s.id, s.attempt, s.originalquestion, ' .
--                        's.seq_number, s.answer, s.timestamp, s.event, s.grade, s.raw_grade, ' .
--                        's.penalty, n.sumpenalty';
--                $sql = "SELECT $statefields".
--                       "  FROM {$CFG->prefix}question_states s,".
--                       "       {$CFG->prefix}question_sessions n".
--                       " WHERE s.id = n.newest".
--                       "   AND n.attemptid = '$lastattemptid'".
--                       "   AND n.questionid = '$i'";
--                if (!$laststate = get_record_sql($sql)) {
--                    // Only restore previous responses that have been graded
--                    continue;
--                }
--                // Restore the state so that the responses will be restored
--                restore_question_state($questions[$i], $laststate);
--                $states[$i] = clone($laststate);
--                unset($states[$i]->id);
--            } else {
--                // create a new empty state
--                $states[$i] = new object;
--                $states[$i]->question = $i;
--                $states[$i]->responses = array('' => '');
--                $states[$i]->raw_grade = 0;
--            }
--
--            // now fill/overide initial values
--            $states[$i]->attempt = $attempt->uniqueid;
--            $states[$i]->seq_number = 0;
--            $states[$i]->timestamp = $attempt->timestart;
--            $states[$i]->event = ($attempt->timefinish) ? QUESTION_EVENTCLOSE : QUESTION_EVENTOPEN;
--            $states[$i]->grade = 0;
--            $states[$i]->penalty = 0;
--            $states[$i]->sumpenalty = 0;
--            $states[$i]->manualcomment = '';
--
--            // Prevent further changes to the session from incrementing the
--            // sequence number
--            $states[$i]->changed = true;
--
--            if ($lastattemptid) {
--                // prepare the previous responses for new processing
--                $action = new stdClass;
--                $action->responses = $laststate->responses;
--                $action->timestamp = $laststate->timestamp;
--                $action->event = QUESTION_EVENTSAVE; //emulate save of questions from all pages MDL-7631
--
--                // Process these responses ...
--                question_process_responses($questions[$i], $states[$i], $action, $cmoptions, $attempt);
--
--                // Fix for Bug #5506: When each attempt is built on the last one,
--                // preserve the options from any previous attempt.
--                if ( isset($laststate->options) ) {
--                    $states[$i]->options = $laststate->options;
--                }
--            } else {
--                // Create the empty question type specific information
--                if (!$QTYPES[$questions[$i]->qtype]->create_session_and_responses(
--                        $questions[$i], $states[$i], $cmoptions, $attempt)) {
--                    return false;
--                }
--            }
--            $states[$i]->last_graded = clone($states[$i]);
--        }
-+function question_preload_questions($questionids, $extrafields = '', $join = '') {
-+    global $CFG;
-+    if (empty($questionids)) {
-+        return array();
-     }
--    return $states;
--}
--
--
--/**
--* Creates the run-time fields for the states
--*
--* Extends the state objects for a question by calling
--* {@link restore_session_and_responses()}
--* @param object $question The question for which the state is needed
--* @param object $state The state as loaded from the database
--* @return boolean Represents success or failure
--*/
--function restore_question_state(&$question, &$state) {
--    global $QTYPES;
--
--    // initialise response to the value in the answer field
--    $state->responses = array('' => addslashes($state->answer));
--    unset($state->answer);
--    $state->manualcomment = isset($state->manualcomment) ? addslashes($state->manualcomment) : '';
--
--    // Set the changed field to false; any code which changes the
--    // question session must set this to true and must increment
--    // ->seq_number. The save_question_session
--    // function will save the new state object to the database if the field is
--    // set to true.
--    $state->changed = false;
--
--    // Load the question type specific data
--    return $QTYPES[$question->qtype]
--            ->restore_session_and_responses($question, $state);
--
--}
--
--/**
--* Saves the current state of the question session to the database
--*
--* The state object representing the current state of the session for the
--* question is saved to the question_states table with ->responses[''] saved
--* to the answer field of the database table. The information in the
--* question_sessions table is updated.
--* The question type specific data is then saved.
--* @return mixed           The id of the saved or updated state or false
--* @param object $question The question for which session is to be saved.
--* @param object $state    The state information to be saved. In particular the
--*                         most recent responses are in ->responses. The object
--*                         is updated to hold the new ->id.
--*/
--function save_question_session(&$question, &$state) {
--    global $QTYPES;
--    // Check if the state has changed
--    if (!$state->changed && isset($state->id)) {
--        return $state->id;
-+    if ($join) {
-+        $join = ' JOIN '.$join;
-     }
--    // Set the legacy answer field
--    $state->answer = isset($state->responses['']) ? $state->responses[''] : '';
--
--    // Save the state
--    if (!empty($state->update)) { // this forces the old state record to be overwritten
--        update_record('question_states', $state);
--    } else {
--        if (!$state->id = insert_record('question_states', $state)) {
--            unset($state->id);
--            unset($state->answer);
--            return false;
--        }
-+    if ($extrafields) {
-+        $extrafields = ', ' . $extrafields;
-     }
-+    $sql = 'SELECT q.*' . $extrafields . " FROM {$CFG->prefix}question q" . $join .
-+            ' WHERE q.id IN (' . implode(',', $questionids) . ')';
--    // create or update the session
--    if (!$session = get_record('question_sessions', 'attemptid',
--            $state->attempt, 'questionid', $question->id)) {
--        $session->attemptid = $state->attempt;
--        $session->questionid = $question->id;
--        $session->newest = $state->id;
--        // The following may seem weird, but the newgraded field needs to be set
--        // already even if there is no graded state yet.
--        $session->newgraded = $state->id;
--        $session->sumpenalty = $state->sumpenalty;
--        $session->manualcomment = $state->manualcomment;
--        if (!insert_record('question_sessions', $session)) {
--            error('Could not insert entry in question_sessions');
--        }
--    } else {
--        $session->newest = $state->id;
--        if (question_state_is_graded($state) or $state->event == QUESTION_EVENTOPEN) {
--            // this state is graded or newly opened, so it goes into the lastgraded field as well
--            $session->newgraded = $state->id;
--            $session->sumpenalty = $state->sumpenalty;
--            $session->manualcomment = $state->manualcomment;
--        } else {
--            $session->manualcomment = addslashes($session->manualcomment);
--        }
--        update_record('question_sessions', $session);
-+    // Load the questions
-+    if (!$questions = get_records_sql($sql)) {
-+        return array();
-     }
--    unset($state->answer);
--
--    // Save the question type specific state information and responses
--    if (!$QTYPES[$question->qtype]->save_session_and_responses(
--     $question, $state)) {
--        return false;
-+    foreach ($questions as $question) {
-+        $question->_partiallyloaded = true;
-     }
--    // Reset the changed flag
--    $state->changed = false;
--    return $state->id;
--}
--/**
--* Determines whether a state has been graded by looking at the event field
--*
--* @return boolean         true if the state has been graded
--* @param object $state
--*/
--function question_state_is_graded($state) {
--    $gradedevents = explode(',', QUESTION_EVENTS_GRADED);
--    return (in_array($state->event, $gradedevents));
--}
-+    // Note, a possible optimisation here would be to not load the TEXT fields
-+    // (that is, questiontext and generalfeedback) here, and instead load them in
-+    // question_load_questions. That would add one DB query, but reduce the amount
-+    // of data transferred most of the time. I am not going to do this optimisation
-+    // until it is shown to be worthwhile.
--/**
--* Determines whether a state has been closed by looking at the event field
--*
--* @return boolean         true if the state has been closed
--* @param object $state
--*/
--function question_state_is_closed($state) {
--    return ($state->event == QUESTION_EVENTCLOSE
--        or $state->event == QUESTION_EVENTCLOSEANDGRADE
--        or $state->event == QUESTION_EVENTMANUALGRADE);
-+    return $questions;
- }
--
- /**
-- * Extracts responses from submitted form
-+ * Load a set of questions, given a list of ids. The $join and $extrafields arguments can be used
-+ * together to pull in extra data. See, for example, the usage in mod/quiz/attempt.php, and
-+ * read the code below to see how the SQL is assembled. Throws exceptions on error.
-  *
-- * This can extract the responses given to one or several questions present on a page
-- * It returns an array with one entry for each question, indexed by question id
-- * Each entry is an object with the properties
-- *  ->event     The event that has triggered the submission. This is determined by which button
-- *               the user has pressed.
-- *  ->responses An array holding the responses to an individual question, indexed by the
-- *               name of the corresponding form element.
-- *  ->timestamp A unix timestamp
-- * @return array            array of action objects, indexed by question ids.
-- * @param array $questions  an array containing at least all questions that are used on the form
-- * @param array $formdata   the data submitted by the form on the question page
-- * @param integer $defaultevent  the event type used if no 'mark' or 'validate' is submitted
-+ * @param array $questionids array of question ids.
-+ * @param string $extrafields extra SQL code to be added to the query.
-+ * @param string $join extra SQL code to be added to the query.
-+ * @param array $extraparams values for any placeholders in $join.
-+ * You are strongly recommended to use named placeholder.
-+ *
-+ * @return array question objects.
-  */
--function question_extract_responses($questions, $formdata, $defaultevent=QUESTION_EVENTSAVE) {
--
--    $time = time();
--    $actions = array();
--    foreach ($formdata as $key => $response) {
--        // Get the question id from the response name
--        if (false !== ($quid = question_get_id_from_name_prefix($key))) {
--            // check if this is a valid id
--            if (!isset($questions[$quid])) {
--                error('Form contained question that is not in questionids');
--            }
--
--            // Remove the name prefix from the name
--            //decrypt trying
--            $key = substr($key, strlen($questions[$quid]->name_prefix));
--            if (false === $key) {
--                $key = '';
--            }
--            // Check for question validate and mark buttons & set events
--            if ($key === 'validate') {
--                $actions[$quid]->event = QUESTION_EVENTVALIDATE;
--            } else if ($key === 'submit') {
--                $actions[$quid]->event = QUESTION_EVENTSUBMIT;
--            } else {
--                $actions[$quid]->event = $defaultevent;
--            }
--
--            // Update the state with the new response
--            $actions[$quid]->responses[$key] = $response;
-+function question_load_questions($questionids, $extrafields = '', $join = '') {
-+    $questions = question_preload_questions($questionids, $extrafields, $join);
--            // Set the timestamp
--            $actions[$quid]->timestamp = $time;
--        }
--    }
--    foreach ($actions as $quid => $notused) {
--        ksort($actions[$quid]->responses);
-+    // Load the question type specific information
-+    if (!get_question_options($questions)) {
-+        return 'Could not load the question options';
-     }
--    return $actions;
--}
--
--
--/**
-- * Returns the html for question feedback image.
-- * @param float   $fraction  value representing the correctness of the user's
-- *                           response to a question.
-- * @param boolean $selected  whether or not the answer is the one that the
-- *                           user picked.
-- * @return string
-- */
--function question_get_feedback_image($fraction, $selected=true) {
--
--    global $CFG;
--    if ($fraction >= 1.0) {
--        if ($selected) {
--            $feedbackimg = '<img src="'.$CFG->pixpath.'/i/tick_green_big.gif" '.
--                            'alt="'.get_string('correct', 'quiz').'" class="icon" />';
--        } else {
--            $feedbackimg = '<img src="'.$CFG->pixpath.'/i/tick_green_small.gif" '.
--                            'alt="'.get_string('correct', 'quiz').'" class="icon" />';
--        }
--    } else if ($fraction > 0.0 && $fraction < 1.0) {
--        if ($selected) {
--            $feedbackimg = '<img src="'.$CFG->pixpath.'/i/tick_amber_big.gif" '.
--                            'alt="'.get_string('partiallycorrect', 'quiz').'" class="icon" />';
--        } else {
--            $feedbackimg = '<img src="'.$CFG->pixpath.'/i/tick_amber_small.gif" '.
--                            'alt="'.get_string('partiallycorrect', 'quiz').'" class="icon" />';
--        }
--    } else {
--        if ($selected) {
--            $feedbackimg = '<img src="'.$CFG->pixpath.'/i/cross_red_big.gif" '.
--                            'alt="'.get_string('incorrect', 'quiz').'" class="icon" />';
--        } else {
--            $feedbackimg = '<img src="'.$CFG->pixpath.'/i/cross_red_small.gif" '.
--                            'alt="'.get_string('incorrect', 'quiz').'" class="icon" />';
--        }
--    }
--    return $feedbackimg;
-+    return $questions;
- }
--
- /**
-- * Returns the class name for question feedback.
-- * @param float  $fraction  value representing the correctness of the user's
-- *                          response to a question.
-- * @return string
-+ * Private function to factor common code out of get_question_options().
-+ *
-+ * @param object $question the question to tidy.
-+ * @param boolean $loadtags load the question tags from the tags table. Optional, default false.
-+ * @return boolean true if successful, else false.
-  */
--function question_get_feedback_class($fraction) {
--
--    global $CFG;
--
--    if ($fraction >= 1.0) {
--        $class = 'correct';
--    } else if ($fraction > 0.0 && $fraction < 1.0) {
--        $class = 'partiallycorrect';
--    } else {
--        $class = 'incorrect';
-+function _tidy_question(&$question, $loadtags = false) {
-+    global $CFG, $QTYPES;
-+    if (!array_key_exists($question->qtype, $QTYPES)) {
-+        $question->qtype = 'missingtype';
-+        $question->questiontext = '<p>' . get_string('warningmissingtype', 'quiz') . '</p>' . $question->questiontext;
-     }
--    return $class;
--}
--
--
--/**
--* For a given question in an attempt we walk the complete history of states
--* and recalculate the grades as we go along.
--*
--* This is used when a question is changed and old student
--* responses need to be marked with the new version of a question.
--*
--* TODO: Make sure this is not quiz-specific
--*
--* @return boolean            Indicates whether the grade has changed
--* @param object  $question   A question object
--* @param object  $attempt    The attempt, in which the question needs to be regraded.
--* @param object  $cmoptions
--* @param boolean $verbose    Optional. Whether to print progress information or not.
--*/
--function regrade_question_in_attempt($question, $attempt, $cmoptions, $verbose=false) {
--
--    // load all states for this question in this attempt, ordered in sequence
--    if ($states = get_records_select('question_states',
--            "attempt = '{$attempt->uniqueid}' AND question = '{$question->id}'",
--            'seq_number ASC')) {
--        $states = array_values($states);
--
--        // Subtract the grade for the latest state from $attempt->sumgrades to get the
--        // sumgrades for the attempt without this question.
--        $attempt->sumgrades -= $states[count($states)-1]->grade;
--
--        // Initialise the replaystate
--        $state = clone($states[0]);
--        $state->manualcomment = get_field('question_sessions', 'manualcomment', 'attemptid',
--                $attempt->uniqueid, 'questionid', $question->id);
--        restore_question_state($question, $state);
--        $state->sumpenalty = 0.0;
--        $replaystate = clone($state);
--        $replaystate->last_graded = $state;
--
--        $changed = false;
--        for($j = 1; $j < count($states); $j++) {
--            restore_question_state($question, $states[$j]);
--            $action = new stdClass;
--            $action->responses = $states[$j]->responses;
--            $action->timestamp = $states[$j]->timestamp;
--
--            // Change event to submit so that it will be reprocessed
--            if (QUESTION_EVENTCLOSE == $states[$j]->event
--                    or QUESTION_EVENTGRADE == $states[$j]->event
--                    or QUESTION_EVENTCLOSEANDGRADE == $states[$j]->event) {
--                $action->event = QUESTION_EVENTSUBMIT;
--
--            // By default take the event that was saved in the database
--            } else {
--                $action->event = $states[$j]->event;
--            }
--
--            if ($action->event == QUESTION_EVENTMANUALGRADE) {
--                // Ensure that the grade is in range - in the past this was not checked,
--                // but now it is (MDL-14835) - so we need to ensure the data is valid before
--                // proceeding.
--                if ($states[$j]->grade < 0) {
--                    $states[$j]->grade = 0;
--                } else if ($states[$j]->grade > $question->maxgrade) {
--                    $states[$j]->grade = $question->maxgrade;
--                }
--                $error = question_process_comment($question, $replaystate, $attempt,
--                        $replaystate->manualcomment, $states[$j]->grade);
--                if (is_string($error)) {
--                     notify($error);
--                }
--            } else {
--
--                // Reprocess (regrade) responses
--                if (!question_process_responses($question, $replaystate,
--                        $action, $cmoptions, $attempt)) {
--                    $verbose && notify("Couldn't regrade state #{$state->id}!");
--                }
--            }
--
--            // We need rounding here because grades in the DB get truncated
--            // e.g. 0.33333 != 0.3333333, but we want them to be equal here
--            if ((round((float)$replaystate->raw_grade, 5) != round((float)$states[$j]->raw_grade, 5))
--                    or (round((float)$replaystate->penalty, 5) != round((float)$states[$j]->penalty, 5))
--                    or (round((float)$replaystate->grade, 5) != round((float)$states[$j]->grade, 5))) {
--                $changed = true;
--            }
--
--            $replaystate->id = $states[$j]->id;
--            $replaystate->changed = true;
--            $replaystate->update = true; // This will ensure that the existing database entry is updated rather than a new one created
--            save_question_session($question, $replaystate);
-+    if ($success = $QTYPES[$question->qtype]->get_question_options($question)) {
-+        if (isset($question->_partiallyloaded)) {
-+            unset($question->_partiallyloaded);
-         }
--        if ($changed) {
--            // TODO, call a method in quiz to do this, where 'quiz' comes from
--            // the question_attempts table.
--            update_record('quiz_attempts', $attempt);
--        }
--
--        return $changed;
--    }
--    return false;
--}
--
--/**
--* Processes an array of student responses, grading and saving them as appropriate
--*
--* @param object $question Full question object, passed by reference
--* @param object $state    Full state object, passed by reference
--* @param object $action   object with the fields ->responses which
--*                         is an array holding the student responses,
--*                         ->action which specifies the action, e.g., QUESTION_EVENTGRADE,
--*                         and ->timestamp which is a timestamp from when the responses
--*                         were submitted by the student.
--* @param object $cmoptions
--* @param object $attempt  The attempt is passed by reference so that
--*                         during grading its ->sumgrades field can be updated
--* @return boolean         Indicates success/failure
--*/
--function question_process_responses(&$question, &$state, $action, $cmoptions, &$attempt) {
--    global $QTYPES;
--
--    // if no responses are set initialise to empty response
--    if (!isset($action->responses)) {
--        $action->responses = array('' => '');
-     }
--
--    // make sure these are gone!
--    unset($action->responses['submit'], $action->responses['validate']);
--
--    // Check the question session is still open
--    if (question_state_is_closed($state)) {
--        return true;
--    }
--
--    // If $action->event is not set that implies saving
--    if (! isset($action->event)) {
--        debugging('Ambiguous action in question_process_responses.' , DEBUG_DEVELOPER);
--        $action->event = QUESTION_EVENTSAVE;
--    }
--    // If submitted then compare against last graded
--    // responses, not last given responses in this case
--    if (question_isgradingevent($action->event)) {
--        $state->responses = $state->last_graded->responses;
--    }
--
--    // Check for unchanged responses (exactly unchanged, not equivalent).
--    // We also have to catch questions that the student has not yet attempted
--    $sameresponses = $QTYPES[$question->qtype]->compare_responses($question, $action, $state);
--    if (!empty($state->last_graded) && $state->last_graded->event == QUESTION_EVENTOPEN &&
--            question_isgradingevent($action->event)) {
--        $sameresponses = false;
--    }
--
--    // If the response has not been changed then we do not have to process it again
--    // unless the attempt is closing or validation is requested
--    if ($sameresponses and QUESTION_EVENTCLOSE != $action->event
--            and QUESTION_EVENTVALIDATE != $action->event) {
--        return true;
--    }
--
--    // Roll back grading information to last graded state and set the new
--    // responses
--    $newstate = clone($state->last_graded);
--    $newstate->responses = $action->responses;
--    $newstate->seq_number = $state->seq_number + 1;
--    $newstate->changed = true; // will assure that it gets saved to the database
--    $newstate->last_graded = clone($state->last_graded);
--    $newstate->timestamp = $action->timestamp;
--    $state = $newstate;
--
--    // Set the event to the action we will perform. The question type specific
--    // grading code may override this by setting it to QUESTION_EVENTCLOSE if the
--    // attempt at the question causes the session to close
--    $state->event = $action->event;
--
--    if (!question_isgradingevent($action->event)) {
--        // Grade the response but don't update the overall grade
--        if (!$QTYPES[$question->qtype]->grade_responses($question, $state, $cmoptions)) {
--            return false;
--        }
--
--        // Temporary hack because question types are not given enough control over what is going
--        // on. Used by Opaque questions.
--        // TODO fix this code properly.
--        if (!empty($state->believeevent)) {
--            // If the state was graded we need to ...
--            if (question_state_is_graded($state)) {
--                question_apply_penalty_and_timelimit($question, $state, $attempt, $cmoptions);
--
--                // update the attempt grade
--                $attempt->sumgrades -= (float)$state->last_graded->grade;
--                $attempt->sumgrades += (float)$state->grade;
--
--                // and update the last_graded field.
--                unset($state->last_graded);
--                $state->last_graded = clone($state);
--                unset($state->last_graded->changed);
--            }
--        } else {
--            // Don't allow the processing to change the event type
--            $state->event = $action->event;
--        }
--
--    } else { // grading event
--
--        // Unless the attempt is closing, we want to work out if the current responses
--        // (or equivalent responses) were already given in the last graded attempt.
--        if(QUESTION_EVENTCLOSE != $action->event && QUESTION_EVENTOPEN != $state->last_graded->event &&
--                $QTYPES[$question->qtype]->compare_responses($question, $state, $state->last_graded)) {
--            $state->event = QUESTION_EVENTDUPLICATE;
--        }
--
--        // If we did not find a duplicate or if the attempt is closing, perform grading
--        if ((!$sameresponses and QUESTION_EVENTDUPLICATE != $state->event) or
--                QUESTION_EVENTCLOSE == $action->event) {
--            if (!$QTYPES[$question->qtype]->grade_responses($question, $state, $cmoptions)) {
--                return false;
--            }
--
--            // Calculate overall grade using correct penalty method
--            question_apply_penalty_and_timelimit($question, $state, $attempt, $cmoptions);
--        }
--
--        // If the state was graded we need to ...
--        if (question_state_is_graded($state)) {
--            // update the attempt grade
--            $attempt->sumgrades -= (float)$state->last_graded->grade;
--            $attempt->sumgrades += (float)$state->grade;
--
--            // and update the last_graded field.
--            unset($state->last_graded);
--            $state->last_graded = clone($state);
--            unset($state->last_graded->changed);
--        }
-+    if ($loadtags && !empty($CFG->usetags)) {
-+        require_once($CFG->dirroot . '/tag/lib.php');
-+        $question->tags = tag_get_tags_array('question', $question->id);
-     }
--    $attempt->timemodified = $action->timestamp;
--
--    return true;
-+    return $success;
- }
- /**
--* Determine if event requires grading
--*/
--function question_isgradingevent($event) {
--    return (QUESTION_EVENTSUBMIT == $event || QUESTION_EVENTCLOSE == $event);
--}
--
--/**
--* Applies the penalty from the previous graded responses to the raw grade
--* for the current responses
--*
--* The grade for the question in the current state is computed by subtracting the
--* penalty accumulated over the previous graded responses at the question from the
--* raw grade. If the timestamp is more than 1 minute beyond the end of the attempt
--* the grade is set to zero. The ->grade field of the state object is modified to
--* reflect the new grade but is never allowed to decrease.
--* @param object $question The question for which the penalty is to be applied.
--* @param object $state    The state for which the grade is to be set from the
--*                         raw grade and the cumulative penalty from the last
--*                         graded state. The ->grade field is updated by applying
--*                         the penalty scheme determined in $cmoptions to the ->raw_grade and
--*                         ->last_graded->penalty fields.
--* @param object $cmoptions  The options set by the course module.
--*                           The ->penaltyscheme field determines whether penalties
--*                           for incorrect earlier responses are subtracted.
--*/
--function question_apply_penalty_and_timelimit(&$question, &$state, $attempt, $cmoptions) {
--    // TODO. Quiz dependancy. The fact that the attempt that is passed in here
--    // is from quiz_attempts, and we use things like $cmoptions->timelimit.
--
--    // deal with penalty
--    if ($cmoptions->penaltyscheme) {
--        $state->grade = $state->raw_grade - $state->sumpenalty;
--        $state->sumpenalty += (float) $state->penalty;
--    } else {
--        $state->grade = $state->raw_grade;
--    }
--
--    // deal with timelimit
--    if ($cmoptions->timelimit) {
--        // We allow for 5% uncertainty in the following test
--        if ($state->timestamp - $attempt->timestart > $cmoptions->timelimit * 63) {
--            $cm = get_coursemodule_from_instance('quiz', $cmoptions->id);
--            if (!has_capability('mod/quiz:ignoretimelimits', get_context_instance(CONTEXT_MODULE, $cm->id),
--                    $attempt->userid, false)) {
--                $state->grade = 0;
-+ * Updates the question objects with question type specific
-+ * information by calling {@link get_question_options()}
-+ *
-+ * Can be called either with an array of question objects or with a single
-+ * question object.
-+ *
-+ * @param mixed $questions Either an array of question objects to be updated
-+ *         or just a single question object
-+ * @param boolean $loadtags load the question tags from the tags table. Optional, default false.
-+ * @return bool Indicates success or failure.
-+ */
-+function get_question_options(&$questions, $loadtags = false) {
-+    if (is_array($questions)) { // deal with an array of questions
-+        foreach ($questions as $i => $notused) {
-+            if (!_tidy_question($questions[$i], $loadtags)) {
-+                return false;
-             }
-         }
-+        return true;
-+    } else { // deal with single question
-+        return _tidy_question($questions, $loadtags);
-     }
--
--    // deal with closing time
--    if ($cmoptions->timeclose and $state->timestamp > ($cmoptions->timeclose + 60) // allowing 1 minute lateness
--             and !$attempt->preview) { // ignore closing time for previews
--        $state->grade = 0;
--    }
--
--    // Ensure that the grade does not go down
--    $state->grade = max($state->grade, $state->last_graded->grade);
- }
- /**
-@@ -1497,168 +804,6 @@ function print_question_icon($question, $return = false) {
- }
- /**
--* Returns a html link to the question image if there is one
--*
--* @return string The html image tag or the empy string if there is no image.
--* @param object $question The question object
--*/
--function get_question_image($question) {
--
--    global $CFG;
--    $img = '';
--
--    if (!$category = get_record('question_categories', 'id', $question->category)){
--        error('invalid category id '.$question->category);
--    }
--    $coursefilesdir = get_filesdir_from_context(get_context_instance_by_id($category->contextid));
--
--    if ($question->image) {
--
--        if (substr(strtolower($question->image), 0, 7) == 'http://') {
--            $img .= $question->image;
--
--        } else {
--            require_once($CFG->libdir .'/filelib.php');
--            $img = get_file_url("$coursefilesdir/{$question->image}");
--        }      
--    }
--    return $img;
--}
--
--function question_print_comment_box($question, $state, $attempt, $url) {
--    global $CFG, $QTYPES;
--
--    $prefix = 'response';
--    $usehtmleditor = can_use_richtext_editor();
--    if (!question_state_is_graded($state) && $QTYPES[$question->qtype]->is_question_manual_graded($question, $attempt->layout)) {
--        $grade = '';
--    } else {
--        $grade = round($state->last_graded->grade, 3);
--    }
--    echo '<form method="post" action="'.$url.'">';
--    include($CFG->dirroot.'/question/comment.html');
--    echo '<input type="hidden" name="attempt" value="'.$attempt->uniqueid.'" />';
--    echo '<input type="hidden" name="question" value="'.$question->id.'" />';
--    echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
--    echo '<input type="submit" name="submit" value="'.get_string('save', 'quiz').'" />';
--    echo '</form>';
--
--    if ($usehtmleditor) {
--        use_html_editor();
--    }
--}
--
--/**
-- * Process a manual grading action. That is, use $comment and $grade to update
-- * $state and $attempt. The attempt and the comment text are stored in the
-- * database. $state is only updated in memory, it is up to the call to store
-- * that, if appropriate.
-- *
-- * @param object $question the question
-- * @param object $state the state to be updated.
-- * @param object $attempt the attempt the state belongs to, to be updated.
-- * @param string $comment the new comment from the teacher.
-- * @param mixed $grade the grade the teacher assigned, or '' to not change the grade.
-- * @return mixed true on success, a string error message if a problem is detected
-- *         (for example score out of range).
-- */
--function question_process_comment($question, &$state, &$attempt, $comment, $grade) {
--    $grade = trim($grade);
--    if ($grade < 0 || $grade > $question->maxgrade) {
--        $a = new stdClass;
--        $a->grade = $grade;
--        $a->maxgrade = $question->maxgrade;
--        $a->name = $question->name;
--        return get_string('errormanualgradeoutofrange', 'question', $a);
--    }
--
--    // Update the comment and save it in the database
--    $comment = trim($comment);
--    $state->manualcomment = $comment;
--    if (!set_field('question_sessions', 'manualcomment', $comment, 'attemptid', $attempt->uniqueid, 'questionid', $question->id)) {
--        return get_string('errorsavingcomment', 'question', $question);
--    }
--
--    // Update the attempt if the score has changed.
--    if ($grade !== '' && (abs($state->last_graded->grade - $grade) > 0.002 || $state->last_graded->event != QUESTION_EVENTMANUALGRADE)) {
--        $attempt->sumgrades = $attempt->sumgrades - $state->last_graded->grade + $grade;
--        $attempt->timemodified = time();
--        if (!update_record('quiz_attempts', $attempt)) {
--            return get_string('errorupdatingattempt', 'question', $attempt);
--        }
--
--        // We want to update existing state (rather than creating new one) if it
--        // was itself created by a manual grading event.
--        $state->update = $state->event == QUESTION_EVENTMANUALGRADE;
--
--        // Update the other parts of the state object.
--        $state->raw_grade = $grade;
--        $state->grade = $grade;
--        $state->penalty = 0;
--        $state->timestamp = time();
--        $state->seq_number++;
--        $state->event = QUESTION_EVENTMANUALGRADE;
--
--        // Update the last graded state (don't simplify!)
--        unset($state->last_graded);
--        $state->last_graded = clone($state);
--
--        // We need to indicate that the state has changed in order for it to be saved.
--        $state->changed = 1;
--    }
--
--    return true;
--}
--
--/**
--* Construct name prefixes for question form element names
--*
--* Construct the name prefix that should be used for example in the
--* names of form elements created by questions.
--* This is called by {@link get_question_options()}
--* to set $question->name_prefix.
--* This name prefix includes the question id which can be
--* extracted from it with {@link question_get_id_from_name_prefix()}.
--*
--* @return string
--* @param integer $id  The question id
--*/
--function question_make_name_prefix($id) {
--    return 'resp' . $id . '_';
--}
--
--/**
--* Extract question id from the prefix of form element names
--*
--* @return integer      The question id
--* @param string $name  The name that contains a prefix that was
--*                      constructed with {@link question_make_name_prefix()}
--*/
--function question_get_id_from_name_prefix($name) {
--    if (!preg_match('/^resp([0-9]+)_/', $name, $matches))
--        return false;
--    return (integer) $matches[1];
--}
--
--/**
-- * Returns the unique id for a new attempt
-- *
-- * Every module can keep their own attempts table with their own sequential ids but
-- * the question code needs to also have a unique id by which to identify all these
-- * attempts. Hence a module, when creating a new attempt, calls this function and
-- * stores the return value in the 'uniqueid' field of its attempts table.
-- */
--function question_new_attempt_uniqueid($modulename='quiz') {
--    global $CFG;
--    $attempt = new stdClass;
--    $attempt->modulename = $modulename;
--    if (!$id = insert_record('question_attempts', $attempt)) {
--        error('Could not create new entry in question_attempts table');
--    }
--    return $id;
--}
--
--/**
-  * Creates a stamp that uniquely identifies this version of the question
-  *
-  * In future we want this to use a hash of the question data to guarantee that
-@@ -1674,33 +819,8 @@ function question_hash($question) {
- /// FUNCTIONS THAT SIMPLY WRAP QUESTIONTYPE METHODS //////////////////////////////////
- /**
-- * Get the HTML that needs to be included in the head tag when the
-- * questions in $questionlist are printed in the gives states.
-- *
-- * @param array $questionlist a list of questionids of the questions what will appear on this page.
-- * @param array $questions an array of question objects, whose keys are question ids.
-- *      Must contain all the questions in $questionlist
-- * @param array $states an array of question state objects, whose keys are question ids.
-- *      Must contain the state of all the questions in $questionlist
-- *
-- * @return string some HTML code that can go inside the head tag.
-- */
--function get_html_head_contributions(&$questionlist, &$questions, &$states) {
--    global $QTYPES;
--
--    $contributions = array();
--    foreach ($questionlist as $questionid) {
--        $question = $questions[$questionid];
--        $contributions = array_merge($contributions,
--                $QTYPES[$question->qtype]->get_html_head_contributions(
--                $question, $states[$questionid]));
--    }
--    return implode("\n", array_unique($contributions));
--}
--
--/**
-- * Like @see{get_html_head_contributions} but for the editing page
-- * question/question.php.
-+ * Get anything that needs to be included in the head of the question editing page
-+ * for a particular question type. This function is called by question/question.php.
-  *
-  * @param $question A question object. Only $question->qtype is used.
-  * @return string some HTML code that can go inside the head tag.
-@@ -1712,20 +832,6 @@ function get_editing_head_contributions($question) {
- }
- /**
-- * Prints a question
-- *
-- * Simply calls the question type specific print_question() method.
-- * @param object $question The question to be rendered.
-- * @param object $state    The state to render the question in.
-- * @param integer $number  The number for this question.
-- * @param object $cmoptions  The options specified by the course module
-- * @param object $options  An object specifying the rendering options.
-- */
--function print_question(&$question, &$state, $number, $cmoptions, $options=null) {
--    global $QTYPES;
--    $QTYPES[$question->qtype]->print_question($question, $state, $number, $cmoptions, $options);
--}
--/**
-  * Saves question options
-  *
-  * Simply calls the question type specific save_question_options() method.
-@@ -1736,44 +842,6 @@ function save_question_options($question) {
-     $QTYPES[$question->qtype]->save_question_options($question);
- }
--/**
--* Gets all teacher stored answers for a given question
--*
--* Simply calls the question type specific get_all_responses() method.
--*/
--// ULPGC ecastro
--function get_question_responses($question, $state) {
--    global $QTYPES;
--    $r = $QTYPES[$question->qtype]->get_all_responses($question, $state);
--    return $r;
--}
--
--
--/**
--* Gets the response given by the user in a particular state
--*
--* Simply calls the question type specific get_actual_response() method.
--*/
--// ULPGC ecastro
--function get_question_actual_response($question, $state) {
--    global $QTYPES;
--
--    $r = $QTYPES[$question->qtype]->get_actual_response($question, $state);
--    return $r;
--}
--
--/**
--* TODO: document this
--*/
--// ULPGc ecastro
--function get_question_fraction_grade($question, $state) {
--    global $QTYPES;
--
--    $r = $QTYPES[$question->qtype]->get_fractional_grade($question, $state);
--    return $r;
--}
--
--
- /// CATEGORY FUNCTIONS /////////////////////////////////////////////////////////////////
- /**
-@@ -1796,7 +864,7 @@ function sort_categories_by_tree(&$categories, $id = 0, $level = 1) {
-     if ($level == 1) {
-         foreach ($keys as $key) {
-             //If not processed and it's a good candidate to start (because its parent doesn't exist in the course)
--            if (!isset($categories[$key]->processed) && !record_exists('question_categories', 'course', $categories[$key]->course, 'id', $categories[$key]->parent)) {
-+            if (!isset($categories[$key]->processed) && !record_exists('question_categories', 'contextid', $categories[$key]->contextid, 'id', $categories[$key]->parent)) {
-                 $children[$key] = $categories[$key];
-                 $categories[$key]->processed = true;
-                 $children = $children + sort_categories_by_tree($categories, $children[$key]->id, $level+1);
-@@ -2019,6 +1087,7 @@ function question_add_context_in_key($categories){
-     }
-     return $newcatarray;
- }
-+
- function question_add_tops($categories, $pcontexts){
-     $topcats = array();
-     foreach ($pcontexts as $context){
-@@ -2048,8 +1117,6 @@ function question_categorylist($categoryid) {
- }
--
--
- //===========================
- // Import/Export Functions
- //===========================
-@@ -2098,7 +1165,6 @@ function get_import_export_formats( $type ) {
-     return $fileformatnames;
- }
--
- /**
- * Create default export filename
- *
-@@ -2144,6 +1210,7 @@ function default_export_filename($course,$category) {
-     return $export_name;
- }
-+
- class context_to_string_translator{
-     /**
-      * @var array used to translate between contextids and strings for this context.
-@@ -2352,4 +1419,3 @@ function get_filesdir_from_context($context){
-     }
-     return $courseid;
- }
--?>
diff --git a/question/todo/top-level_question_changes.patch.txt b/question/todo/top-level_question_changes.patch.txt
deleted file mode 100644 (file)
index 2b0ac00..0000000
+++ /dev/null
@@ -1,1363 +0,0 @@
-diff --git a/question/backuplib.php b/question/backuplib.php
-index 6d37298..0e2cf4b 100644
---- a/question/backuplib.php
-+++ b/question/backuplib.php
-@@ -192,7 +192,7 @@
-                 fwrite ($bf,full_tag("QUESTIONTEXTFORMAT",$level + 2,false,$question->questiontextformat));
-                 fwrite ($bf,full_tag("IMAGE",$level + 2,false,$question->image));
-                 fwrite ($bf,full_tag("GENERALFEEDBACK",$level + 2,false,$question->generalfeedback));
--                fwrite ($bf,full_tag("DEFAULTGRADE",$level + 2,false,$question->defaultgrade));
-+                fwrite ($bf,full_tag("DEFAULTGRADE",$level + 2,false,$question->defaultmark));
-                 fwrite ($bf,full_tag("PENALTY",$level + 2,false,$question->penalty));
-                 fwrite ($bf,full_tag("QTYPE",$level + 2,false,$question->qtype));
-                 fwrite ($bf,full_tag("LENGTH",$level + 2,false,$question->length));
-@@ -205,6 +205,9 @@
-                 fwrite ($bf,full_tag("MODIFIEDBY",$level + 2,false,$question->modifiedby));
-                 // Backup question type specific data
-                 $status = $status && $QTYPES[$question->qtype]->backup($bf,$preferences,$question->id, $level + 2);
-+
-+                $status = $status && backup_question_hints($bf, $preferences, $question->id, $level + 2);
-+
-                 //End question
-                 $status = $status && fwrite ($bf,end_tag("QUESTION",$level + 1,true));
-                 //Do some output
-@@ -223,6 +226,27 @@
-         return $status;
-     }
-+    function question_backup_hints($bf, $preferences, $questionid, $level = 6) {
-+        $hints = get_records('question_hints', 'questionid', $questionid, 'id');
-+        if (!$hints) {
-+            return true;
-+        }
-+
-+        $status = true;
-+        $status = $status && fwrite($bf, start_tag("HINTS", $level, true));
-+        foreach ($hints as $hint) {
-+            $status = $status && fwrite($bf, start_tag("HINT", $level + 1, true));
-+            $status = $status && fwrite($bf, full_tag("HINT_TEXT", $level + 2, false, $hint->hint));
-+            $status = $status && fwrite($bf, full_tag("SHOWNUMCORRECT", $level + 2, false, $hint->shownumcorrect));
-+            $status = $status && fwrite($bf, full_tag("CLEARWRONG", $level + 2, false, $hint->clearwrong));
-+            $status = $status && fwrite($bf, full_tag("OPTIONS", $level + 2, false, $hint->options));
-+            $status = $status && fwrite($bf, end_tag("HINT", $level + 1, true));
-+        }
-+        $status = $status && fwrite($bf, end_tag("HINTS", $level, true));
-+
-+        return $status;
-+    }
-+
-     //This function backups the answers data in some question types
-     //(truefalse, shortanswer,multichoice,numerical,calculated)
-     function question_backup_answers($bf,$preferences,$question, $level = 6) {
-diff --git a/question/editlib.php b/question/editlib.php
-index c18daf9..e21a4ce 100644
---- a/question/editlib.php
-+++ b/question/editlib.php
-@@ -1,17 +1,33 @@
--<?php // $Id$
-+<?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/>.
-+
-+
- /**
-  * Functions used to show question editing interface
-  *
-- *
-- * @author Martin Dougiamas and many others. This has recently been extensively
-- *         rewritten by members of the Serving Mathematics project
-- *         {@link http://maths.york.ac.uk/serving_maths}
-- * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
-- * @package questionbank
-+ * @package mod_quiz
-+ * @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
-+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-  */
-+
- require_once($CFG->libdir.'/questionlib.php');
-+
- define('DEFAULT_QUESTIONS_PER_PAGE', 20);
- function get_module_from_cmid($cmid){
-@@ -492,13 +508,13 @@ function question_showbank_actions($pageurl, $cm){
-                     // for each question either hide it if it is in use or delete it
-                     foreach ($questionlist as $questionid) {
-                         question_require_capability_on($questionid, 'edit');
--                        if (record_exists('quiz_question_instances', 'question', $questionid)) {
-+                        if (questions_in_use(array($questionid))) {
-                             if (!set_field('question', 'hidden', 1, 'id', $questionid)) {
-                                 question_require_capability_on($questionid, 'edit');
-                                 error('Was not able to hide question');
-                             }
-                         } else {
--                            delete_question($questionid);
-+                            question_delete_question($questionid);
-                         }
-                     }
-                 }
-@@ -551,7 +567,7 @@ function question_showbank($tabname, $contexts, $pageurl, $cm, $page, $perpage,
-                 $key = $matches[1];
-                 $questionlist .= $key.',';
-                 question_require_capability_on($key, 'edit');
--                if (record_exists('quiz_question_instances', 'question', $key)) {
-+                if (questions_in_use(array($key))) {
-                     $questionnames .= '* ';
-                     $inuse = true;
-                 }
-diff --git a/question/exportfile.php b/question/exportfile.php
-index 1a40c5f..67503e5 100644
---- a/question/exportfile.php
-+++ b/question/exportfile.php
-@@ -1,17 +1,43 @@
--<?php  // $Id$
--    require_once(dirname(__FILE__) . '/../config.php');
--    require_once($CFG->libdir . '/filelib.php');
-+<?php
--    // Note: file.php always calls require_login() with $setwantsurltome=false
--    //       in order to avoid messing redirects. MDL-14495
--    require_login(0, true, null, false);
-+// 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/>.
--    $relativepath = get_file_argument('question/exportfile.php');
--    if (!$relativepath) {
--        error('No valid arguments supplied or incorrect server configuration');
--    }
--    $pathname = $CFG->dataroot . '/temp/questionexport/' . $USER->id . '/' .  $relativepath;
-+/**
-+ * This script sends question exports to users who do not have permisison to
-+ * view the course files.
-+ *
-+ * @package moodlecore
-+ * @subpackage questionbank
-+ * @copyright 2008 The Open University
-+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-+ */
--    send_temp_file($pathname, $relativepath);
--?>
-\ No newline at end of file
-+require_once(dirname(__FILE__) . '/../config.php');
-+require_once($CFG->libdir . '/filelib.php');
-+
-+// Note: file.php always calls require_login() with $setwantsurltome=false
-+//       in order to avoid messing redirects. MDL-14495
-+require_login(0, true, null, false);
-+
-+$relativepath = get_file_argument('question/exportfile.php');
-+if (!$relativepath) {
-+    error('No valid arguments supplied or incorrect server configuration');
-+}
-+
-+$pathname = $CFG->dataroot . '/temp/questionexport/' . $USER->id . '/' .  $relativepath;
-+
-+send_temp_file($pathname, $relativepath);
-diff --git a/question/file.php b/question/file.php
-index cd4789a..8774e7d 100644
---- a/question/file.php
-+++ b/question/file.php
-@@ -1,86 +1,109 @@
--<?php  // $Id$
--      // This script fetches files from the dataroot/questionattempt directory
--      // It is based on the top-level file.php
--      //
--      // On a module-by-module basis (currently only implemented for quiz), it checks
--      // whether the user has permission to view the file.
--      //
--      // Syntax:      question/file.php/attemptid/questionid/filename.ext
--      // Workaround:  question/file.php?file=/attemptid/questionid/filename.ext
--
--    require_once('../config.php');
--    require_once('../lib/filelib.php');
--
--    // disable moodle specific debug messages
--    disable_debugging();
--
--    $relativepath = get_file_argument('file.php');
--    // force download for any student-submitted files to prevent XSS attacks.
--    $forcedownload = 1;
--
--    // relative path must start with '/', because of backup/restore!!!
--    if (!$relativepath) {
--        error('No valid arguments supplied or incorrect server configuration');
--    } else if ($relativepath{0} != '/') {
--        error('No valid arguments supplied, path does not start with slash!');
--    }
-+<?php
--    $pathname = $CFG->dataroot.'/questionattempt'.$relativepath;
-+// 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/>.
--    // extract relative path components
--    $args = explode('/', trim($relativepath, '/'));
--    // check for the right number of directories in the path
--    if (count($args) != 3) {
--        error('Invalid arguments supplied');
--    }
-+/**
-+ * This script fetches files from the dataroot/questionattempt directory
-+ * It is based on the top-level file.php
-+ *
-+ * On a module-by-module basis (currently only implemented for quiz), it checks
-+ * whether the user has permission to view the file.
-+ *
-+ * Syntax:      question/file.php/attemptid/questionid/filename.ext
-+ * Workaround:  question/file.php?file=/attemptid/questionid/filename.ext
-+ *
-+ * @package moodlecore
-+ * @subpackage questionengine
-+ * @copyright 2007 Adriane Boyd
-+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-+ */
--    // security: require login
--    require_login();
-+require_once('../config.php');
-+require_once('../lib/filelib.php');
--    // security: do not return directory node!
--    if (is_dir($pathname)) {
--        question_attempt_not_found();
--    }
-+// disable moodle specific debug messages
-+disable_debugging();
--    $lifetime = 0;  // do not cache because students may reupload files
--
--    // security: check that the user has permission to access this file
--    $haspermission = false;
--    if ($attempt = get_record("question_attempts", "id", $args[0])) {
--        $modfile = $CFG->dirroot .'/mod/'. $attempt->modulename .'/lib.php';
--        $modcheckfileaccess = $attempt->modulename .'_check_file_access';
--        if (file_exists($modfile)) {
--            @require_once($modfile);
--            if (function_exists($modcheckfileaccess)) {
--                $haspermission = $modcheckfileaccess($args[0], $args[1]);
--            }
--        }
--    } else if ($args[0][0] == 0) {
--        global $USER;
--        $list = explode('_', $args[0]);
--        if ($list[1] == $USER->id) {
--            $haspermission = true;
--        }
--    }
-+$relativepath = get_file_argument('file.php');
-+// force download for any student-submitted files to prevent XSS attacks.
-+$forcedownload = 1;
-+
-+// relative path must start with '/', because of backup/restore!!!
-+if (!$relativepath) {
-+    error('No valid arguments supplied or incorrect server configuration');
-+} else if ($relativepath{0} != '/') {
-+    error('No valid arguments supplied, path does not start with slash!');
-+}
-+
-+$pathname = $CFG->dataroot.'/questionattempt'.$relativepath;
--    if ($haspermission) {
--        // check that file exists
--        if (!file_exists($pathname)) {
--            question_attempt_not_found();
-+// extract relative path components
-+$args = explode('/', trim($relativepath, '/'));
-+
-+// check for the right number of directories in the path
-+if (count($args) != 3) {
-+    error('Invalid arguments supplied');
-+}
-+
-+// security: require login
-+require_login();
-+
-+// security: do not return directory node!
-+if (is_dir($pathname)) {
-+    question_attempt_not_found();
-+}
-+
-+$lifetime = 0;  // do not cache because students may reupload files
-+
-+// security: check that the user has permission to access this file
-+$haspermission = false;
-+if ($attempt = get_record("question_attempts", "id", $args[0])) {
-+    $modfile = $CFG->dirroot .'/mod/'. $attempt->modulename .'/lib.php';
-+    $modcheckfileaccess = $attempt->modulename .'_check_file_access';
-+    if (file_exists($modfile)) {
-+        @require_once($modfile);
-+        if (function_exists($modcheckfileaccess)) {
-+            $haspermission = $modcheckfileaccess($args[0], $args[1]);
-         }
-+    }
-+} else if ($args[0][0] == 0) {
-+    global $USER;
-+    $list = explode('_', $args[0]);
-+    if ($list[1] == $USER->id) {
-+        $haspermission = true;
-+    }
-+}
--        // send the file
--        session_write_close(); // unlock session during fileserving
--        $filename = $args[count($args)-1];
--        send_file($pathname, $filename, $lifetime, $CFG->filteruploadedfiles, false, $forcedownload);
--    } else {
-+if ($haspermission) {
-+    // check that file exists
-+    if (!file_exists($pathname)) {
-         question_attempt_not_found();
-     }
--    function question_attempt_not_found() {
--        global $CFG;
--        header('HTTP/1.0 404 not found');
--        print_error('filenotfound', 'error', $CFG->wwwroot); //this is not displayed on IIS??
--    }
--?>
-+    // send the file
-+    session_write_close(); // unlock session during fileserving
-+    $filename = $args[count($args)-1];
-+    send_file($pathname, $filename, $lifetime, $CFG->filteruploadedfiles, false, $forcedownload);
-+} else {
-+    question_attempt_not_found();
-+}
-+
-+function question_attempt_not_found() {
-+    global $CFG;
-+    header('HTTP/1.0 404 not found');
-+    print_error('filenotfound', 'error', $CFG->wwwroot); //this is not displayed on IIS??
-+}
-diff --git a/question/format.php b/question/format.php
-index 61ba333..410b275 100644
---- a/question/format.php
-+++ b/question/format.php
-@@ -184,8 +184,8 @@ class qformat_default {
-      * @param qtypehint hint about a question type from format
-      * @return object question object suitable for save_options() or false if cannot handle
-      */
--    function try_importing_using_qtypes( $data, $question=null, $extra=null, $qtypehint='') {
--        global $QTYPES;
-+    function try_importing_using_qtypes($data, $question = null, $extra = null,
-+            $qtypehint = '') {
-         // work out what format we are using
-         $formatname = substr(get_class($this), strlen('qformat_'));
-@@ -193,7 +193,7 @@ class qformat_default {
-         //first try importing using a hint from format
-         if (!empty($qtypehint)) {
--            $qtype = $QTYPES[$qtypehint];
-+            $qtype = question_bank::get_qtype($qtypehint, false);
-             if (is_object($qtype) && method_exists($qtype, $methodname)) {
-                 $question = $qtype->$methodname($data, $question, $this, $extra);
-                 if ($question) {
-@@ -204,7 +204,7 @@ class qformat_default {
-         // loop through installed questiontypes checking for
-         // function to handle this question
--        foreach ($QTYPES as $qtype) {
-+        foreach (question_bank::get_all_qtypes() as $qtype) {
-             if (method_exists( $qtype, $methodname)) {
-                 if ($question = $qtype->$methodname( $data, $question, $this, $extra )) {
-                     return $question;
-@@ -242,6 +242,9 @@ class qformat_default {
-         }
-         if (! $questions = $this->readquestions($lines)) {   // Extract all the questions
-+            if (!is_array($questions)) {
-+                return false;
-+            }
-             notify( get_string('noquestionsinfile','quiz') );
-             return false;
-         }
-@@ -501,7 +504,7 @@ class qformat_default {
-         $question = new stdClass();
-         $question->shuffleanswers = $CFG->quiz_shuffleanswers;
--        $question->defaultgrade = 1;
-+        $question->defaultmark = 1;
-         $question->image = "";
-         $question->usecase = 0;
-         $question->multiplier = array();
-@@ -510,7 +513,7 @@ class qformat_default {
-         $question->partiallycorrectfeedback = '';
-         $question->incorrectfeedback = '';
-         $question->answernumbering = 'abc';
--        $question->penalty = 0.1;
-+        $question->penalty = 0.3333333;
-         $question->length = 1;
-         // this option in case the questiontypes class wants
-diff --git a/question/import_form.php b/question/import_form.php
-index 108846b..cb876af 100644
---- a/question/import_form.php
-+++ b/question/import_form.php
-@@ -90,5 +90,24 @@ class question_import_form extends moodleform {
-             return  NULL;
-         }
-     }
-+
-+    
-+    
-+    public function validation($data, $files){
-+        $errors = parent::validation($data, $files);
-+        // If a file is uploaded return
-+        if ($data['choosefile'] || $files['newfile']) {
-+            return $errors;
-+        }
-+        // If file is not uploaded from  file upload
-+        if (!$files['newfile']) {
-+            $errors['newfile'] = get_string('importfromupload', 'question');
-+        }
-+        // If filke is not imported from course files
-+        if (!$data['choosefile'] && !$files['newfile']) {
-+            $errors['choosefile'] = get_string('importfromcoursefiles', 'question');
-+        }
-+        return $errors;
-+    }
- }
- ?>
-diff --git a/question/move_form.php b/question/move_form.php
-index d2b6445..4965167 100644
---- a/question/move_form.php
-+++ b/question/move_form.php
-@@ -1,4 +1,29 @@
--<?php  // $Id$
-+<?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/>.
-+
-+
-+/**
-+ * Form for moving questions between categories.
-+ *
-+ * @package moodlecore
-+ * @subpackage questionbank
-+ * @copyright 2008 The Open University
-+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-+ */
- if (!defined('MOODLE_INTERNAL')) {
-     die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
-@@ -13,16 +38,11 @@ class question_move_form extends moodleform {
-         $currentcat   = $this->_customdata['currentcat'];
-         $contexts   = $this->_customdata['contexts'];
--//--------------------------------------------------------------------------------
-         $mform->addElement('questioncategory', 'category', get_string('category','quiz'), compact('contexts', 'currentcat'));
--
--//--------------------------------------------------------------------------------
-         $this->add_action_buttons(true, get_string('categorymoveto', 'quiz'));
--//--------------------------------------------------------------------------------
-         $mform->addElement('hidden', 'delete', $currentcat);
-         $mform->setType('delete', PARAM_INT);
-     }
- }
--?>
-diff --git a/question/preview.php b/question/preview.php
-index 4335165..f3c7baf 100644
---- a/question/preview.php
-+++ b/question/preview.php
-@@ -1,4 +1,21 @@
--<?php // $Id$
-+<?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/>.
-+
-+
- /**
-  * This page displays a preview of a question
-  *
-@@ -7,231 +24,178 @@
-  * information is stored in the session as an array of subsequent states rather
-  * than in the database.
-  *
-- * TODO: make this work with activities other than quiz
-- *
-- * @author Alex Smith as part of the Serving Mathematics project
-- *         {@link http://maths.york.ac.uk/serving_maths}
-- * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
-- * @package questionbank
-+ * @package core
-+ * @subpackage questionbank
-+ * @copyright Alex Smith {@link http://maths.york.ac.uk/serving_maths} and
-+ *      numerous contributors.
-+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-  */
--    require_once("../config.php");
--    require_once($CFG->libdir.'/questionlib.php');
--    require_once($CFG->dirroot.'/mod/quiz/locallib.php'); // We really want to get rid of this
--
--    $id = required_param('id', PARAM_INT);        // question id
--    // if no quiz id is specified then a dummy quiz with default options is used
--    $quizid = optional_param('quizid', 0, PARAM_INT);
--    // if no quiz id is specified then tell us the course
--    if (empty($quizid)) {
--        $courseid = required_param('courseid', PARAM_INT);
--    }
--
--    // Test if we are continuing an attempt at a question
--    $continue = optional_param('continue', 0, PARAM_BOOL);
--    // Check for any of the submit buttons
--    $fillcorrect = optional_param('fillcorrect', 0, PARAM_BOOL);
--    $markall = optional_param('markall', 0, PARAM_BOOL);
--    $finishattempt = optional_param('finishattempt', 0, PARAM_BOOL);
--    $back = optional_param('back', 0, PARAM_BOOL);
--    $startagain = optional_param('startagain', 0, PARAM_BOOL);
--    // We are always continuing an attempt if a submit button was pressed with the
--    // exception of the start again button
--    if ($fillcorrect || $markall || $finishattempt || $back) {
--        $continue = true;
--    } else if ($startagain) {
--        $continue = false;
--    }
--
--    $url = new moodle_url($CFG->wwwroot . '/question/preview.php');
--    $url->param('id', $id);
--    if ($quizid) {
--        $url->param('quizid', $quizid);
--    } else {
--        $url->param('courseid', $courseid);
--    }
--    $url->param('continue', 1);
--    if (!$continue) {
--        // Start a new attempt; delete the old session
--        unset($SESSION->quizpreview);
--        // Redirect to ourselves but with continue=1; prevents refreshing the page
--        // from restarting an attempt (needed so that random questions don't change)
--        redirect($url->out());
--    }
--    // Load the question information
--    if (!$questions = get_records('question', 'id', $id)) {
--        error('Could not load question');
--    }
--    if (empty($quizid)) {
--        $quiz = new cmoptions;
--        $quiz->id = 0;
--        $quiz->review = $CFG->quiz_review;
--        require_login($courseid, false);
--        $quiz->course = $courseid;
--    } else if (!$quiz = get_record('quiz', 'id', $quizid)) {
--        error("Quiz id $quizid does not exist");
--    } else {
--        require_login($quiz->course, false, get_coursemodule_from_instance('quiz', $quizid, $quiz->course));
--    }
--
--
--
--    if ($maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $id)) {
--        $questions[$id]->maxgrade = $maxgrade;
--    } else {
--        $questions[$id]->maxgrade = $questions[$id]->defaultgrade;
--    }
--
--    $quiz->id = 0; // just for safety
--    $quiz->questions = $id;
--
--    if (!$category = get_record("question_categories", "id", $questions[$id]->category)) {
--        error("This question doesn't belong to a valid category!");
--    }
--
--    if (!question_has_capability_on($questions[$id], 'use', $questions[$id]->category)){
--        error("You can't preview these questions!");
--    }
--    if (isset($COURSE)){
--        $quiz->course = $COURSE->id;
--    }
--
--    // Load the question type specific information
--    if (!get_question_options($questions)) {
--        print_error('newattemptfail', 'quiz');
--    }
--
--    // Create a dummy quiz attempt
--    // TODO: find out what of the following we really need. What is $attempt
--    //       really used for?
--    $timenow = time();
--    $attempt->quiz = $quiz->id;
--    $attempt->userid = $USER->id;
--    $attempt->attempt = 0;
--    $attempt->sumgrades = 0;
--    $attempt->timestart = $timenow;
--    $attempt->timefinish = 0;
--    $attempt->timemodified = $timenow;
--    $attempt->uniqueid = 0;
--    $attempt->id = 0;
--
--    // Restore the history of question sessions from the moodle session or create
--    // new sessions. Make $states a reference to the states array in the moodle
--    // session.
--    if (isset($SESSION->quizpreview->states) and $SESSION->quizpreview->questionid == $id) {
--        // Reload the question session history from the moodle session
--        $states =& $SESSION->quizpreview->states;
--        $historylength = count($states) - 1;
--        if ($back && $historylength > 0) {
--            // Go back one step in the history
--            unset($states[$historylength]);
--            $historylength--;
-+require_once(dirname(__FILE__) . '/../config.php');
-+require_once($CFG->libdir . '/questionlib.php');
-+require_once($CFG->libdir . '/formslib.php');
-+require_once(dirname(__FILE__) . '/previewlib.php');
-+require_js('yui_dom-event');
-+require_js($CFG->httpswwwroot . '/question/preview.js');
-+
-+// Get and validate question id.
-+$id = required_param('id', PARAM_INT); // Question id
-+$question = question_bank::load_question($id);
-+require_login();
-+if (!$category = get_record('question_categories', 'id', $question->category)) {
-+    print_error('unknownquestioncategory', 'question', $question->category);
-+}
-+question_require_capability_on($question, 'use');
-+
-+// Get and validate display options.
-+$options = new question_preview_options($question);
-+$options->load_user_defaults();
-+$options->set_from_request();
-+
-+// Get and validate exitsing preview, or start a new one.
-+$previewid = optional_param('previewid', 0, PARAM_ALPHANUM);
-+if ($previewid) {
-+    if (!isset($SESSION->question_previews[$previewid])) {
-+        print_error('notyourpreview', 'question');
-+    }
-+    try {
-+        $quba = question_engine::load_questions_usage_by_activity($previewid);
-+    } catch (Exception $e){
-+        print_error('submissionoutofsequencefriendlymessage', 'question',
-+                question_preview_url($question->id, $options->behaviour,
-+                $options->maxmark, $options));
-+    }
-+    $slot = $quba->get_first_question_number();
-+    $usedquestion = $quba->get_question($slot);
-+    if ($usedquestion->id != $question->id) {
-+        print_error('questionidmismatch', 'question');
-+    }
-+    $question = $usedquestion;
-+
-+} else {
-+    $quba = question_engine::make_questions_usage_by_activity('core_question_preview',
-+            get_context_instance_by_id($category->contextid));
-+    $quba->set_preferred_behaviour($options->behaviour);
-+    $slot = $quba->add_question($question, $options->maxmark);
-+    $quba->start_all_questions();
-+    begin_sql();
-+    question_engine::save_questions_usage_by_activity($quba);
-+    commit_sql();
-+
-+    $SESSION->question_previews[$quba->get_id()] = true;
-+}
-+$options->behaviour = $quba->get_preferred_behaviour();
-+$options->maxmark = $quba->get_question_max_mark($slot);
-+
-+// Create the settings form, and initialise the fields.
-+$optionsform = new preview_options_form($CFG->wwwroot . '/question/preview.php?id=' . $question->id, $quba);
-+$optionsform->set_data($options);
-+
-+// Process change of settings, if that was requested.
-+if ($newoptions = $optionsform->get_submitted_data()) {
-+    // Set user preferences
-+    $options->save_user_preview_options($newoptions);
-+    restart_preview($previewid, $question->id, $newoptions);
-+}
-+
-+// Prepare a URL that is used in various places.
-+$actionurl = question_preview_action_url($question->id, $quba->get_id(), $options);
-+
-+// Process any actions from the buttons at the bottom of the form.
-+if (data_submitted() && confirm_sesskey()) {
-+    if (optional_param('restart', false, PARAM_BOOL)) {
-+        restart_preview($previewid, $question->id, $options);
-+
-+    } else if (optional_param('fill', null, PARAM_BOOL)) {
-+        $correctresponse = $quba->get_correct_response($slot);
-+        $quba->process_action($slot, $correctresponse);
-+        begin_sql();
-+        question_engine::save_questions_usage_by_activity($quba);
-+        commit_sql();
-+        redirect($actionurl);
-+
-+    } else if (optional_param('finish', null, PARAM_BOOL)) {
-+        try {
-+            $quba->process_all_actions();
-+        } catch (question_out_of_sequence_exception $e){
-+            print_error('submissionoutofsequencefriendlymessage', 'question', $actionurl);
-         }
-+        $quba->finish_all_questions();
-+        begin_sql();
-+        question_engine::save_questions_usage_by_activity($quba);
-+        commit_sql();
-+        redirect($actionurl);
-+
-     } else {
--        // Record the question id in the moodle session
--        $SESSION->quizpreview->questionid = $id;
--        // Create an empty session for the question
--        if (!$newstates =
--         get_question_states($questions, $quiz, $attempt)) {
--            print_error('newattemptfail', 'quiz');
-+        try {
-+            $quba->process_all_actions();
-+        } catch (question_out_of_sequence_exception $e){
-+            print_error('submissionoutofsequencefriendlymessage', 'question', $actionurl);
-         }
--        $SESSION->quizpreview->states = array($newstates);
--        $states =& $SESSION->quizpreview->states;
--        $historylength = 0;
--    }
--
--    if (!$fillcorrect && !$back && ($form = data_submitted())) {
--        $form = (array)$form;
--        $submitted = true;
--
--        // Create a new item in the history of question states (don't simplify!)
--        $states[$historylength + 1] = array();
--        $states[$historylength + 1][$id] = clone($states[$historylength][$id]);
--        $historylength++;
--        $curstate =& $states[$historylength][$id];
--        $curstate->changed = false;
--
--        // Process the responses
--        unset($form['id']);
--        unset($form['quizid']);
--        unset($form['continue']);
--        unset($form['markall']);
--        unset($form['finishattempt']);
--        unset($form['back']);
--        unset($form['startagain']);
--
--        $event = $finishattempt ? QUESTION_EVENTCLOSE : QUESTION_EVENTSUBMIT;
--        if ($actions = question_extract_responses($questions, $form, $event)) {
--            $actions[$id]->timestamp = 0; // We do not care about timelimits here
--            if (!question_process_responses($questions[$id], $curstate, $actions[$id], $quiz, $attempt)) {
--                unset($SESSION->quizpreview);
--                print_error('errorprocessingresponses', 'question', $url->out());
--            }
--            if (!$curstate->changed) {
--                // Update the current state rather than creating a new one
--                $historylength--;
--                unset($states[$historylength]);
--                $states = array_values($states);
--                $curstate =& $states[$historylength][$id];
--            }
-+        begin_sql();
-+        question_engine::save_questions_usage_by_activity($quba);
-+        commit_sql();
-+        $scrollpos = optional_param('scrollpos', '', PARAM_RAW);
-+        if ($scrollpos !== '') {
-+            $actionurl .= '&scrollpos=' . ((int) $scrollpos);
-         }
--    } else {
--        $submitted = false;
--        $curstate =& $states[$historylength][$id];
--    }
--
--    // TODO: should not use quiz-specific function here
--    $options = quiz_get_renderoptions($quiz->review, $curstate);
--    $options->noeditlink = true;
--
--    // Fill in the correct responses (unless the question is in readonly mode)
--    if ($fillcorrect && !$options->readonly) {
--        $curstate->responses = $QTYPES[$questions[$id]->qtype]
--         ->get_correct_responses($questions[$id], $curstate);
--    }
--
--    $strpreview = get_string('preview', 'quiz').' '.format_string($questions[$id]->name);
--    $questionlist = array($id);
--    $headtags = get_html_head_contributions($questionlist, $questions, $states[$historylength]);
--    print_header($strpreview, '', '', '', $headtags);
--    print_heading($strpreview);
-+        redirect($actionurl);
-+    }
-+}
-+
-+if ($question->length) {
-+    $displaynumber = '1';
-+} else {
-+    $displaynumber = 'i';
-+}
-+$restartdisabled = '';
-+$finishdisabled = '';
-+$filldisabled = '';
-+if ($quba->get_question_state($slot)->is_finished()) {
-+    $finishdisabled = ' disabled="disabled"';
-+    $filldisabled = ' disabled="disabled"';
-+}
-+if (!$previewid) {
-+    $restartdisabled = ' disabled="disabled"';
-+}
-+// Output
-+$title = get_string('previewquestion', 'question', format_string($question->name));
-+$headtags = question_engine::initialise_js() . $quba->render_question_head_html($slot);
-+print_header($title, '', '', '', $headtags);
-+print_heading($title);
-+
-+// Start the question form.
-+echo '<form method="post" action="' . s($actionurl) .
-+        '" enctype="multipart/form-data" id="responseform">', "\n";
-+print_js_call('question_init_form', array('responseform'));
-+echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />', "\n";
-+echo '<input type="hidden" name="slots" value="' . $slot . '" />', "\n";
-+
-+// Output the question.
-+echo $quba->render_question($slot, $options, $displaynumber);
-+
-+echo '<p class="notifytiny">' . get_string('behaviourbeingused', 'question',
-+        question_engine::get_behaviour_name(
-+        $quba->get_question_attempt($slot)->get_behaviour_name())) . '</p>';
-+// Finish the question form.
-+echo '<div id="previewcontrols" class="controls">';
-+echo '<input type="submit" name="restart"' . $restartdisabled .
-+        ' value="' . get_string('restart', 'question') . '" />', "\n";
-+echo '<input type="submit" name="fill"' . $filldisabled .
-+        ' value="' . get_string('fillincorrect', 'question') . '" />', "\n";
-+echo '<input type="submit" name="finish"' . $finishdisabled .
-+        ' value="' . get_string('submitandfinish', 'question') . '" />', "\n";
-+echo '<input type="hidden" name="scrollpos" id="scrollpos" value="" />';
-+echo '</div>';
-+echo '</form>';
-+
-+// Display the settings form.
-+$optionsform->display();
-+
-+echo '<script type="text/javascript">question_preview_init("' .
-+        get_string('closepreview', 'question') . '", "previewcontrols");</script>', "\n";
-+
-+// Finish output.
-+use_html_editor();
-+print_footer('empty');
--    if (!empty($quizid)) {
--        echo '<p class="quemodname">'.get_string('modulename', 'quiz') . ': ';
--        p(format_string($quiz->name));
--        echo "</p>\n";
--    }
--    $number = 1;
--    echo '<form method="post" action="'.$url->out(true).'" enctype="multipart/form-data" id="responseform">', "\n";
--    print_question($questions[$id], $curstate, $number, $quiz, $options);
--
--    echo '<div class="controls">';
--    echo $url->hidden_params_out();
--
--    // Print the mark and finish attempt buttons
--    echo '<input name="markall" type="submit" value="' . get_string('markall',
--     'quiz') . "\" />\n";
--    echo '<input name="finishattempt" type="submit" value="' .
--     get_string('finishattempt', 'quiz') . "\" />\n";
--    echo '<br />';
--    echo '<br />';
--    // Print the fill correct button (unless the question is in readonly mode)
--    if (!$options->readonly) {
--        echo '<input name="fillcorrect" type="submit" value="' .
--         get_string('fillcorrect', 'quiz') . "\" />\n";
--    }
--    // Print the navigation buttons
--    if ($historylength > 0) {
--        echo '<input name="back" type="submit" value="' . get_string('previous',
--         'quiz') . "\" />\n";
--    }
--    // Print the start again button
--    echo '<input name="startagain" type="submit" value="' .
--     get_string('startagain', 'quiz') . "\" />\n";
--    // Print the close window button
--    echo '<input type="button" onclick="window.close()" value="' .
--     get_string('closepreview', 'quiz') . "\" />";
--    echo '</div>';
--    echo '</form>';
--    print_footer();
--?>
-diff --git a/question/previewlib.php b/question/previewlib.php
-new file mode 100644
-index 0000000..f507a38
---- /dev/null
-+++ b/question/previewlib.php
-@@ -0,0 +1,214 @@
-+<?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/>.
-+
-+
-+/**
-+ * Helper code for the question preview UI.
-+ *
-+ * @package core
-+ * @subpackage questionbank
-+ * @copyright 2009 The Open University
-+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-+ */
-+
-+/**
-+ * Settings form for the preview options.
-+ *
-+ * @copyright 2009 The Open University
-+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-+ */
-+class preview_options_form extends moodleform {
-+    public function definition() {
-+        $mform = $this->_form;
-+
-+        $hiddenofvisible = array(
-+            question_display_options::HIDDEN => get_string('notshown', 'question'),
-+            question_display_options::VISIBLE => get_string('shown', 'question'),
-+        );
-+
-+        $mform->addElement('header', 'optionsheader', get_string('changeoptions', 'question'));
-+
-+        $behaviours = question_engine::get_behaviour_options($this->_customdata->get_preferred_behaviour());
-+        $mform->addElement('select', 'behaviour', get_string('howquestionsbehave', 'question'), $behaviours);
-+        $mform->setHelpButton('behaviour', array('howquestionsbehave', get_string('howquestionsbehave', 'question'), 'question'));
-+
-+        $mform->addElement('text', 'maxmark', get_string('markedoutof', 'question'), array('size' => '5'));
-+        $mform->setType('maxmark', PARAM_NUMBER);
-+
-+        $mform->addElement('select', 'correctness', get_string('whethercorrect', 'question'), $hiddenofvisible);
-+
-+        $marksoptions = array(
-+            question_display_options::HIDDEN => get_string('notshown', 'question'),
-+            question_display_options::MAX_ONLY => get_string('showmaxmarkonly', 'question'),
-+            question_display_options::MARK_AND_MAX => get_string('showmarkandmax', 'question'),
-+        );
-+        $mform->addElement('select', 'marks', get_string('marks', 'question'), $marksoptions);
-+
-+        $mform->addElement('select', 'markdp', get_string('decimalplacesingrades', 'question'),
-+                question_engine::get_dp_options());
-+
-+        $mform->addElement('select', 'feedback', get_string('specificfeedback', 'question'), $hiddenofvisible);
-+
-+        $mform->addElement('select', 'generalfeedback', get_string('generalfeedback', 'question'), $hiddenofvisible);
-+
-+        $mform->addElement('select', 'rightanswer', get_string('rightanswer', 'question'), $hiddenofvisible);
-+
-+        $mform->addElement('select', 'history', get_string('responsehistory', 'question'), $hiddenofvisible);
-+
-+        $mform->addElement('submit', 'submit', get_string('restartwiththeseoptions', 'question'), $hiddenofvisible);
-+    }
-+}
-+
-+
-+/**
-+ * Displays question preview options as default and set the options
-+ * Setting default, getting and setting user preferences in question preview options.
-+ *
-+ * @copyright 2010 The Open University
-+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-+ */
-+class question_preview_options extends question_display_options {
-+    /** @var string the behaviour to use for this preview. */
-+    public $behaviour;
-+
-+    /** @var number the maximum mark to use for this preview. */
-+    public $maxmark;
-+
-+    /** @var string prefix to append to field names to get user_preference names. */
-+    const OPTIONPREFIX = 'question_preview_options_';
-+
-+    /**
-+     * Constructor.
-+     */
-+    public function __construct($question) {
-+        global $CFG;
-+        $this->behaviour = 'deferredfeedback';
-+        $this->maxmark = $question->defaultmark;
-+        $this->correctness = self::VISIBLE;
-+        $this->marks = self::MARK_AND_MAX;
-+        $this->markdp = $CFG->quiz_decimalpoints;
-+        $this->feedback = self::VISIBLE;
-+        $this->numpartscorrect = $this->feedback;
-+        $this->generalfeedback = self::VISIBLE;
-+        $this->rightanswer = self::VISIBLE;
-+        $this->history = self::HIDDEN;
-+        $this->flags = self::HIDDEN;
-+        $this->manualcomment = self::HIDDEN;
-+    }
-+
-+    /**
-+     * @return array names of the options we store in the user preferences table.
-+     */
-+    protected function get_user_pref_fields() {
-+        return array('behaviour', 'correctness', 'marks', 'markdp', 'feedback',
-+                'generalfeedback', 'rightanswer', 'history');
-+    }
-+
-+    /**
-+     * @return array names and param types of the options we read from the request.
-+     */
-+    protected function get_field_types() {
-+        return array(
-+            'behaviour' => PARAM_ALPHA,
-+            'maxmark' => PARAM_NUMBER,
-+            'correctness' => PARAM_BOOL,
-+            'marks' => PARAM_INT,
-+            'markdp' => PARAM_INT,
-+            'feedback' => PARAM_BOOL,
-+            'generalfeedback' => PARAM_BOOL,
-+            'rightanswer' => PARAM_BOOL,
-+            'history' => PARAM_BOOL,
-+        );
-+    }
-+
-+    /**
-+     * Load the value of the options from the user_preferences table.
-+     */
-+    public function load_user_defaults() {
-+        foreach ($this->get_user_pref_fields() as $field) {
-+            $this->$field = get_user_preferences(
-+                    self::OPTIONPREFIX . $field, $this->$field);
-+        }
-+        $this->numpartscorrect = $this->feedback;
-+    }
-+
-+    /**
-+     * Save a change to the user's preview options to the database.
-+     * @param object $newoptions
-+     */
-+    public function save_user_preview_options($newoptions) {
-+        foreach ($this->get_user_pref_fields() as $field) {
-+            if (isset($newoptions->$field)) {
-+                set_user_preference(self::OPTIONPREFIX . $field, $newoptions->$field);
-+            }
-+        }
-+    }
-+
-+    /**
-+     * Set the value of any fields included in the request.
-+     */
-+    public function set_from_request() {
-+        foreach ($this->get_field_types() as $field => $type) {
-+            $this->$field = optional_param($field, $this->$field, $type);
-+        }
-+        $this->numpartscorrect = $this->feedback;
-+    }
-+
-+    /**
-+     * @return string URL fragment. Parameters needed in the URL when continuing
-+     * this preview.
-+     */
-+    public function get_query_string() {
-+        $querystring = array();
-+        foreach ($this->get_field_types() as $field => $notused) {
-+            if ($field == 'behaviour' || $field == 'maxmark') {
-+                continue;
-+            }
-+            $querystring[] = $field . '=' . $this->$field;
-+        }
-+        return implode('&', $querystring);
-+    }
-+}
-+
-+
-+/**
-+ * The the URL to use for actions relating to this preview.
-+ * @param integer $questionid the question being previewed.
-+ * @param integer $qubaid the id of the question usage for this preview.
-+ * @param question_preview_options $options the options in use.
-+ */
-+function question_preview_action_url($questionid, $qubaid,
-+        question_preview_options $options) {
-+    global $CFG;
-+    $url = $CFG->wwwroot . '/question/preview.php?id=' . $questionid . '&previewid=' . $qubaid;
-+    return $url . '&' . $options->get_query_string();
-+}
-+
-+/**
-+ * Delete the current preview, if any, and redirect to start a new preview.
-+ * @param integer $previewid
-+ * @param integer $questionid
-+ * @param object $displayoptions
-+ */
-+function restart_preview($previewid, $questionid, $displayoptions) {
-+    if ($previewid) {
-+        begin_sql();
-+        question_engine::delete_questions_usage_by_activity($previewid);
-+        commit_sql();
-+    }
-+    redirect(question_preview_url($questionid, $displayoptions->behaviour, $displayoptions->maxmark, $displayoptions));
-+}
-diff --git a/question/question.php b/question/question.php
-index c67c6fb..a0a9e49 100644
---- a/question/question.php
-+++ b/question/question.php
-@@ -142,7 +142,6 @@ if ($mform->is_cancelled()){
-         close_window();
-     } else {
-         $nexturl = new moodle_url($returnurl);
--        $nexturl->param('lastchanged', $question->id);
-         redirect($nexturl->out());
-     }
- } elseif ($fromform = $mform->get_data()) {
-@@ -222,7 +221,7 @@ if ($mform->is_cancelled()){
-     }
- } else {
--    list($streditingquestion,) = $QTYPES[$question->qtype]->get_heading();
-+    $streditingquestion = $QTYPES[$question->qtype]->get_heading();
-     $headtags = get_editing_head_contributions($question);
-     if ($cm !== null) {
-         $strmodule = get_string('modulename', $cm->modname);
-diff --git a/question/restorelib.php b/question/restorelib.php
-index ca8d940..aa37793 100644
---- a/question/restorelib.php
-+++ b/question/restorelib.php
-@@ -325,7 +325,7 @@
-             $question->questiontextformat = backup_todb($que_info['#']['QUESTIONTEXTFORMAT']['0']['#']);
-             $question->image = backup_todb($que_info['#']['IMAGE']['0']['#']);
-             $question->generalfeedback = backup_todb_optional_field($que_info, 'GENERALFEEDBACK', '');
--            $question->defaultgrade = backup_todb($que_info['#']['DEFAULTGRADE']['0']['#']);
-+            $question->defaultmark = backup_todb($que_info['#']['DEFAULTGRADE']['0']['#']);
-             $question->penalty = backup_todb($que_info['#']['PENALTY']['0']['#']);
-             $question->qtype = backup_todb($que_info['#']['QTYPE']['0']['#']);
-             $question->length = backup_todb($que_info['#']['LENGTH']['0']['#']);
-@@ -335,6 +335,17 @@
-             $question->timecreated = backup_todb_optional_field($que_info, 'TIMECREATED', 0);
-             $question->timemodified = backup_todb_optional_field($que_info, 'TIMEMODIFIED', 0);
-+            // Update old, badly rounded penalties, to the new equivalents.
-+            if ($question->penalty >= 0.33 && $question->penalty <= 0.34) {
-+                $question->penalty = 0.3333333;
-+            }
-+            if ($question->penalty >= 0.66 && $question->penalty <= 0.67) {
-+                $question->penalty = 0.6666667;
-+            }
-+            if ($question->penalty >= 1) {
-+                $question->penalty = 1;
-+            }
-+
-             // Set the createdby field, if the user was in the backup, or if we are on the same site.
-             $createdby = backup_todb_optional_field($que_info, 'CREATEDBY', null);
-             if (!empty($createdby)) {
-@@ -466,7 +477,8 @@
-                 }
-                 //Now, restore every question_answers in this question
--                $status = question_restore_answers($oldid,$newid,$que_info,$restore);
-+                $status = $status && question_restore_answers($oldid,$newid,$que_info,$restore);
-+                $status = $status && question_restore_hints($oldid,$newid,$que_info,$restore);
-                 // Restore questiontype specific data
-                 if (array_key_exists($question->qtype, $QTYPES)) {
-                     $status = $QTYPES[$question->qtype]->restore($oldid,$newid,$que_info,$restore);
-@@ -510,6 +522,61 @@
-         }
-     }
-+    function question_restore_hints($old_question_id, $new_question_id, $info, $restore) {
-+        if (!isset($info['#']['HINTS']['0']['#']['HINT'])) {
-+            return true;
-+        }
-+        $hints = $info['#']['HINTS']['0']['#']['HINT'];
-+
-+        $status = true;
-+        $count = 0;
-+        foreach ($hints as $hint_info) {
-+            if (isset($hint_info['#']['REST'])) {
-+                // Backwards compatibility.
-+                $hintoptions = backup_todb($hint_info['#']['REST']['0']['#']);
-+                if ($hintoptions) {
-+                    $hintoptions = unserialize($hintoptions);
-+                } else {
-+                    $hintoptions = array(0, 0);
-+                }
-+                $numoptions = count($hintoptions);
-+