MDL-16094 File storage conversion Quiz and Questions
authorDongsheng Cai <unoter@gmail.com>
Tue, 10 Aug 2010 09:56:48 +0000 (09:56 +0000)
committerDongsheng Cai <unoter@gmail.com>
Tue, 10 Aug 2010 09:56:48 +0000 (09:56 +0000)
80 files changed:
lib/db/install.xml
lib/db/upgrade.php
lib/questionlib.php
mod/quiz/attemptlib.php
mod/quiz/db/install.xml
mod/quiz/db/upgrade.php
mod/quiz/index.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/mod_form.php
mod/quiz/review.php
mod/quiz/version.php
mod/quiz/view.php
pluginfile.php
question/contextmoveq.php
question/edit.php
question/editlib.php
question/format.php
question/preview.php
question/previewlib.php [new file with mode: 0644]
question/question.php
question/type/calculated/db/install.xml
question/type/calculated/db/upgrade.php
question/type/calculated/edit_calculated_form.php
question/type/calculated/lang/en/qtype_calculated.php
question/type/calculated/lib.php [new file with mode: 0644]
question/type/calculated/questiontype.php
question/type/calculated/version.php
question/type/calculatedmulti/edit_calculatedmulti_form.php
question/type/calculatedmulti/lib.php [new file with mode: 0644]
question/type/calculatedmulti/questiontype.php
question/type/calculatedsimple/edit_calculatedsimple_form.php
question/type/calculatedsimple/lib.php [new file with mode: 0644]
question/type/calculatedsimple/questiontype.php
question/type/description/edit_description_form.php
question/type/description/question.html
question/type/description/questiontype.php
question/type/edit_question_form.php
question/type/essay/display.html
question/type/essay/edit_essay_form.php
question/type/essay/lib.php [new file with mode: 0644]
question/type/essay/questiontype.php
question/type/match/db/install.xml
question/type/match/db/upgrade.php [new file with mode: 0644]
question/type/match/display.html
question/type/match/edit_match_form.php
question/type/match/lib.php [new file with mode: 0644]
question/type/match/questiontype.php
question/type/match/version.php
question/type/missingtype/display.html
question/type/missingtype/questiontype.php
question/type/multianswer/edit_multianswer_form.php
question/type/multianswer/questiontype.php
question/type/multichoice/db/install.xml
question/type/multichoice/db/upgrade.php
question/type/multichoice/display.html
question/type/multichoice/edit_multichoice_form.php
question/type/multichoice/lib.php [new file with mode: 0644]
question/type/multichoice/questiontype.php
question/type/multichoice/version.php
question/type/numerical/db/install.xml
question/type/numerical/db/upgrade.php
question/type/numerical/display.html
question/type/numerical/edit_numerical_form.php
question/type/numerical/lib.php [new file with mode: 0644]
question/type/numerical/questiontype.php
question/type/numerical/version.php
question/type/questiontype.php
question/type/random/edit_random_form.php
question/type/random/questiontype.php
question/type/randomsamatch/edit_randomsamatch_form.php
question/type/shortanswer/display.html
question/type/shortanswer/edit_shortanswer_form.php
question/type/shortanswer/lib.php [new file with mode: 0644]
question/type/shortanswer/questiontype.php
question/type/truefalse/display.html
question/type/truefalse/edit_truefalse_form.php
question/type/truefalse/lib.php [new file with mode: 0644]
question/type/truefalse/questiontype.php
version.php

index 04d4fb9..a3a9697 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20100803" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20100806" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="name"/>
         <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="contextid"/>
         <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="context that this category is shared in" PREVIOUS="name" NEXT="info"/>
-        <FIELD NAME="info" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="stamp"/>
-        <FIELD NAME="stamp" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="info" NEXT="parent"/>
+        <FIELD NAME="info" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="infoformat"/>
+        <FIELD NAME="infoformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="info" NEXT="stamp"/>
+        <FIELD NAME="stamp" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="infoformat" NEXT="parent"/>
         <FIELD NAME="parent" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="stamp" NEXT="sortorder"/>
         <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="999" SEQUENCE="false" PREVIOUS="parent"/>
       </FIELDS>
         <FIELD NAME="parent" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="category" NEXT="name"/>
         <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="parent" NEXT="questiontext"/>
         <FIELD NAME="questiontext" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="name" NEXT="questiontextformat"/>
-        <FIELD NAME="questiontextformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="questiontext" NEXT="image"/>
-        <FIELD NAME="image" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="questiontextformat" NEXT="generalfeedback"/>
-        <FIELD NAME="generalfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="to store the question feedback" PREVIOUS="image" NEXT="defaultgrade"/>
-        <FIELD NAME="defaultgrade" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" DECIMALS="7" PREVIOUS="generalfeedback" NEXT="penalty"/>
+        <FIELD NAME="questiontextformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="questiontext" NEXT="generalfeedback"/>
+        <FIELD NAME="generalfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="to store the question feedback" PREVIOUS="questiontextformat" NEXT="generalfeedbackformat"/>
+        <FIELD NAME="generalfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="generalfeedback" NEXT="defaultgrade"/>
+        <FIELD NAME="defaultgrade" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" DECIMALS="7" PREVIOUS="generalfeedbackformat" NEXT="penalty"/>
         <FIELD NAME="penalty" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="false" DEFAULT="0.1" SEQUENCE="false" DECIMALS="7" PREVIOUS="defaultgrade" NEXT="qtype"/>
         <FIELD NAME="qtype" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" PREVIOUS="penalty" NEXT="length"/>
         <FIELD NAME="length" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="qtype" NEXT="stamp"/>
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="question"/>
         <FIELD NAME="question" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="answer"/>
-        <FIELD NAME="answer" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="question" NEXT="fraction"/>
-        <FIELD NAME="fraction" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="7" PREVIOUS="answer" NEXT="feedback"/>
-        <FIELD NAME="feedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="fraction"/>
+        <FIELD NAME="answer" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="question" NEXT="answerformat"/>
+        <FIELD NAME="answerformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="answer" NEXT="fraction"/>
+        <FIELD NAME="fraction" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="7" PREVIOUS="answerformat" NEXT="feedback"/>
+        <FIELD NAME="feedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="fraction" NEXT="feedbackformat"/>
+        <FIELD NAME="feedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="feedback"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/>
         <FIELD NAME="newest" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="questionid" NEXT="newgraded"/>
         <FIELD NAME="newgraded" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="newest" NEXT="sumpenalty"/>
         <FIELD NAME="sumpenalty" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="7" PREVIOUS="newgraded" NEXT="manualcomment"/>
-        <FIELD NAME="manualcomment" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="sumpenalty" NEXT="flagged"/>
-        <FIELD NAME="flagged" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="The person attempting the question may mark certain questions within their question_attempt if the module that owns the attempt allow it. This field stores the status of that flag." PREVIOUS="manualcomment"/>
+        <FIELD NAME="manualcomment" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="sumpenalty" NEXT="manualcommentformat"/>
+        <FIELD NAME="manualcommentformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="manualcomment" NEXT="flagged"/>
+        <FIELD NAME="flagged" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="The person attempting the question may mark certain questions within their question_attempt if the module that owns the attempt allow it. This field stores the status of that flag." PREVIOUS="manualcommentformat"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="attemptid"/>
         <KEY NAME="handlerid" TYPE="foreign" FIELDS="handlerid" REFTABLE="events_handlers" REFFIELDS="id" PREVIOUS="queuedeventid"/>
       </KEYS>
     </TABLE>
-    <TABLE NAME="grade_outcomes" COMMENT="This table describes the outcomes used in the system. An outcome is a statement tied to a rubric scale from low to high, such as â\80\9cNot met, Borderline, Metâ\80\9d (stored as 0,1 or 2)" PREVIOUS="events_queue_handlers" NEXT="grade_outcomes_courses">
+    <TABLE NAME="grade_outcomes" COMMENT="This table describes the outcomes used in the system. An outcome is a statement tied to a rubric scale from low to high, such as Ã¢Â\80Â\9cNot met, Borderline, MetâÂ\80Â\9d (stored as 0,1 or 2)" PREVIOUS="events_queue_handlers" NEXT="grade_outcomes_courses">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" COMMENT="id of the table" NEXT="courseid"/>
         <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="Mostly these are defined site wide ie NULL" PREVIOUS="id" NEXT="shortname"/>
       </KEYS>
     </TABLE>
   </TABLES>
-</XMLDB>
\ No newline at end of file
+</XMLDB>
index 0881eea..a7b8d79 100644 (file)
@@ -4911,7 +4911,170 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
         upgrade_main_savepoint(true, 2010080305);
     }
 
+    if ($oldversion < 2010080900) {
 
+    /// Define field generalfeedbackformat to be added to question
+        $table = new xmldb_table('question');
+        $field = new xmldb_field('generalfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'generalfeedback');
+
+    /// Conditionally launch add field generalfeedbackformat
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+    /// Define field infoformat to be added to question_categories
+        $table = new xmldb_table('question_categories');
+        $field = new xmldb_field('infoformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'info');
+
+    /// Conditionally launch add field infoformat
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+    /// Define field answerformat to be added to question_answers
+        $table = new xmldb_table('question_answers');
+        $field = new xmldb_field('answerformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'answer');
+
+    /// Conditionally launch add field answerformat
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+    /// Define field feedbackformat to be added to question_answers
+        $field = new xmldb_field('feedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'feedback');
+
+    /// Conditionally launch add field feedbackformat
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+    /// Define field manualcommentformat to be added to question_sessions
+        $table = new xmldb_table('question_sessions');
+        $field = new xmldb_field('manualcommentformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'manualcomment');
+
+    /// Conditionally launch add field manualcommentformat
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+    /// Main savepoint reached
+        upgrade_main_savepoint(true, 2010080900);
+    }
+
+    /// updating question image
+    if ($oldversion < 2010080901) {
+        $fs = get_file_storage();
+        $rs = $DB->get_recordset('question');
+        $textlib = textlib_get_instance();
+        foreach ($rs as $question) {
+            if (empty($question->image)) {
+                continue;
+            }
+            if (!$category = $DB->get_record('question_categories', array('id'=>$question->category))) {
+                continue;
+            }
+            $categorycontext = get_context_instance_by_id($category->contextid);
+            // question files are stored in course level
+            // so we have to find course context
+            switch ($categorycontext->contextlevel){
+                case CONTEXT_COURSE :
+                    $context = $categorycontext;
+                    break;
+                case CONTEXT_MODULE :
+                    $courseid = $DB->get_field('course_modules', 'course', array('id'=>$categorycontext->instanceid));
+                    $context = get_context_instance(CONTEXT_COURSE, $courseid);
+                    break;
+                case CONTEXT_COURSECAT :
+                case CONTEXT_SYSTEM :
+                    $context = get_system_context();
+                    break;
+                default :
+                    continue;
+            }
+            if ($textlib->substr($textlib->strtolower($question->image), 0, 7) == 'http://') {
+                // it is a link, appending to existing question text
+                $question->questiontext .= ' <img src="' . $question->image . '" />';
+                // update question record
+                $DB->update_record('question', $question);
+            } else {
+                $filename = basename($question->image);
+                $filepath = dirname($question->image);
+                if (empty($filepath) or $filepath == '.' or $filepath == '/') {
+                    $filepath = '/';
+                } else {
+                    // append /
+                    $filepath = '/'.trim($filepath, './@#$ ').'/';
+                }
+
+                // course files already moved to file pool by previous upgrade block
+                // so we just create copy from course_legacy area
+                if ($image = $fs->get_file($context->id, 'course', 'legacy', 0, $filepath, $filename)) {
+                    // move files to file pool
+                    $file_record = array(
+                        'contextid'=>$category->contextid,
+                        'component'=>'question',
+                        'filearea'=>'questiontext',
+                        'itemid'=>$question->id
+                    );
+                    $fs->create_file_from_storedfile($file_record, $image);
+                    $question->questiontext .= ' <img src="@@PLUGINFILE@@' . $filepath . $filename . '" />';
+                    // update question record
+                    $DB->update_record('question', $question);
+                }
+            }
+        }
+        $rs->close();
+
+        // Define field image to be dropped from question
+        $table = new xmldb_table('question');
+        $field = new xmldb_field('image');
+
+        // Conditionally launch drop field image
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // fix fieldformat
+        $sql = 'SELECT a.*, q.qtype FROM {question_answers} a, {question} q WHERE a.question = q.id';
+        $rs = $DB->get_recordset_sql($sql);
+        foreach ($rs as $record) {
+            // generalfeedback should use questiontext format
+            if ($CFG->texteditors !== 'textarea') {
+                if (!empty($record->feedback)) {
+                    $record->feedback = text_to_html($record->feedback);
+                }
+                $record->feedbackformat = FORMAT_HTML;
+            } else {
+                $record->feedbackformat = FORMAT_MOODLE;
+                $record->answerformat = FORMAT_MOODLE;
+            }
+            unset($record->qtype);
+            $DB->update_record('question_answers', $record);
+        }
+        $rs->close();
+
+        $rs = $DB->get_recordset('question');
+        foreach ($rs as $record) {
+            if ($CFG->texteditors !== 'textarea') {
+                if (!empty($record->questiontext)) {
+                    $record->questiontext = text_to_html($record->questiontext);
+                }
+                $record->questiontextformat = FORMAT_HTML;
+                // conver generalfeedback text to html
+                if (!empty($record->generalfeedback)) {
+                    $record->generalfeedback = text_to_html($record->generalfeedback);
+                }
+            } else {
+                $record->questiontextformat = FORMAT_MOODLE;
+            }
+            // generalfeedbackformat should be the save as questiontext format
+            $record->generalfeedbackformat = $record->questiontextformat;
+            $DB->update_record('question', $record);
+        }
+        $rs->close();
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2010080901);
+    }
     return true;
 }
 
index fbecc81..c05efd2 100644 (file)
@@ -851,18 +851,31 @@ function question_delete_activity($cm, $feedback=true) {
  *
  * @global object
  * @param string $questionids a comma-separated list of question ids.
- * @param integer $newcategory the id of the category to move to.
+ * @param integer $newcategoryid the id of the category to move to.
  */
-function question_move_questions_to_category($questionids, $newcategory) {
-    global $DB;
-
+function question_move_questions_to_category($questionids, $newcategoryid) {
+    global $DB, $QTYPES;
     $result = true;
+    $ids = explode(',', $questionids);
+    foreach ($ids as $questionid) {
+        $questionid = (int)$questionid;
+        $params = array();
+        $params[] = $questionid;
+        $sql = 'SELECT q.*, c.id AS contextid, c.contextlevel, c.instanceid, c.path, c.depth
+                  FROM {question} q, {question_categories} qc, {context} c
+                 WHERE q.category=qc.id AND q.id=? AND qc.contextid=c.id';
+        $question = $DB->get_record_sql($sql, $params);
+        $category = $DB->get_record('question_categories', array('id'=>$newcategoryid));
+        // process files
+        $QTYPES[$question->qtype]->move_files($question, $category);
+    }
+
 
     // Move the questions themselves.
-    $result = $result && $DB->set_field_select('question', 'category', $newcategory, "id IN ($questionids)");
+    $result = $result && $DB->set_field_select('question', 'category', $newcategoryid, "id IN ($questionids)");
 
     // Move any subquestions belonging to them.
-    $result = $result && $DB->set_field_select('question', 'category', $newcategory, "parent IN ($questionids)");
+    $result = $result && $DB->set_field_select('question', 'category', $newcategoryid, "parent IN ($questionids)");
 
     // TODO Deal with datasets.
 
@@ -1080,6 +1093,7 @@ function question_load_states(&$questions, &$states, $cmoptions, $attempt, $last
                 $states[$qid]->last_graded = clone($states[$qid]);
             }
         } 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
@@ -2091,7 +2105,7 @@ function question_init_qengine_js() {
     $module = array(
         'name' => 'core_question_flags',
         'fullpath' => '/question/flags.js',
-        'requires' => array('base', 'dom', 'event-delegate', 'io-base'), 
+        'requires' => array('base', 'dom', 'event-delegate', 'io-base'),
     );
     $actionurl = $CFG->wwwroot . '/question/toggleflag.php';
     $flagattributes = array(
@@ -2162,9 +2176,9 @@ function question_get_editing_head_contributions($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
@@ -3191,4 +3205,124 @@ class question_edit_contexts {
             print_error('nopermissions', '', '', 'access question edit tab '.$tabname);
         }
     }
-}
\ No newline at end of file
+}
+
+/**
+ * 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;
+
+    $attemptid = (int)array_shift($args);
+    $questionid = (int)array_shift($args);
+
+    require_login($course, false);
+
+    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);
+}
index 3d4fae4..51ce60a 100644 (file)
@@ -283,6 +283,10 @@ class quiz {
         return $this->accessmanager;
     }
 
+    public function get_overall_feedback($grade) {
+        return quiz_feedback_for_grade($grade, $this->quiz, $this->context, $this->cm);
+    }
+
     /**
      * Wrapper round the has_capability funciton that automatically passes in the quiz context.
      */
@@ -571,7 +575,7 @@ class quiz_attempt extends quiz {
         return $this->attempt->timefinish != 0;
     }
 
-    /** @return boolean whether this attemp is a preview attempt. */
+    /** @return boolean whether this attempt is a preview attempt. */
     public function is_preview() {
         return $this->attempt->preview;
     }
@@ -629,10 +633,12 @@ class quiz_attempt extends quiz {
     /**
      * Wrapper that calls get_render_options with the appropriate arguments.
      *
+     * @param integer questionid the quetsion to get the render options for.
      * @return object the render options for this user on this attempt.
      */
-    public function get_render_options($state) {
-        return quiz_get_renderoptions($this->quiz, $this->attempt, $this->context, $state);
+    public function get_render_options($questionid) {
+        return quiz_get_renderoptions($this->quiz, $this->attempt, $this->context,
+                $this->get_question_state($questionid));
     }
 
     /**
@@ -669,7 +675,7 @@ class quiz_attempt extends quiz {
             case QUESTION_EVENTCLOSEANDGRADE:
             case QUESTION_EVENTCLOSE:
             case QUESTION_EVENTMANUALGRADE:
-                $options = $this->get_render_options($this->states[$questionid]);
+                $options = $this->get_render_options($questionid);
                 if ($options->scores && $this->questions[$questionid]->maxgrade > 0) {
                     return question_get_feedback_class($state->last_graded->raw_grade /
                             $this->questions[$questionid]->maxgrade);
@@ -703,7 +709,7 @@ class quiz_attempt extends quiz {
      * @return string the formatted grade, to the number of decimal places specified by the quiz.
      */
     public function get_question_score($questionid) {
-        $options = $this->get_render_options($this->states[$questionid]);
+        $options = $this->get_render_options($questionid);
         if ($options->scores) {
             return quiz_format_question_grade($this->quiz, $this->states[$questionid]->last_graded->grade);
         } else {
@@ -805,7 +811,7 @@ class quiz_attempt extends quiz {
         if ($reviewing) {
             $options = $this->get_review_options();
         } else {
-            $options = $this->get_render_options($this->states[$id]);
+            $options = $this->get_render_options($id);
         }
         if ($thispageurl) {
             $this->quiz->thispageurl = $thispageurl;
@@ -816,6 +822,20 @@ class quiz_attempt extends quiz {
                 $this->quiz, $options);
     }
 
+    public function check_file_access($questionid, $isreviewing, $contextid, $component,
+            $filearea, $args, $forcedownload) {
+        if ($isreviewing) {
+            $options = $this->get_review_options();
+        } else {
+            $options = $this->get_render_options($questionid);
+        }
+        // XXX: mulitichoice type needs quiz id to get maxgrade
+        $options->quizid = $this->attempt->quiz;
+        return question_check_file_access($this->questions[$questionid],
+                $this->get_question_state($questionid), $options, $contextid,
+                $component, $filearea, $args, $forcedownload);
+    }
+
     /**
      * Triggers the sending of the notification emails at the end of this attempt.
      */
index d85a3fe..2196e07 100755 (executable)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="mod/quiz/db" VERSION="20100220" COMMENT="XMLDB file for Moodle mod/quiz"
+<XMLDB PATH="mod/quiz/db" VERSION="20100722" COMMENT="XMLDB file for Moodle mod/quiz"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
 >
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="quizid"/>
         <FIELD NAME="quizid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references quiz.id." PREVIOUS="id" NEXT="feedbacktext"/>
-        <FIELD NAME="feedbacktext" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="The feedback to show for a attempt where mingrade &lt;= attempt grade &lt; maxgrade. See function quiz_feedback_for_grade in mod/quiz/locallib.php." PREVIOUS="quizid" NEXT="mingrade"/>
-        <FIELD NAME="mingrade" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The lower limit of this grade band. Inclusive." PREVIOUS="feedbacktext" NEXT="maxgrade"/>
+        <FIELD NAME="feedbacktext" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="The feedback to show for a attempt where mingrade &lt;= attempt grade &lt; maxgrade. See function quiz_feedback_for_grade in mod/quiz/locallib.php." PREVIOUS="quizid" NEXT="feedbacktextformat"/>
+        <FIELD NAME="feedbacktextformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="feedbacktext" NEXT="mingrade"/>
+        <FIELD NAME="mingrade" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The lower limit of this grade band. Inclusive." PREVIOUS="feedbacktextformat" NEXT="maxgrade"/>
         <FIELD NAME="maxgrade" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The upper limit of this grade band. Exclusive." PREVIOUS="mingrade"/>
       </FIELDS>
       <KEYS>
index 80667d3..b528afa 100644 (file)
@@ -341,6 +341,22 @@ function xmldb_quiz_upgrade($oldversion) {
         upgrade_mod_savepoint(true, 2010051800, 'quiz');
     }
 
+    if ($oldversion < 2010080600) {
+
+        // Define field feedbacktextformat to be added to quiz_feedback
+        $table = new xmldb_table('quiz_feedback');
+        $field = new xmldb_field('feedbacktextformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'feedbacktext');
+
+        // Conditionally launch add field feedbacktextformat
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // quiz savepoint reached
+        upgrade_mod_savepoint(true, 2010080600, 'quiz');
+    }
+
+
     return true;
 }
 
index 351b505..631f95b 100644 (file)
                     $grade = get_string('outofshort', 'quiz', $a);
                 }
                 if ($alloptions->overallfeedback) {
-                    $feedback = quiz_feedback_for_grade($scores[$quiz->id], $quiz->id);
+                    $feedback = quiz_feedback_for_grade($scores[$quiz->id], $quiz, $context, $cm);
                 }
             }
             $data[] = $grade;
index 2d373a3..3d0737f 100644 (file)
@@ -104,6 +104,7 @@ define("QUIZ_MAX_EVENT_LENGTH", 5*24*60*60);   // 5 days maximum
  */
 function quiz_add_instance($quiz) {
     global $DB;
+    $cmid = $quiz->coursemodule;
 
     // Process the options from the form.
     $quiz->created = time();
@@ -982,10 +983,10 @@ function quiz_process_options(&$quiz) {
     if (isset($quiz->feedbacktext)) {
         // Clean up the boundary text.
         for ($i = 0; $i < count($quiz->feedbacktext); $i += 1) {
-            if (empty($quiz->feedbacktext[$i])) {
-                $quiz->feedbacktext[$i] = '';
+            if (empty($quiz->feedbacktext[$i]['text'])) {
+                $quiz->feedbacktext[$i]['text'] = '';
             } else {
-                $quiz->feedbacktext[$i] = trim($quiz->feedbacktext[$i]);
+                $quiz->feedbacktext[$i]['text'] = trim($quiz->feedbacktext[$i]['text']);
             }
         }
 
@@ -1023,7 +1024,7 @@ function quiz_process_options(&$quiz) {
             }
         }
         for ($i = $numboundaries + 1; $i < count($quiz->feedbacktext); $i += 1) {
-            if (!empty($quiz->feedbacktext[$i]) && trim($quiz->feedbacktext[$i]) != '') {
+            if (!empty($quiz->feedbacktext[$i]['text']) && trim($quiz->feedbacktext[$i]['text']) != '') {
                 return get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1);
             }
         }
@@ -1145,17 +1146,25 @@ function quiz_process_options(&$quiz) {
  */
 function quiz_after_add_or_update($quiz) {
     global $DB;
+    $cmid = $quiz->coursemodule;
+
+    // we need to use context now, so we need to make sure all needed info is already in db
+    $DB->set_field('course_modules', 'instance', $quiz->id, array('id'=>$cmid));
+    $context = get_context_instance(CONTEXT_MODULE, $cmid);
 
     // Save the feedback
     $DB->delete_records('quiz_feedback', array('quizid' => $quiz->id));
 
-    for ($i = 0; $i <= $quiz->feedbackboundarycount; $i += 1) {
+    for ($i = 0; $i <= $quiz->feedbackboundarycount; $i++) {
         $feedback = new stdClass;
         $feedback->quizid = $quiz->id;
-        $feedback->feedbacktext = $quiz->feedbacktext[$i];
+        $feedback->feedbacktext = $quiz->feedbacktext[$i]['text'];
+        $feedback->feedbacktextformat = $quiz->feedbacktext[$i]['format'];
         $feedback->mingrade = $quiz->feedbackboundaries[$i];
         $feedback->maxgrade = $quiz->feedbackboundaries[$i - 1];
-        $DB->insert_record('quiz_feedback', $feedback, false);
+        $feedback->id = $DB->insert_record('quiz_feedback', $feedback);
+        $feedbacktext = file_save_draft_area_files((int)$quiz->feedbacktext[$i]['itemid'], $context->id, 'mod_quiz', 'feedback', $feedback->id, array('subdirs'=>false, 'maxfiles'=>-1, 'maxbytes'=>0), $quiz->feedbacktext[$i]['text']);
+        $DB->set_field('quiz_feedback', 'feedbacktext', $feedbacktext, array('id'=>$feedback->id));
     }
 
     // Update the events relating to this quiz.
@@ -1421,23 +1430,44 @@ function quiz_reset_userdata($data) {
  * @param int $questionid int question id
  * @return boolean to indicate access granted or denied
  */
-function quiz_check_file_access($attemptuniqueid, $questionid) {
-    global $USER, $DB;
+function quiz_check_file_access($attemptuniqueid, $questionid, $context = null) {
+    global $USER, $DB, $CFG;
+    require_once(dirname(__FILE__).'/attemptlib.php');
+    require_once(dirname(__FILE__).'/locallib.php');
 
     $attempt = $DB->get_record('quiz_attempts', array('uniqueid' => $attemptuniqueid));
-    $quiz = $DB->get_record('quiz', array('id' => $attempt->quiz));
-    $context = get_context_instance(CONTEXT_COURSE, $quiz->course);
+    $attemptobj = quiz_attempt::create($attempt->id);
+
+    // does question exist?
+    if (!$question = $DB->get_record('question', array('id' => $questionid))) {
+        return false;
+    }
+
+    if ($context === null) {
+        $quiz = $DB->get_record('quiz', array('id' => $attempt->quiz));
+        $cm = get_coursemodule_from_id('quiz', $quiz->id);
+        $context = get_context_instance(CONTEXT_MODULE, $cm->id);
+    }
+
+    // Load those questions and the associated states.
+    $attemptobj->load_questions(array($questionid));
+    $attemptobj->load_question_states(array($questionid));
+
+    // obtain state
+    $state = $attemptobj->get_question_state($questionid);
+    // obtain questoin
+    $question = $attemptobj->get_question($questionid);
 
     // access granted if the current user submitted this file
-    if ($attempt->userid == $USER->id) {
-        return true;
+    if ($attempt->userid != $USER->id) {
+        return false;
     // access granted if the current user has permission to grade quizzes in this course
-    } else if (has_capability('mod/quiz:viewreports', $context) || has_capability('mod/quiz:grade', $context)) {
-        return true;
+    }
+    if (!(has_capability('mod/quiz:viewreports', $context) || has_capability('mod/quiz:grade', $context))) {
+        return false;
     }
 
-    // otherwise, this user does not have permission
-    return false;
+    return array($question, $state, array());
 }
 
 /**
@@ -1682,3 +1712,98 @@ function quiz_extend_settings_navigation($settings, $quiznode) {
 
     question_extend_settings_navigation($quiznode, $PAGE->cm->context)->trim_if_empty();
 }
+
+/**
+ * Serves the quiz files.
+ *
+ * @param object $course
+ * @param object $cm
+ * @param object $context
+ * @param string $filearea
+ * @param array $args
+ * @param bool $forcedownload
+ * @return bool false if file not found, does not return if found - justsend the file
+ */
+function quiz_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
+    global $CFG, $DB;
+
+    if ($context->contextlevel != CONTEXT_MODULE) {
+        return false;
+    }
+
+    require_login($course, false, $cm);
+
+    if (!$quiz = $DB->get_record('quiz', array('id'=>$cm->instance))) {
+        return false;
+    }
+
+    // 'intro' area is served by pluginfile.php
+    $fileareas = array('feedback');
+    if (!in_array($filearea, $fileareas)) {
+        return false;
+    }
+
+    $feedbackid = (int)array_shift($args);
+    if (!$feedback = $DB->get_record('quiz_feedback', array('id'=>$feedbackid))) {
+        return false;
+    }
+
+    $fs = get_file_storage();
+    $relativepath = implode('/', $args);
+    $fullpath = "/$context->id/mod_quiz/$filearea/$feedbackid/$relativepath";
+    if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
+        return false;
+    }
+    send_stored_file($file, 0, 0, true);
+}
+
+/**
+ * Called via pluginfile.php -> question_pluginfile to serve files belonging to
+ * a question in a question_attempt when that attempt is a quiz attempt.
+ *
+ * @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.
+ * @return bool false if file not found, does not return if found - justsend the file
+ */
+function quiz_question_pluginfile($course, $context, $component,
+        $filearea, $attemptid, $questionid, $args, $forcedownload) {
+    global $USER, $CFG;
+    require_once($CFG->dirroot . '/mod/quiz/locallib.php');
+
+    $attemptobj = quiz_attempt::create($attemptid);
+    require_login($attemptobj->get_courseid(), false, $attemptobj->get_cm());
+    $questionids = array($questionid);
+    $attemptobj->load_questions($questionids);
+    $attemptobj->load_question_states($questionids);
+
+    if ($attemptobj->is_own_attempt() && !$attemptobj->is_finished()) {
+        // In the middle of an attempt.
+        if (!$attemptobj->is_preview_user()) {
+            $attemptobj->require_capability('mod/quiz:attempt');
+        }
+        $isreviewing = false;
+
+    } else {
+        // Reviewing an attempt.
+        $attemptobj->check_review_capability();
+        $isreviewing = true;
+    }
+
+    if (!$attemptobj->check_file_access($questionid, $isreviewing, $context->id,
+            $component, $filearea, $args, $forcedownload)) {
+        send_file_not_found();
+    }
+
+    $fs = get_file_storage();
+    $relativepath = implode('/', $args);
+    $fullpath = "/$context->id/$component/$filearea/$relativepath";
+    if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
+        send_file_not_found();
+    }
+
+    send_stored_file($file, 0, 0, $forcedownload);
+}
index a4b247a..04051a1 100644 (file)
@@ -456,21 +456,22 @@ function quiz_rescale_grade($rawgrade, $quiz, $round = true) {
  * @param integer $quizid the id of the quiz object.
  * @return string the comment that corresponds to this grade (empty string if there is not one.
  */
-function quiz_feedback_for_grade($grade, $quizid) {
+function quiz_feedback_for_grade($grade, $quiz, $context, $cm=null) {
     global $DB;
-    $feedback = $DB->get_field_select('quiz_feedback', 'feedbacktext',
-            "quizid = ? AND mingrade <= ? AND $grade < maxgrade", array($quizid, $grade));
 
-    if (empty($feedback)) {
-        $feedback = '';
+    $feedback = $DB->get_record_select('quiz_feedback', "quizid = ? AND mingrade <= ? AND $grade < maxgrade", array($quiz->id, $grade));
+
+    if (empty($feedback->feedbacktext)) {
+        $feedback->feedbacktext = '';
     }
 
     // Clean the text, ready for display.
     $formatoptions = new stdClass;
     $formatoptions->noclean = true;
-    $feedback = format_text($feedback, FORMAT_MOODLE, $formatoptions);
+    $feedbacktext = file_rewrite_pluginfile_urls($feedback->feedbacktext, 'pluginfile.php', $context->id, 'mod_quiz', 'feedback', $feedback->id);
+    $feedbacktext = format_text($feedbacktext, $feedback->feedbacktextformat, $formatoptions);
 
-    return $feedback;
+    return $feedbacktext;
 }
 
 /**
@@ -1263,4 +1264,4 @@ function quiz_get_js_module() {
             array('flagged', 'question'),
         ),
     );
-}
\ No newline at end of file
+}
index e51b1e2..e7f5dba 100644 (file)
@@ -296,7 +296,7 @@ class mod_quiz_mod_form extends moodleform_mod {
         $mform->addElement('static', 'gradeboundarystatic1', get_string('gradeboundary', 'quiz'), '100%');
 
         $repeatarray = array();
-        $repeatarray[] = &MoodleQuickForm::createElement('text', 'feedbacktext', get_string('feedback', 'quiz'), array('size' => 50));
+        $repeatarray[] = &MoodleQuickForm::createElement('editor', 'feedbacktext', get_string('feedback', 'quiz'), null, array('maxfiles'=>EDITOR_UNLIMITED_FILES, 'noclean'=>true, 'context'=>$this->context));
         $mform->setType('feedbacktext', PARAM_RAW);
         $repeatarray[] = &MoodleQuickForm::createElement('text', 'feedbackboundaries', get_string('gradeboundary', 'quiz'), array('size' => 10));
         $mform->setType('feedbackboundaries', PARAM_NOTAGS);
@@ -313,7 +313,7 @@ class mod_quiz_mod_form extends moodleform_mod {
                 get_string('addmoreoverallfeedbacks', 'quiz'), true);
 
         // Put some extra elements in before the button
-        $insertEl = &MoodleQuickForm::createElement('text', "feedbacktext[$nextel]", get_string('feedback', 'quiz'), array('size' => 50));
+        $insertEl = &MoodleQuickForm::createElement('editor', "feedbacktext[$nextel]", get_string('feedback', 'quiz'), null, array('maxfiles'=>EDITOR_UNLIMITED_FILES, 'noclean'=>true, 'context'=>$this->context));
         $mform->insertElementBefore($insertEl, 'boundary_add_fields');
 
         $insertEl = &MoodleQuickForm::createElement('static', 'gradeboundarystatic2', get_string('gradeboundary', 'quiz'), '0%');
@@ -342,7 +342,19 @@ class mod_quiz_mod_form extends moodleform_mod {
         if (count($this->_feedbacks)) {
             $key = 0;
             foreach ($this->_feedbacks as $feedback){
-                $default_values['feedbacktext['.$key.']'] = $feedback->feedbacktext;
+                $draftid = file_get_submitted_draft_itemid('feedbacktext['.$key.']');
+                $default_values['feedbacktext['.$key.']']['text'] = file_prepare_draft_area(
+                    $draftid,       // draftid
+                    $this->context->id,    // context
+                    'mod_quiz',   // component
+                    'feedback',             // filarea
+                    !empty($feedback->id)?(int)$feedback->id:null, // itemid
+                    null,
+                    $feedback->feedbacktext      // text
+                );
+                $default_values['feedbacktext['.$key.']']['format'] = $feedback->feedbacktextformat;
+                $default_values['feedbacktext['.$key.']']['itemid'] = $draftid;
+
                 if ($feedback->mingrade > 0) {
                     $default_values['feedbackboundaries['.$key.']'] = (100.0 * $feedback->mingrade / $default_values['grade']) . '%';
                 }
@@ -433,7 +445,7 @@ class mod_quiz_mod_form extends moodleform_mod {
             }
         }
         for ($i = $numboundaries + 1; $i < count($data['feedbacktext']); $i += 1) {
-            if (!empty($data['feedbacktext'][$i] ) && trim($data['feedbacktext'][$i] ) != '') {
+            if (!empty($data['feedbacktext'][$i]['text']) && trim($data['feedbacktext'][$i]['text'] ) != '') {
                 $errors["feedbacktext[$i]"] = get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1);
             }
         }
index 446bba9..2ae4fac 100644 (file)
     }
 
 /// Feedback if there is any, and the user is allowed to see it now.
-    $feedback = quiz_feedback_for_grade($grade, $attempt->quiz);
+    $feedback = $attemptobj->get_overall_feedback($grade);
     if ($options->overallfeedback && $feedback) {
         $rows[] = '<tr><th scope="row" class="cell">' . get_string('feedback', 'quiz') .
                 '</th><td class="cell">' . $feedback . '</td></tr>';
index 42c59ba..9cc23d7 100644 (file)
@@ -5,7 +5,7 @@
 //  This fragment is called by moodle_needs_upgrading() and /admin/index.php
 ////////////////////////////////////////////////////////////////////////////////
 
-$module->version  = 2010051801;   // The (date) version of this module
+$module->version  = 2010080600;   // 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 e557916..f402cbe 100644 (file)
 
             if ($feedbackcolumn && $attempt->timefinish > 0) {
                 if ($attemptoptions->overallfeedback) {
-                    $row[] = quiz_feedback_for_grade($attemptgrade, $quiz->id);
+                    $row[] = quiz_feedback_for_grade($attemptgrade, $quiz, $context, $cm);
                 } else {
                     $row[] = '';
                 }
         }
         if ($feedbackcolumn) {
             $resultinfo .= $OUTPUT->heading(get_string('overallfeedback', 'quiz'), 3, 'main');
-            $resultinfo .= '<p class="quizgradefeedback">'.quiz_feedback_for_grade($mygrade, $quiz->id)."</p>\n";
+            $resultinfo .= '<p class="quizgradefeedback">'.quiz_feedback_for_grade($mygrade, $quiz, $context, $cm)."</p>\n";
         }
 
         if ($resultinfo) {
index bcc5291..de97303 100644 (file)
@@ -630,6 +630,12 @@ if ($component === 'blog') {
         send_file_not_found();
     }
 
+// ========================================================================================================================
+} else if ($component === 'question') {
+    require_once($CFG->libdir . '/questionlib.php');
+    question_pluginfile($course, $context, 'question', $filearea, $args, $forcedownload);
+    send_file_not_found();
+
 // ========================================================================================================================
 } else if (strpos($component, 'mod_') === 0) {
     $modname = substr($component, 4);
index a3a3acd..9f0be64 100644 (file)
@@ -12,7 +12,7 @@ require_once(dirname(__FILE__) . '/../config.php');
 require_once(dirname(__FILE__) . '/editlib.php');
 require_once($CFG->dirroot.'/question/contextmoveq_form.php');
 
-$ids = required_param('ids',PARAM_SEQUENCE); // question ids
+$ids = required_param('ids', PARAM_SEQUENCE); // question ids
 
 if (!$cmid = optional_param('cmid', 0, PARAM_INT)){
     $courseid = required_param('courseid', PARAM_INT);
@@ -165,6 +165,9 @@ if ($contextmoveform->is_cancelled()){
     if (!question_move_questions_to_category($ids, $tocat->id)) {
         print_error('errormovingquestions', 'question', $returnurl, $ids);
     }
+    if ($returnurl) {
+        $returnurl = new moodle_url('/' . $returnurl);
+    }
     redirect($returnurl);
 }
 
index 9c59487..f19bb1c 100644 (file)
 ///////////////////////////////////////////////////////////////////////////
 
 /**
-* Page to edit the question bank
-*
-* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
-* @package questionbank
-*//** */
+ * Page to edit the question bank
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package questionbank
+ */
 
     require_once("../config.php");
     require_once("editlib.php");
@@ -67,7 +67,7 @@
 
     $context = $contexts->lowest();
     $streditingquestions = get_string('editquestions', "quiz");
-    $PAGE->set_title($streditingquestions);    
+    $PAGE->set_title($streditingquestions);
     $PAGE->set_heading($COURSE->fullname);
     echo $OUTPUT->header();
 
index 1daca7a..33bab06 100644 (file)
@@ -1402,7 +1402,7 @@ class question_bank_view {
                     $questionids[] = $key;
                 }
             }
-            if ($questionids){
+            if ($questionids) {
                 list($usql, $params) = $DB->get_in_or_equal($questionids);
                 $sql = "SELECT q.*, c.contextid FROM {question} q, {question_categories} c WHERE q.id $usql AND c.id = q.category";
                 if (!$questions = $DB->get_records_sql($sql, $params)){
@@ -1426,8 +1426,9 @@ class question_bank_view {
                 } else {
                     $returnurl = str_replace($CFG->wwwroot . '/', '', $returnurl);
                     $movecontexturl  = new moodle_url('/question/contextmoveq.php',
-                            array('returnurl' => $returnurl, 'ids' => $questionids,
-                            'tocatid' => $tocategoryid));
+                                                    array('returnurl' => $returnurl,
+                                                            'ids' => implode(',', $questionids),
+                                                            'tocatid' => $tocategoryid));
                     if (!empty($cm->id)){
                         $movecontexturl->param('cmid', $cm->id);
                     } else {
index fdbcc1f..cc0203f 100644 (file)
@@ -862,6 +862,7 @@ class qformat_default {
      * performs the conversion.
      */
     function format_question_text($question) {
+        global $DB;
         $formatoptions = new stdClass;
         $formatoptions->noclean = true;
         $formatoptions->para = false;
@@ -870,10 +871,7 @@ class qformat_default {
         } else {
             $format = $question->questiontextformat;
         }
-        return format_text($question->questiontext, $format, $formatoptions);
+        $text = $question->questiontext;
+        return format_text(html_to_text($text), $format, $formatoptions);
     }
-
-
 }
-
-
index 7e60641..daad31b 100644 (file)
     $PAGE->set_title($strpreview);
     $PAGE->set_heading($COURSE->fullname);
     echo $OUTPUT->header();
-    
+
     if (!empty($quizid)) {
         echo '<p class="quemodname">'.get_string('modulename', 'quiz') . ': ';
         p(format_string($quiz->name));
     }
     $number = 1;
     echo '<form method="post" action="'.$url->out_omit_querystring().'" enctype="multipart/form-data" id="responseform">', "\n";
-    print_question($questions[$id], $curstate, $number, $quiz, $options);
+    print_question($questions[$id], $curstate, $number, $quiz, $options, $context);
 
     echo '<div class="controls">';
     echo html_writer::input_hidden_params($url);
diff --git a/question/previewlib.php b/question/previewlib.php
new file mode 100644 (file)
index 0000000..c3dfbfb
--- /dev/null
@@ -0,0 +1,108 @@
+<?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/>.
+
+/**
+ * Library functions used by question/preview.php.
+ *
+ * @package    core
+ * @subpackage questionengine
+ * @copyright  2010 The Open University
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Called via pluginfile.php -> question_pluginfile to serve files belonging to
+ * a question in a question_attempt when that attempt is a preview.
+ *
+ * @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.
+ * @return bool false if file not found, does not return if found - justsend the file
+ */
+function question_preview_question_pluginfile($course, $context, $component,
+        $filearea, $attemptid, $questionid, $args, $forcedownload) {
+    global $USER, $SESSION, $DB, $CFG;
+    require_once($CFG->dirroot . '/mod/quiz/locallib.php');
+
+    if (!$question = $DB->get_record('question', array('id' => $questionid))) {
+        return send_file_not_found();
+    }
+
+    if (!question_has_capability_on($question, 'use', $question->category)) {
+        send_file_not_found();
+    }
+
+    if (!isset($SESSION->quizpreview->states) || $SESSION->quizpreview->questionid != $questionid) {
+        send_file_not_found();
+    }
+
+    $states = end($SESSION->quizpreview->states);
+    if (!array_key_exists($question->id, $states)) {
+        send_file_not_found();
+    }
+    $state = $states[$question->id];
+
+    // Build fake cmoptions
+    $quiz = new cmoptions;
+    $quiz->id = 0;
+    $quiz->review = get_config('quiz', 'review');
+    if (empty($course->id)) {
+        $quiz->course = SITEID;
+    } else {
+        $quiz->course = $course->id;
+    }
+    $quiz->decimalpoints = get_config('quiz', 'decimalpoints');
+
+    $questions[$question->id] = $question;
+    get_question_options($questions);
+
+    // Build fake attempt
+    $timenow = time();
+    $attempt = new stdclass;
+    $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;
+    $attempt->layout = $question->id;
+
+    $options = quiz_get_renderoptions($quiz, $attempt, $context, $state);
+    $options->noeditlink = true;
+    // XXX: mulitichoice type needs quiz id to get maxgrade
+    $options->quizid = 0;
+
+    if (!question_check_file_access($question, $state, $options, $context->id, $component,
+            $filearea, $args, $forcedownload)) {
+        send_file_not_found();
+    }
+
+    $fs = get_file_storage();
+    $relativepath = implode('/', $args);
+    $fullpath = "/$context->id/$component/$filearea/$relativepath";
+    if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
+        send_file_not_found();
+    }
+
+    send_stored_file($file, 0, 0, $forcedownload);
+}
index 52ec0bb..e63c603 100644 (file)
@@ -75,6 +75,7 @@ if ($cmid){
     $thiscontext = get_context_instance(CONTEXT_MODULE, $cmid);
 } elseif ($courseid) {
     require_login($courseid, false);
+    $PAGE->set_pagelayout('course');
     $thiscontext = get_context_instance(CONTEXT_COURSE, $courseid);
     $module = null;
     $cm = null;
@@ -106,7 +107,7 @@ if ($id) {
 
 } else if ($categoryid) {
     // Category, but no qtype. They probably came from the addquestion.php
-    // script without choosing a question type. Send them back. 
+    // script without choosing a question type. Send them back.
     $addurl = new moodle_url('/question/addquestion.php', $url->params());
     $addurl->param('validationerror', 1);
     redirect($addurl);
@@ -151,6 +152,8 @@ if ($id) {
 } else  { // creating a new question
     require_capability('moodle/question:add', $categorycontext);
     $formeditable = true;
+    $question->formoptions->canedit = question_has_capability_on($question, 'edit');
+    $question->formoptions->canmove = (question_has_capability_on($question, 'move') && $addpermission);
     $question->formoptions->repeatelements = true;
     $question->formoptions->movecontext = false;
 }
@@ -189,7 +192,9 @@ if ($cm !== null){
 } else {
     $toform->courseid = $COURSE->id;
 }
+
 $toform->inpopup = $inpopup;
+
 $mform->set_data($toform);
 
 if ($mform->is_cancelled()){
index 9304024..7a24263 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="question/type/calculated/db" VERSION="20100208" COMMENT="XMLDB file for Moodle question/type/calculated. This question type also relies on the question_numerical_units table created by the numerical question type, and the tables created by the datasetdependent question type base class."
+<XMLDB PATH="question/type/calculated/db" VERSION="20100720" COMMENT="XMLDB file for Moodle question/type/calculated. This question type also relies on the question_numerical_units table created by the numerical question type, and the tables created by the datasetdependent question type base class."
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
 >
         <FIELD NAME="synchronize" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="question" NEXT="single"/>
         <FIELD NAME="single" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="If 0 it multiple response (checkboxes). Otherwise it is radio buttons." PREVIOUS="synchronize" NEXT="shuffleanswers"/>
         <FIELD NAME="shuffleanswers" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="Whether the choices can be randomly shuffled." PREVIOUS="single" NEXT="correctfeedback"/>
-        <FIELD NAME="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any correct response." PREVIOUS="shuffleanswers" NEXT="partiallycorrectfeedback"/>
-        <FIELD NAME="partiallycorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any partially correct response." PREVIOUS="correctfeedback" NEXT="incorrectfeedback"/>
-        <FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="partiallycorrectfeedback" NEXT="answernumbering"/>
-        <FIELD NAME="answernumbering" TYPE="char" LENGTH="10" NOTNULL="true" DEFAULT="abc" SEQUENCE="false" COMMENT="Indicates how and whether the choices should be numbered." PREVIOUS="incorrectfeedback"/>
+        <FIELD NAME="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any correct response." PREVIOUS="shuffleanswers" NEXT="correctfeedbackformat"/>
+        <FIELD NAME="correctfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="correctfeedback" NEXT="partiallycorrectfeedback"/>
+        <FIELD NAME="partiallycorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any partially correct response." PREVIOUS="correctfeedbackformat" NEXT="partiallycorrectfeedbackformat"/>
+        <FIELD NAME="partiallycorrectfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="partiallycorrectfeedback" NEXT="incorrectfeedback"/>
+        <FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="partiallycorrectfeedbackformat" NEXT="incorrectfeedbackformat"/>
+        <FIELD NAME="incorrectfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="incorrectfeedback" NEXT="answernumbering"/>
+        <FIELD NAME="answernumbering" TYPE="char" LENGTH="10" NOTNULL="true" DEFAULT="abc" SEQUENCE="false" COMMENT="Indicates how and whether the choices should be numbered." PREVIOUS="incorrectfeedbackformat"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/>
index c6131e3..ce05a3a 100644 (file)
@@ -138,6 +138,62 @@ function xmldb_qtype_calculated_upgrade($oldversion) {
         upgrade_plugin_savepoint(true, 2010020800, 'qtype', 'calculated');
     }
 
+    if ($oldversion < 2010020801) {
+
+        // Define field correctfeedbackformat to be added to question_calculated_options
+        $table = new xmldb_table('question_calculated_options');
+        $field = new xmldb_field('correctfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'correctfeedback');
+
+        // Conditionally launch add field correctfeedbackformat
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Define field partiallycorrectfeedbackformat to be added to question_calculated_options
+        $field = new xmldb_field('partiallycorrectfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'partiallycorrectfeedback');
+
+        // Conditionally launch add field partiallycorrectfeedbackformat
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Define field incorrectfeedbackformat to be added to question_calculated_options
+        $field = new xmldb_field('incorrectfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'incorrectfeedback');
+
+        // Conditionally launch add field incorrectfeedbackformat
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // fix fieldformat
+        $rs = $DB->get_recordset('question_calculated_options');
+        foreach ($rs as $record) {
+            if ($CFG->texteditors !== 'textarea') {
+                if (!empty($record->correctfeedback)) {
+                    $record->correctfeedback = text_to_html($record->correctfeedback);
+                }
+                $record->correctfeedbackformat = FORMAT_HTML;
+                if (!empty($record->partiallycorrectfeedback)) {
+                    $record->partiallycorrectfeedback = text_to_html($record->partiallycorrectfeedback);
+                }
+                $record->partiallycorrectfeedbackformat = FORMAT_HTML;
+                if (!empty($record->incorrectfeedback)) {
+                    $record->incorrectfeedback = text_to_html($record->incorrectfeedback);
+                }
+                $record->incorrectfeedbackformat = FORMAT_HTML;
+            } else {
+                $record->correctfeedbackformat = FORMAT_MOODLE;
+                $record->partiallycorrectfeedbackformat = FORMAT_MOODLE;
+                $record->incorrectfeedbackformat = FORMAT_MOODLE;
+            }
+            $DB->update_record('question_calculated_options', $record);
+        }
+        $rs->close();
+
+        // calculated savepoint reached
+        upgrade_plugin_savepoint(true, 2010020801, 'qtype', 'calculated');
+    }
+
     return true;
 }
 
index 7c187f4..1e52ec3 100644 (file)
@@ -1,4 +1,20 @@
 <?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/>.
+
 /**
  * Defines the editing form for the calculated question type.
  *
@@ -19,12 +35,12 @@ class question_edit_calculated_form extends question_edit_form {
      * @var question_calculated_qtype
      */
     public $qtypeobj;
-    public $questiondisplay ;
-    public $activecategory ;
-    public $categorychanged = false ;
+    public $questiondisplay;
+    public $activecategory;
+    public $categorychanged = false;
     public $initialname = '';
-    public $reload = false ;
-    
+    public $reload = false;
+
     function question_edit_calculated_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
         global $QTYPES, $SESSION, $CFG, $DB;
         $this->question = $question;
@@ -42,20 +58,20 @@ class question_edit_calculated_form extends question_edit_form {
                 $regs= array();
                 if(preg_match('~#\{([^[:space:]]*)#~',$question->name , $regs)){
                     $question->name = str_replace($regs[0], '', $question->name);
-                };                             
+                };
             }
         }else {
-        }    
+        }
         parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
     }
+
     function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
-   //     $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
-           $repeated = array();
+        // $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
+        $repeated = array();
         $repeated[] =& $mform->createElement('header', 'answerhdr', $label);
         $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
         $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
-        $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
-                                array('course' => $this->coursefilesid));
+        $repeated[] =& $mform->createElement('editor', 'feedback', get_string('feedback', 'quiz'), null, $this->editoroptions);
         $repeatedoptions['answer']['type'] = PARAM_RAW;
         $repeatedoptions['fraction']['default'] = 0;
         $answersoption = 'answers';
@@ -63,8 +79,8 @@ class question_edit_calculated_form extends question_edit_form {
         $mform->setType('answer', PARAM_NOTAGS);
 
         $addrepeated = array();
-            $addrepeated[] =& $mform->createElement('text', 'tolerance', get_string('tolerance', 'qtype_calculated'));
-            $addrepeated[] =& $mform->createElement('select', 'tolerancetype', get_string('tolerancetype', 'quiz'), $this->qtypeobj->tolerance_types());
+        $addrepeated[] =& $mform->createElement('text', 'tolerance', get_string('tolerance', 'qtype_calculated'));
+        $addrepeated[] =& $mform->createElement('select', 'tolerancetype', get_string('tolerancetype', 'quiz'), $this->qtypeobj->tolerance_types());
         $repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
         $repeatedoptions['tolerance']['default'] = 0.01;
 
@@ -74,7 +90,7 @@ class question_edit_calculated_form extends question_edit_form {
         $answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz'));
         $addrepeated[] =&  $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
         array_splice($repeated, 3, 0, $addrepeated);
-            $repeated[1]->setLabel(get_string('correctanswerformula', 'quiz').'=');
+        $repeated[1]->setLabel(get_string('correctanswerformula', 'quiz').'=');
 
         return $repeated;
     }
@@ -87,8 +103,8 @@ class question_edit_calculated_form extends question_edit_form {
     function definition_inner(&$mform) {
         global $QTYPES;
         $this->qtypeobj =& $QTYPES[$this->qtype()];
-      // echo code left for testing period
-      //  echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
+        // echo code left for testing period
+        // echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
         $label = get_string('sharedwildcards', 'qtype_calculated');
         $mform->addElement('hidden', 'initialcategory', 1);
         $mform->addElement('hidden', 'reload', 1);
@@ -100,43 +116,42 @@ class question_edit_calculated_form extends question_edit_form {
         };
         $addfieldsname='updatecategory';
         $addstring=get_string("updatecategory", "qtype_calculated");
-                $mform->registerNoSubmitButton($addfieldsname);
+        $mform->registerNoSubmitButton($addfieldsname);
 
         $mform->insertElementBefore(    $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
         $mform->registerNoSubmitButton('createoptionbutton');
 
         //editing as regular
-            $mform->setType('single', PARAM_INT);
+        $mform->setType('single', PARAM_INT);
 
-            $mform->addElement('hidden','shuffleanswers', '1');
-            $mform->setType('shuffleanswers', PARAM_INT);
-            $mform->addElement('hidden','answernumbering', 'abc');
-            $mform->setType('answernumbering', PARAM_SAFEDIR);
+        $mform->addElement('hidden','shuffleanswers', '1');
+        $mform->setType('shuffleanswers', PARAM_INT);
+        $mform->addElement('hidden','answernumbering', 'abc');
+        $mform->setType('answernumbering', PARAM_SAFEDIR);
 
         $creategrades = get_grade_options();
 
-            $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'),
-                $creategrades->gradeoptions, 1, 1);
-
+        $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'), $creategrades->gradeoptions, 1, 1);
 
         $repeated = array();
 
         $QTYPES['numerical']->add_units_options($mform,$this);
         $QTYPES['numerical']->add_units_elements($mform,$this);
+
+        $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
         foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
-            $mform->addElement('hidden', $feedbackname);
+            $mform->addElement('editor', $feedbackname, get_string($feedbackname, 'qtype_calculated'), null, $this->editoroptions);
             $mform->setType($feedbackname, PARAM_RAW);
         }
+
         //hidden elements
         $mform->addElement('hidden', 'synchronize', '');
         $mform->setType('synchronize', PARAM_INT);
         $mform->addElement('hidden', 'wizard', 'datasetdefinitions');
         $mform->setType('wizard', PARAM_ALPHA);
-
-
     }
 
-    function set_data($question) {
+    function data_preprocessing($question) {
         global $QTYPES;
 
         $default_values = array();
@@ -145,26 +160,58 @@ class question_edit_calculated_form extends question_edit_form {
             if (count($answers)) {
                 $key = 0;
                 foreach ($answers as $answer){
+                    $draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
                     $default_values['answer['.$key.']'] = $answer->answer;
                     $default_values['fraction['.$key.']'] = $answer->fraction;
                     $default_values['tolerance['.$key.']'] = $answer->tolerance;
                     $default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
                     $default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
                     $default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
-                    $default_values['feedback['.$key.']'] = $answer->feedback;
+                    $default_values['feedback['.$key.']'] = array();
+                    $default_values['feedback['.$key.']']['text'] = file_prepare_draft_area(
+                        $draftid,           // draftid
+                        $this->context->id, // context
+                        'question', // component
+                        'answerfeedback',         // filarea
+                        !empty($answer->id)?(int)$answer->id:null, // itemid
+                        $this->fileoptions, // options
+                        $answer->feedback   // text
+                    );
+                    $default_values['feedback['.$key.']']['format'] = $answer->feedbackformat;
+                    $default_values['feedback['.$key.']']['itemid'] = $draftid;
                     $key++;
                 }
             }
-          $default_values['synchronize'] = $question->options->synchronize ;
-         $QTYPES['numerical']->set_numerical_unit_data($question,$default_values);
+            $default_values['synchronize'] = $question->options->synchronize ;
+            // set unit data, prepare files in instruction area
+            $QTYPES['numerical']->set_numerical_unit_data($this, $question, $default_values);
         }
         if (isset($question->options->single)){
             $default_values['single'] =  $question->options->single;
             $default_values['answernumbering'] =  $question->options->answernumbering;
             $default_values['shuffleanswers'] =  $question->options->shuffleanswers;
-            $default_values['correctfeedback'] =  $question->options->correctfeedback;
-            $default_values['partiallycorrectfeedback'] =  $question->options->partiallycorrectfeedback;
-            $default_values['incorrectfeedback'] =  $question->options->incorrectfeedback;
+            //$default_values['correctfeedback'] =  $question->options->correctfeedback;
+            //$default_values['partiallycorrectfeedback'] =  $question->options->partiallycorrectfeedback;
+            //$default_values['incorrectfeedback'] =  $question->options->incorrectfeedback;
+            // prepare feedback editor to display files in draft area
+            foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
+                $draftid = file_get_submitted_draft_itemid($feedbackname);
+                $text = $question->options->$feedbackname;
+                $feedbackformat = $feedbackname . 'format';
+                $format = $question->options->$feedbackformat;
+                $default_values[$feedbackname] = array();
+                $default_values[$feedbackname]['text'] = file_prepare_draft_area(
+                    $draftid,       // draftid
+                    $this->context->id,    // context
+                    'qtype_calculated',   // component
+                    $feedbackname,         // filarea
+                    !empty($question->id)?(int)$question->id:null, // itemid
+                    $this->fileoptions,    // options
+                    $text      // text
+                );
+                $default_values[$feedbackname]['format'] = $format;
+                $default_values[$feedbackname]['itemid'] = $draftid;
+            }
         }
         $default_values['submitbutton'] = get_string('nextpage', 'qtype_calculated');
         $default_values['makecopy'] = get_string('makecopynextpage', 'qtype_calculated');
@@ -173,16 +220,16 @@ class question_edit_calculated_form extends question_edit_form {
         update category button. The value can be obtain by
          $qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
          but is coded using existing functions
-        */
-         $qu = new stdClass;
-         $el = new stdClass;
-         /* no need to call elementExists() here */
-         if ($this->_form->elementExists('category')){
+         */
+        $qu = new stdClass;
+        $el = new stdClass;
+        /* no need to call elementExists() here */
+        if ($this->_form->elementExists('category')){
             $el=$this->_form->getElement('category');
-         } else {
+        } else {
             $el=$this->_form->getElement('categorymoveto');
-         }
-         if($value =$el->getSelected()) {
+        }
+        if($value =$el->getSelected()) {
             $qu->category =$value[0];
         }else {
             $qu->category=$question->category;// on load  $question->category is set by question.php
@@ -191,7 +238,7 @@ class question_edit_calculated_form extends question_edit_form {
         $this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ;
         $question = (object)((array)$question + $default_values);
 
-        parent::set_data($question);
+        return $question;
     }
 
     function qtype() {
@@ -200,21 +247,21 @@ class question_edit_calculated_form extends question_edit_form {
 
     function validation($data, $files) {
         global $QTYPES;
-              // echo code left for testing period
+        // echo code left for testing period
 
-              //  echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
-              //  echo "<p>data <pre>";print_r($data);echo "</pre></p>";
+        // echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
+        // echo "<p>data <pre>";print_r($data);echo "</pre></p>";
 
         $errors = parent::validation($data, $files);
         //verifying for errors in {=...} in question text;
         $qtext = "";
-        $qtextremaining = $data['questiontext'] ;
-        $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
-            foreach ($possibledatasets as $name => $value) {
+        $qtextremaining = $data['questiontext']['text'];
+        $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
+        foreach ($possibledatasets as $name => $value) {
             $qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
         }
-    //     echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
-        while  (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
+        // echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
+        while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
             $qtextsplits = explode($regs1[0], $qtextremaining, 2);
             $qtext =$qtext.$qtextsplits[0];
             $qtextremaining = $qtextsplits[1];
@@ -229,45 +276,45 @@ class question_edit_calculated_form extends question_edit_form {
         $answers = $data['answer'];
         $answercount = 0;
         $maxgrade = false;
-        $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
+        $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
         $mandatorydatasets = array();
         foreach ($answers as $key => $answer){
             $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
         }
         if ( count($mandatorydatasets )==0){
-          //  $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
+            //  $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
             foreach ($answers as $key => $answer){
                 $errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
             }
         }
- // regular calculated
-            foreach ($answers as $key => $answer){
-                //check no of choices
-                // the * for everykind of answer not actually implemented
-                $trimmedanswer = trim($answer);
-                if (($trimmedanswer!='')||$answercount==0){
-                    $eqerror = qtype_calculated_find_formula_errors($trimmedanswer);
-                    if (FALSE !== $eqerror){
-                        $errors['answer['.$key.']'] = $eqerror;
-                    }
+        // regular calculated
+        foreach ($answers as $key => $answer){
+            //check no of choices
+            // the * for everykind of answer not actually implemented
+            $trimmedanswer = trim($answer);
+            if (($trimmedanswer!='')||$answercount==0){
+                $eqerror = qtype_calculated_find_formula_errors($trimmedanswer);
+                if (FALSE !== $eqerror){
+                    $errors['answer['.$key.']'] = $eqerror;
                 }
-                if ($trimmedanswer!=''){
-                    if ('2' == $data['correctanswerformat'][$key]
-                            && '0' == $data['correctanswerlength'][$key]) {
+            }
+            if ($trimmedanswer!=''){
+                if ('2' == $data['correctanswerformat'][$key]
+                    && '0' == $data['correctanswerlength'][$key]) {
                         $errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
                     }
-                    if (!is_numeric($data['tolerance'][$key])){
-                        $errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
-                    }
-                    if ($data['fraction'][$key] == 1) {
-                       $maxgrade = true;
-                    }
-
-                    $answercount++;
+                if (!is_numeric($data['tolerance'][$key])){
+                    $errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
                 }
-                //check grades
+                if ($data['fraction'][$key] == 1) {
+                    $maxgrade = true;
+                }
+
+                $answercount++;
+            }
+            //check grades
 
-                //TODO how should grade checking work here??
+            //TODO how should grade checking work here??
                 /*if ($answer != '') {
                     if ($data['fraction'][$key] > 0) {
                         $totalfraction += $data['fraction'][$key];
@@ -276,10 +323,10 @@ class question_edit_calculated_form extends question_edit_form {
                         $maxfraction = $data['fraction'][$key];
                     }
                 }*/
-            }
+        }
 
-            //grade checking :
-            /// Perform sanity checks on fractional grades
+        //grade checking :
+        /// Perform sanity checks on fractional grades
             /*if ( ) {
                 if ($maxfraction != 1) {
                     $maxfraction = $maxfraction * 100;
@@ -292,7 +339,7 @@ class question_edit_calculated_form extends question_edit_form {
                     $errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
                 }
             }
-            $units  = $data['unit'];
+            $units = $data['unit'];
             if (count($units)) {
                 foreach ($units as $key => $unit){
                     if (is_numeric($unit)){
@@ -309,18 +356,17 @@ class question_edit_calculated_form extends question_edit_form {
                         }
 
                     }
-                }                
+                }
             }*/
-            $QTYPES['numerical']->validate_numerical_options($data, $errors) ;
-            if ($answercount==0){
-                $errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
-            }
-            if ($maxgrade == false) {
-                $errors['fraction[0]'] = get_string('fractionsnomax', 'question');
-            }
-        
+        $QTYPES['numerical']->validate_numerical_options($data, $errors) ;
+        if ($answercount==0){
+            $errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
+        }
+        if ($maxgrade == false) {
+            $errors['fraction[0]'] = get_string('fractionsnomax', 'question');
+        }
+
 
         return $errors;
     }
 }
-
index 11def2d..bd895b1 100644 (file)
@@ -45,6 +45,7 @@ $string['choosedatasetproperties'] = 'Choose wildcards dataset properties';
 $string['choosedatasetproperties_help'] = 'A dataset is a set of values inserted in place of a wildcard. You can create a private dataset for a specific question, or a shared dataset that can be used for other calculated questions within the category.';
 $string['correctanswershows'] = 'Correct answer shows';
 $string['correctanswershowsformat'] = 'Format';
+$string['correctfeedback'] = 'For any correct response';
 $string['dataitemdefined']='with {$a} numerical values already defined is available';
 $string['datasetrole']= ' The wild cards <strong>{x..}</strong> will be substituted by a numerical value from their dataset';
 $string['deleteitem'] = 'Delete Item';
@@ -61,6 +62,7 @@ $string['forceregenerationall'] = 'forceregeneration of all wildcards';
 $string['forceregenerationshared'] = 'forceregeneration of only non-shared wildcards';
 $string['getnextnow'] = 'Get New \'Item to Add\' Now';
 $string['hexanotallowed'] = 'Dataset <strong>{$a->name}</strong> hexadecimal format value $a->value is not allowed' ;
+$string['incorrectfeedback'] = 'For any incorrect response';
 $string['item(s)'] = 'item(s)';
 $string['itemno'] = 'Item {$a}';
 $string['itemscount']='Items<br />Count';
@@ -91,8 +93,9 @@ $string['nocommaallowed'] = 'The , cannot be used, use . as in 0.013 or 1.3e-2'
 $string['nodataset'] = 'nothing - it is not a wild card';
 $string['nosharedwildcard'] = 'No shared wild card in this category';
 $string['notvalidnumber'] = 'Wild card value is not a valid number ' ;
-$string['oneanswertrueansweroutsidelimits'] = 'At least one correct answer outside the true value limits.<br />Modify the answers tolerance settings available as Advanced parameters';   
+$string['oneanswertrueansweroutsidelimits'] = 'At least one correct answer outside the true value limits.<br />Modify the answers tolerance settings available as Advanced parameters';
 $string['param'] = 'Param {<strong>{$a}</strong>}';
+$string['partiallycorrectfeedback'] = 'For any partially correct response';
 $string['possiblehdr'] = 'Possible wild cards present only in the question text';
 $string['questiondatasets'] = 'Question datasets';
 $string['questiondatasets_help'] = 'Question datasets of wild cards that will be used in each individual question';
diff --git a/question/type/calculated/lib.php b/question/type/calculated/lib.php
new file mode 100644 (file)
index 0000000..7cd6208
--- /dev/null
@@ -0,0 +1,31 @@
+<?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/>.
+
+/**
+ * Serve question type files
+ *
+ * @since 2.0
+ * @package questionbank
+ * @subpackage questiontypes
+ * @author Dongsheng Cai <dongsheng@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+function qtype_calculated_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
+    global $CFG;
+    require_once($CFG->libdir . '/questionlib.php');
+    question_pluginfile($course, $context, 'qtype_calculated', $filearea, $args, $forcedownload);
+}
index ea43f1e..794b949 100644 (file)
@@ -1,17 +1,31 @@
 <?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/>.
+
+
 /////////////////
 // CALCULATED ///
 /////////////////
 
 /// QUESTION TYPE CLASS //////////////////
 
-
-
 class question_calculated_qtype extends default_questiontype {
 
     // Used by the function custom_generator_tools:
-    var $calcgenerateidhasbeenadded = false;
+    public $calcgenerateidhasbeenadded = false;
     public $virtualqtype = false;
 
     function name() {
@@ -29,10 +43,10 @@ class question_calculated_qtype extends default_questiontype {
     function get_question_options(&$question) {
         // First get the datasets and default options
         // the code is used for calculated, calculatedsimple and calculatedmulti qtypes
-         global $CFG, $DB, $OUTPUT, $QTYPES;
+        global $CFG, $DB, $OUTPUT, $QTYPES;
         if (!$question->options = $DB->get_record('question_calculated_options', array('question' => $question->id))) {
-          //  echo $OUTPUT->notification('Error: Missing question options for calculated question'.$question->id.'!');
-          //  return false;
+            //  echo $OUTPUT->notification('Error: Missing question options for calculated question'.$question->id.'!');
+            //  return false;
             $question->options->synchronize = 0;
             $question->options->single = 0; //$question->single;
             $question->options->answernumbering = 'abc';
@@ -40,19 +54,18 @@ class question_calculated_qtype extends default_questiontype {
             $question->options->correctfeedback = '';
             $question->options->partiallycorrectfeedback = '';
             $question->options->incorrectfeedback = '';
-
         }
 
         if (!$question->options->answers = $DB->get_records_sql(
-                                "SELECT a.*, c.tolerance, c.tolerancetype, c.correctanswerlength, c.correctanswerformat " .
-                                "FROM {question_answers} a, " .
-                                "     {question_calculated} c " .
-                                "WHERE a.question = ? " .
-                                "AND   a.id = c.answer ".
-                                "ORDER BY a.id ASC", array($question->id))) {
-            echo $OUTPUT->notification('Error: Missing question answer for calculated question ' . $question->id . '!');
-            return false;
-        }
+            "SELECT a.*, c.tolerance, c.tolerancetype, c.correctanswerlength, c.correctanswerformat " .
+            "FROM {question_answers} a, " .
+            "     {question_calculated} c " .
+            "WHERE a.question = ? " .
+            "AND   a.id = c.answer ".
+            "ORDER BY a.id ASC", array($question->id))) {
+                echo $OUTPUT->notification('Error: Missing question answer for calculated question ' . $question->id . '!');
+                return false;
+            }
 
         if ( $this->get_virtual_qtype() ==  $QTYPES['numerical']){
             $QTYPES['numerical']->get_numerical_units($question);
@@ -66,16 +79,12 @@ class question_calculated_qtype extends default_questiontype {
     }
 
     function get_datasets_for_export(&$question){
-        global $DB;
+        global $DB, $CFG;
         $datasetdefs = array();
         if (!empty($question->id)) {
-            global $CFG;
             $sql = "SELECT i.*
-                    FROM {question_datasets} d,
-                         {question_dataset_definitions} i
-                    WHERE d.question = ?
-                    AND   d.datasetdefinition = i.id
-                   ";
+                      FROM {question_datasets} d, {question_dataset_definitions} i
+                     WHERE d.question = ? AND d.datasetdefinition = i.id";
             if ($records = $DB->get_records_sql($sql, array($question->id))) {
                 foreach ($records as $r) {
                     $def = $r ;
@@ -90,7 +99,7 @@ class question_calculated_qtype extends default_questiontype {
                     $def->minimum=$min;
                     $def->maximum=$max;
                     $def->decimals=$dec ;
-                     if ($def->itemcount > 0 ) {
+                    if ($def->itemcount > 0 ) {
                         // get the datasetitems
                         $def->items = array();
                         if ($items = $this->get_database_dataset_items($def->id)){
@@ -100,8 +109,8 @@ class question_calculated_qtype extends default_questiontype {
                                 $def->items[$n] = new stdClass;
                                 $def->items[$n]->itemnumber=$ii->itemnumber;
                                 $def->items[$n]->value=$ii->value;
-                           }
-                           $def->number_of_items=$n ;
+                            }
+                            $def->number_of_items=$n ;
                         }
                     }
                     $datasetdefs["1-$r->category-$r->name"] = $def;
@@ -112,10 +121,9 @@ class question_calculated_qtype extends default_questiontype {
     }
 
     function save_question_options($question) {
-        //$options = $question->subtypeoptions;
-        // Get old answers:
-        // the code is used for calculated, calculatedsimple and calculatedmulti qtypes
         global $CFG, $DB, $QTYPES ;
+        // the code is used for calculated, calculatedsimple and calculatedmulti qtypes
+        $context = $question->context;
         if (isset($question->answer) && !isset($question->answers)) {
             $question->answers = $question->answer;
         }
@@ -129,26 +137,26 @@ class question_calculated_qtype extends default_questiontype {
         }
         // as used only by calculated
         if(isset($question->synchronize)){
-        $options->synchronize = $question->synchronize;
+            $options->synchronize = $question->synchronize;
         }else {
             $options->synchronize = 0 ;
         }
         $options->single = 0; //$question->single;
         $options->answernumbering =  $question->answernumbering;
         $options->shuffleanswers = $question->shuffleanswers;
-        $options->correctfeedback = trim($question->correctfeedback);
-        $options->partiallycorrectfeedback = trim($question->partiallycorrectfeedback);
-        $options->incorrectfeedback = trim($question->incorrectfeedback);
+
+        foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
+            $feedback = $question->$feedbackname;
+            $options->$feedbackname = trim($feedback['text']);
+            $feedbackformat = $feedbackname . 'format';
+            $options->$feedbackformat = trim($feedback['format']);
+            $options->$feedbackname = file_save_draft_area_files($feedback['itemid'], $context->id, 'qtype_calculated', $feedbackname, $question->id, self::$fileoptions, trim($feedback['text']));
+        }
+
         if ($update) {
-            if (!$DB->update_record("question_calculated_options", $options)) {
-                $result->error = "Could not update calculated question options! (id=$options->id)";
-                return $result;
-            }
+            $DB->update_record("question_calculated_options", $options);
         } else {
-            if (!$DB->insert_record("question_calculated_options", $options)) {
-                $result->error = "Could not insert calculated question options!";
-                return $result;
-            }
+            $DB->insert_record("question_calculated_options", $options);
         }
 
         // Get old versions of the objects
@@ -173,18 +181,22 @@ class question_calculated_qtype extends default_questiontype {
             $question->answers=$question->answer;
         }
         foreach ($question->answers as $key => $dataanswer) {
-            if (  trim($dataanswer) != '' ) {
+            if ( trim($dataanswer) != '' ) {
                 $answer = new stdClass;
                 $answer->question = $question->id;
                 $answer->answer = trim($dataanswer);
                 $answer->fraction = $question->fraction[$key];
-                $answer->feedback = trim($question->feedback[$key]);
+                $answer->feedbackformat = $question->feedback[$key]['format'];
 
                 if ($oldanswer = array_shift($oldanswers)) {  // Existing answer, so reuse it
                     $answer->id = $oldanswer->id;
+                    $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $oldanswer->id, self::$fileoptions, trim($question->feedback[$key]['text']));
                     $DB->update_record("question_answers", $answer);
                 } else { // This is a completely new answer
+                    $answer->feedback = '';
                     $answer->id = $DB->insert_record("question_answers", $answer);
+                    $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback[$key]['text']));
+                    $DB->set_field('question_answers', 'feedback', $answer->feedback, array('id'=>$answer->id));
                 }
 
                 // Set up the options object
@@ -219,15 +231,15 @@ class question_calculated_qtype extends default_questiontype {
                 $DB->delete_records('question_calculated', array('id' => $oo->id));
             }
         }
+
         $result = $QTYPES['numerical']->save_numerical_options($question);
         if (isset($result->error)) {
             return $result;
         }
 
-
         if( isset($question->import_process)&&$question->import_process){
             $this->import_datasets($question);
-         }
+        }
         // Report any problems.
         if (!empty($result->notice)) {
             return $result;
@@ -250,12 +262,12 @@ class question_calculated_qtype extends default_questiontype {
                 $todo='create' ;
             }else if ($dataset->status =='shared' ){
                 if ($sharedatasetdefs = $DB->get_records_select(
-                        'question_dataset_definitions',
-                        "type = '1'
-                        AND name = ?
-                        AND category = ?
-                        ORDER BY id DESC ", array($dataset->name, $question->category)
-                       )) { // so there is at least one
+                    'question_dataset_definitions',
+                    "type = '1'
+                    AND name = ?
+                    AND category = ?
+                    ORDER BY id DESC ", array($dataset->name, $question->category)
+                )) { // so there is at least one
                     $sharedatasetdef = array_shift($sharedatasetdefs);
                     if ( $sharedatasetdef->options ==  $datasetdef->options ){// identical so use it
                         $todo='useit' ;
@@ -267,15 +279,15 @@ class question_calculated_qtype extends default_questiontype {
                 }else { // no so create one
                     $datasetdef->category =$question->category ;
                     $todo='create' ;
-               }
+                }
             }
             if (  $todo=='create'){
                 $datasetdef->id = $DB->insert_record( 'question_dataset_definitions', $datasetdef);
-           }
-           // Create relation to the dataset:
-           $questiondataset = new stdClass;
-           $questiondataset->question = $question->id;
-           $questiondataset->datasetdefinition = $datasetdef->id;
+            }
+            // Create relation to the dataset:
+            $questiondataset = new stdClass;
+            $questiondataset->question = $question->id;
+            $questiondataset->datasetdefinition = $datasetdef->id;
             $DB->insert_record('question_datasets', $questiondataset);
             if ($todo=='create'){ // add the items
                 foreach ($dataset->datasetitem as $dataitem ){
@@ -292,20 +304,20 @@ class question_calculated_qtype extends default_questiontype {
     function restore_session_and_responses(&$question, &$state) {
         global $OUTPUT;
         if (!preg_match('~^dataset([0-9]+)[^-]*-(.*)$~',
-                $state->responses[''], $regs)) {
-            echo $OUTPUT->notification("Wrongly formatted raw response answer " .
-                   "{$state->responses['']}! Could not restore session for " .
-                   " question #{$question->id}.");
-            $state->options->datasetitem = 1;
-            $state->options->dataset = array();
-            $state->responses = array('' => '');
-            return false;
-        }
+            $state->responses[''], $regs)) {
+                echo $OUTPUT->notification("Wrongly formatted raw response answer " .
+                    "{$state->responses['']}! Could not restore session for " .
+                    " question #{$question->id}.");
+                $state->options->datasetitem = 1;
+                $state->options->dataset = array();
+                $state->responses = array('' => '');
+                return false;
+            }
 
         // Restore the chosen dataset
         $state->options->datasetitem = $regs[1];
         $state->options->dataset =
-         $this->pick_question_dataset($question,$state->options->datasetitem);
+            $this->pick_question_dataset($question,$state->options->datasetitem);
         $state->responses = array('' => $regs[2]);
         $virtualqtype = $this->get_virtual_qtype();
         return $virtualqtype->restore_session_and_responses($question, $state);
@@ -315,29 +327,25 @@ class question_calculated_qtype extends default_questiontype {
         // Find out how many datasets are available
         global $CFG, $DB, $QTYPES, $OUTPUT;
         if(!$maxnumber = (int)$DB->get_field_sql(
-                            "SELECT MIN(a.itemcount)
-                            FROM {question_dataset_definitions} a,
-                                 {question_datasets} b
-                            WHERE b.question = ?
-                            AND   a.id = b.datasetdefinition", array($question->id))) {
+            "SELECT MIN(a.itemcount)
+               FROM {question_dataset_definitions} a, {question_datasets} b
+              WHERE b.question = ? AND a.id = b.datasetdefinition", array($question->id))) {
             print_error('cannotgetdsforquestion', 'question', '', $question->id);
         }
-                    $sql = "SELECT i.*
-                    FROM {question_datasets} d,
-                         {question_dataset_definitions} i
-                    WHERE d.question = ?
-                    AND   d.datasetdefinition = i.id
-                    AND   i.category != 0
-                   ";
+
+        $sql = "SELECT i.*
+                  FROM {question_datasets} d, {question_dataset_definitions} i
+                 WHERE d.question = ? AND d.datasetdefinition = i.id AND i.category != 0";
+
         if (!$question->options->synchronize || !$records = $DB->get_records_sql($sql, array($question->id))) {
             $synchronize_calculated  =  false ;
         }else {
-           // i.e records is true so test coherence
-           $coherence = true ;
-                $a = new stdClass ;
-                $a->qid = $question->id ;
-                $a->qcat = $question->category ;
-           foreach($records as $def ){
+            // i.e records is true so test coherence
+            $coherence = true ;
+            $a = new stdClass ;
+            $a->qid = $question->id ;
+            $a->qcat = $question->category ;
+            foreach($records as $def ){
                 if ($def->category != $question->category){
                     $a->name = $def->name;
                     $a->sharedcat = $def->category ;
@@ -346,8 +354,8 @@ class question_calculated_qtype extends default_questiontype {
                 }
             }
             if(!$coherence){
-                         echo $OUTPUT->notification(get_string('nocoherencequestionsdatyasetcategory','qtype_calculated',$a));
-          }
+                echo $OUTPUT->notification(get_string('nocoherencequestionsdatyasetcategory','qtype_calculated',$a));
+            }
             $synchronize_calculated  = true ;
         }
 
@@ -368,18 +376,16 @@ class question_calculated_qtype extends default_questiontype {
 
         };
         $state->options->dataset =
-         $this->pick_question_dataset($question,$state->options->datasetitem);
-                    $virtualqtype = $this->get_virtual_qtype( );
-            return $virtualqtype->create_session_and_responses($question, $state, $cmoptions, $attempt);
-        }
-
-
+            $this->pick_question_dataset($question,$state->options->datasetitem);
+        $virtualqtype = $this->get_virtual_qtype( );
+        return $virtualqtype->create_session_and_responses($question, $state, $cmoptions, $attempt);
+    }
 
     function save_session_and_responses(&$question, &$state) {
         global $DB;
         $responses = 'dataset'.$state->options->datasetitem.'-' ;
         // regular numeric type
-         if(isset($state->responses['unit']) && isset($question->options->units[$state->responses['unit']])){
+        if(isset($state->responses['unit']) && isset($question->options->units[$state->responses['unit']])){
             $responses .= $state->responses['answer'].'|||||'.$question->options->units[$state->responses['unit']]->unit;
         }else if(isset($state->responses['unit'])){
             $responses .= $state->responses['answer'].'|||||'.$state->responses['unit'] ;
@@ -400,7 +406,7 @@ class question_calculated_qtype extends default_questiontype {
         foreach ($form->answers as $key => $answer) {
             $a->answer              = trim($form->answer[$key]);
             $a->fraction              = $form->fraction[$key];//new
-           $a->tolerance           = $form->tolerance[$key];
+            $a->tolerance           = $form->tolerance[$key];
             $a->tolerancetype       = $form->tolerancetype[$key];
             $a->correctanswerlength = $form->correctanswerlength[$key];
             $a->correctanswerformat = $form->correctanswerformat[$key];
@@ -412,45 +418,45 @@ class question_calculated_qtype extends default_questiontype {
 
     function validate_form($form) {
         switch($form->wizardpage) {
-            case 'question':
-                $calculatedmessages = array();
-                if (empty($form->name)) {
-                    $calculatedmessages[] = get_string('missingname', 'quiz');
-                }
-                if (empty($form->questiontext)) {
-                    $calculatedmessages[] = get_string('missingquestiontext', 'quiz');
+        case 'question':
+            $calculatedmessages = array();
+            if (empty($form->name)) {
+                $calculatedmessages[] = get_string('missingname', 'quiz');
+            }
+            if (empty($form->questiontext)) {
+                $calculatedmessages[] = get_string('missingquestiontext', 'quiz');
+            }
+            // Verify formulas
+            foreach ($form->answers as $key => $answer) {
+                if ('' === trim($answer)) {
+                    $calculatedmessages[] =
+                        get_string('missingformula', 'quiz');
                 }
-                // Verify formulas
-                foreach ($form->answers as $key => $answer) {
-                    if ('' === trim($answer)) {
-                        $calculatedmessages[] =
-                            get_string('missingformula', 'quiz');
-                    }
-                    if ($formulaerrors =
-                     qtype_calculated_find_formula_errors($answer)) {
+                if ($formulaerrors =
+                    qtype_calculated_find_formula_errors($answer)) {
                         $calculatedmessages[] = $formulaerrors;
                     }
-                    if (! isset($form->tolerance[$key])) {
-                        $form->tolerance[$key] = 0.0;
-                    }
-                    if (! is_numeric($form->tolerance[$key])) {
-                        $calculatedmessages[] =
-                            get_string('tolerancemustbenumeric', 'quiz');
-                    }
+                if (! isset($form->tolerance[$key])) {
+                    $form->tolerance[$key] = 0.0;
                 }
+                if (! is_numeric($form->tolerance[$key])) {
+                    $calculatedmessages[] =
+                        get_string('tolerancemustbenumeric', 'quiz');
+                }
+            }
 
-                if (!empty($calculatedmessages)) {
-                    $errorstring = "The following errors were found:<br />";
-                    foreach ($calculatedmessages as $msg) {
-                        $errorstring .= $msg . '<br />';
-                    }
-                    print_error($errorstring);
+            if (!empty($calculatedmessages)) {
+                $errorstring = "The following errors were found:<br />";
+                foreach ($calculatedmessages as $msg) {
+                    $errorstring .= $msg . '<br />';
                 }
+                print_error($errorstring);
+            }
 
-                break;
-            default:
-                return parent::validate_form($form);
-                break;
+            break;
+        default:
+            return parent::validate_form($form);
+            break;
         }
         return true;
     }
@@ -468,16 +474,16 @@ class question_calculated_qtype extends default_questiontype {
 
         // See where we're coming from
         switch($form->wizardpage) {
-            case 'question':
-                require("$CFG->dirroot/question/type/calculated/datasetdefinitions.php");
-                break;
-            case 'datasetdefinitions':
-            case 'datasetitems':
-                require("$CFG->dirroot/question/type/calculated/datasetitems.php");
-                break;
-            default:
-                print_error('invalidwizardpage', 'question');
-                break;
+        case 'question':
+            require("$CFG->dirroot/question/type/calculated/datasetdefinitions.php");
+            break;
+        case 'datasetdefinitions':
+        case 'datasetitems':
+            require("$CFG->dirroot/question/type/calculated/datasetitems.php");
+            break;
+        default:
+            print_error('invalidwizardpage', 'question');
+            break;
         }
     }
 
@@ -495,18 +501,18 @@ class question_calculated_qtype extends default_questiontype {
 
         // See where we're coming from
         switch($wizardnow) {
-            case 'datasetdefinitions':
-                require("$CFG->dirroot/question/type/calculated/datasetdefinitions_form.php");
-                $mform = new question_dataset_dependent_definitions_form("$submiturl?wizardnow=datasetdefinitions", $question);
-                break;
-            case 'datasetitems':
-                require("$CFG->dirroot/question/type/calculated/datasetitems_form.php");
-                $regenerate = optional_param('forceregeneration', 0, PARAM_BOOL);
-                $mform = new question_dataset_dependent_items_form("$submiturl?wizardnow=datasetitems", $question, $regenerate);
-                break;
-            default:
-                print_error('invalidwizardpage', 'question');
-                break;
+        case 'datasetdefinitions':
+            require("$CFG->dirroot/question/type/calculated/datasetdefinitions_form.php");
+            $mform = new question_dataset_dependent_definitions_form("$submiturl?wizardnow=datasetdefinitions", $question);
+            break;
+        case 'datasetitems':
+            require("$CFG->dirroot/question/type/calculated/datasetitems_form.php");
+            $regenerate = optional_param('forceregeneration', 0, PARAM_BOOL);
+            $mform = new question_dataset_dependent_items_form("$submiturl?wizardnow=datasetitems", $question, $regenerate);
+            break;
+        default:
+            print_error('invalidwizardpage', 'question');
+            break;
         }
 
         return $mform;
@@ -523,25 +529,23 @@ class question_calculated_qtype extends default_questiontype {
     function display_question_editing_page(&$mform, $question, $wizardnow){
         global $OUTPUT ;
         switch ($wizardnow){
-            case '':
-                //on first page default display is fine
-                parent::display_question_editing_page($mform, $question, $wizardnow);
-                return;
-                break;
-            case 'datasetdefinitions':
-                 echo $OUTPUT->heading_with_help(get_string("choosedatasetproperties", "qtype_calculated"), 'questiondatasets', 'qtype_calculated');
-                break;
-            case 'datasetitems':
-               echo $OUTPUT->heading_with_help(get_string("editdatasets", "qtype_calculated"), 'questiondatasets', 'qtype_calculated');
-                break;
+        case '':
+            //on first page default display is fine
+            parent::display_question_editing_page($mform, $question, $wizardnow);
+            return;
+            break;
+        case 'datasetdefinitions':
+            echo $OUTPUT->heading_with_help(get_string("choosedatasetproperties", "qtype_calculated"), 'questiondatasets', 'qtype_calculated');
+            break;
+        case 'datasetitems':
+            echo $OUTPUT->heading_with_help(get_string("editdatasets", "qtype_calculated"), 'questiondatasets', 'qtype_calculated');
+            break;
         }
 
-
         $mform->display();
-
     }
 
-     /**
+    /**
      * This method prepare the $datasets in a format similar to dadatesetdefinitions_form.php
      * so that they can be saved
      * using the function save_dataset_definitions($form)
@@ -557,9 +561,9 @@ class question_calculated_qtype extends default_questiontype {
         // the dataset names present in the edit_question_form and edit_calculated_form are retrieved
         $possibledatasets = $this->find_dataset_names($form->questiontext);
         $mandatorydatasets = array();
-            foreach ($form->answers as $answer) {
-                $mandatorydatasets += $this->find_dataset_names($answer);
-            }
+        foreach ($form->answers as $answer) {
+            //$mandatorydatasets += $this->find_dataset_names($answer);
+        }
         // if there are identical datasetdefs already saved in the original question.
         // either when editing a question or saving as new
         // they are retrieved using $questionfromid
@@ -573,9 +577,9 @@ class question_calculated_qtype extends default_questiontype {
         foreach ($mandatorydatasets as $datasetname) {
             if (!isset($datasets[$datasetname])) {
                 list($options, $selected) =
-                        $this->dataset_options($form, $datasetname);
+                    $this->dataset_options($form, $datasetname);
                 $datasets[$datasetname]='';
-                 $form->dataset[$key]=$selected ;
+                $form->dataset[$key]=$selected ;
                 $key++;
             }
         }
@@ -584,32 +588,29 @@ class question_calculated_qtype extends default_questiontype {
         // the $options are not used here
         if ($questionfromid!='0'){
 
-        foreach ($possibledatasets as $datasetname) {
-            if (!isset($datasets[$datasetname])) {
-                list($options, $selected) =
+            foreach ($possibledatasets as $datasetname) {
+                if (!isset($datasets[$datasetname])) {
+                    list($options, $selected) =
                         $this->dataset_options($form, $datasetname,false);
-                $datasets[$datasetname]='';
-                 $form->dataset[$key]=$selected ;
-                $key++;
+                    $datasets[$datasetname]='';
+                    $form->dataset[$key]=$selected ;
+                    $key++;
+                }
             }
         }
-        }
-     return $datasets ;
-     }
+        return $datasets ;
+    }
     function addnamecategory(&$question){
         global $DB;
-                            $categorydatasetdefs = $DB->get_records_sql(
-                "SELECT  a.*
-                   FROM {question_datasets} b,
-                        {question_dataset_definitions} a
-                  WHERE a.id = b.datasetdefinition
-                    AND a.type = '1'
-                    AND a.category != 0
-                    AND b.question = ? ORDER BY a.name ",array($question->id));
+        $categorydatasetdefs = $DB->get_records_sql(
+            "SELECT  a.*
+               FROM {question_datasets} b, {question_dataset_definitions} a
+              WHERE a.id = b.datasetdefinition AND a.type = '1' AND a.category != 0 AND b.question = ?
+           ORDER BY a.name ", array($question->id));
         $questionname = $question->name ;
         $regs= array();
         if(preg_match('~#\{([^[:space:]]*)#~',$questionname , $regs)){
-               $questionname = str_replace($regs[0], '', $questionname);
+            $questionname = str_replace($regs[0], '', $questionname);
         };
 
         if (!empty($categorydatasetdefs)){ // there is at least one with the same name
@@ -617,35 +618,35 @@ class question_calculated_qtype extends default_questiontype {
             foreach($categorydatasetdefs as $def) {
                 if(strlen("{$def->name}")+strlen($questionname) < 250 ){
                     $questionname = '{'.$def->name.'}'
-                    .$questionname;
+                        .$questionname;
                 }
             }
             $questionname ="#".$questionname;
         }
-         if (!$DB->set_field('question', 'name', $questionname, array("id" => $question->id))) {
+        if (!$DB->set_field('question', 'name', $questionname, array("id" => $question->id))) {
             return false ;
         }
     }
 
     /**
-    * this version save the available data at the different steps of the question editing process
-    * without using global $SESSION as storage between steps
-    * at the first step $wizardnow = 'question'
-    *  when creating a new question
-    *  when modifying a question
-    *  when copying as a new question
-    *  the general parameters and answers are saved using parent::save_question
-    *  then the datasets are prepared and saved
-    * at the second step $wizardnow = 'datasetdefinitions'
-    *  the datadefs final type are defined as private, category or not a datadef
-    * at the third step $wizardnow = 'datasetitems'
-    *  the datadefs parameters and the data items are created or defined
-    *
-    * @param object question
-    * @param object $form
-    * @param int $course
-    * @param PARAM_ALPHA $wizardnow should be added as we are coming from question2.php
-    */
+     * this version save the available data at the different steps of the question editing process
+     * without using global $SESSION as storage between steps
+     * at the first step $wizardnow = 'question'
+     *  when creating a new question
+     *  when modifying a question
+     *  when copying as a new question
+     *  the general parameters and answers are saved using parent::save_question
+     *  then the datasets are prepared and saved
+     * at the second step $wizardnow = 'datasetdefinitions'
+     *  the datadefs final type are defined as private, category or not a datadef
+     * at the third step $wizardnow = 'datasetitems'
+     *  the datadefs parameters and the data items are created or defined
+     *
+     * @param object question
+     * @param object $form
+     * @param int $course
+     * @param PARAM_ALPHA $wizardnow should be added as we are coming from question2.php
+     */
     function save_question($question, $form, $course) {
         global $DB;
         $wizardnow =  optional_param('wizardnow', '', PARAM_ALPHA);
@@ -660,73 +661,73 @@ class question_calculated_qtype extends default_questiontype {
 
         // See where we're coming from
         switch($wizardnow) {
-            case '' :
-            case 'question': // coming from the first page, creating the second
-                if (empty($form->id)) { // for a new question $form->id is empty
-                    $question = parent::save_question($question, $form, $course);
-                   //prepare the datasets using default $questionfromid
-                    $this->preparedatasets($form);
-                   $form->id = $question->id;
-                   $this->save_dataset_definitions($form);
-                    if(isset($form->synchronize) && $form->synchronize == 2 ){
-                        $this->addnamecategory($question);
-                    }
-                } else if (!empty($form->makecopy)){
-                   $questionfromid =  $form->id ;
-                   $question = parent::save_question($question, $form, $course);
-                    //prepare the datasets
-                    $this->preparedatasets($form,$questionfromid);
-                    $form->id = $question->id;
-                    $this->save_as_new_dataset_definitions($form,$questionfromid );
-                    if(isset($form->synchronize) && $form->synchronize == 2 ){
-                        $this->addnamecategory($question);
-                    }
-                }  else {// editing a question
-                    $question = parent::save_question($question, $form, $course);
-                    //prepare the datasets
-                    $this->preparedatasets($form,$question->id);
-                    $form->id = $question->id;
-                    $this->save_dataset_definitions($form);
-                    if(isset($form->synchronize) && $form->synchronize == 2 ){
-                        $this->addnamecategory($question);
-                    }
+        case '' :
+        case 'question': // coming from the first page, creating the second
+            if (empty($form->id)) { // for a new question $form->id is empty
+                $question = parent::save_question($question, $form, $course);
+                //prepare the datasets using default $questionfromid
+                $this->preparedatasets($form);
+                $form->id = $question->id;
+                $this->save_dataset_definitions($form);
+                if(isset($form->synchronize) && $form->synchronize == 2 ){
+                    $this->addnamecategory($question);
                 }
-                break;
-            case 'datasetdefinitions':
-                // calculated options
-                // it cannot go here without having done the first page
-                // so the question_calculated_options should exist
-                // only need to update the synchronize field
-                if(isset($form->synchronize) ){
-                    $options_synchronize = $form->synchronize ;
-                }else {
-                    $options_synchronize = 0 ;
+            } else if (!empty($form->makecopy)){
+                $questionfromid =  $form->id ;
+                $question = parent::save_question($question, $form, $course);
+                //prepare the datasets
+                $this->preparedatasets($form,$questionfromid);
+                $form->id = $question->id;
+                $this->save_as_new_dataset_definitions($form,$questionfromid );
+                if(isset($form->synchronize) && $form->synchronize == 2 ){
+                    $this->addnamecategory($question);
                 }
-                if (!$DB->set_field('question_calculated_options', 'synchronize', $options_synchronize, array("question" => $question->id))) {
-                    return false;
+            }  else {// editing a question
+                $question = parent::save_question($question, $form, $course);
+                //prepare the datasets
+                $this->preparedatasets($form,$question->id);
+                $form->id = $question->id;
+                $this->save_dataset_definitions($form);
+                if(isset($form->synchronize) && $form->synchronize == 2 ){
+                    $this->addnamecategory($question);
                 }
-                    if(isset($form->synchronize) && $form->synchronize == 2 ){
-                        $this->addnamecategory($question);
-                    }
+            }
+            break;
+        case 'datasetdefinitions':
+            // calculated options
+            // it cannot go here without having done the first page
+            // so the question_calculated_options should exist
+            // only need to update the synchronize field
+            if(isset($form->synchronize) ){
+                $options_synchronize = $form->synchronize ;
+            }else {
+                $options_synchronize = 0 ;
+            }
+            if (!$DB->set_field('question_calculated_options', 'synchronize', $options_synchronize, array("question" => $question->id))) {
+                return false;
+            }
+            if(isset($form->synchronize) && $form->synchronize == 2 ){
+                $this->addnamecategory($question);
+            }
 
-                $this->save_dataset_definitions($form);
-                break;
-            case 'datasetitems':
-                $this->save_dataset_items($question, $form);
-                $this->save_question_calculated($question, $form);
-                break;
-            default:
-                print_error('invalidwizardpage', 'question');
-                break;
+            $this->save_dataset_definitions($form);
+            break;
+        case 'datasetitems':
+            $this->save_dataset_items($question, $form);
+            $this->save_question_calculated($question, $form);
+            break;
+        default:
+            print_error('invalidwizardpage', 'question');
+            break;
         }
         return $question;
     }
     /**
-    * Deletes question from the question-type specific tables
-    *
-    * @return boolean Success/Failure
-    * @param object $question  The question being deleted
-    */
+     * Deletes question from the question-type specific tables
+     *
+     * @return boolean Success/Failure
+     * @param object $question  The question being deleted
+     */
     function delete_question($questionid) {
         global $DB;
 
@@ -736,43 +737,43 @@ class question_calculated_qtype extends default_questiontype {
         if ($datasets = $DB->get_records('question_datasets', array('question' => $questionid))) {
             foreach ($datasets as $dataset) {
                 if (!$DB->get_records_select(
-                        'question_datasets',
-                        "question != ?
-                        AND datasetdefinition = ? ", array($questionid, $dataset->datasetdefinition))){
-                    $DB->delete_records('question_dataset_definitions', array('id' => $dataset->datasetdefinition));
-                    $DB->delete_records('question_dataset_items', array('definition' => $dataset->datasetdefinition));
-                }
+                    'question_datasets',
+                    "question != ?
+                    AND datasetdefinition = ? ", array($questionid, $dataset->datasetdefinition))){
+                        $DB->delete_records('question_dataset_definitions', array('id' => $dataset->datasetdefinition));
+                        $DB->delete_records('question_dataset_items', array('definition' => $dataset->datasetdefinition));
+                    }
             }
         }
         $DB->delete_records("question_datasets", array("question" => $questionid));
         return true;
     }
     function test_response(&$question, &$state, $answer) {
-          $virtualqtype = $this->get_virtual_qtype();
+        $virtualqtype = $this->get_virtual_qtype();
         return $virtualqtype->test_response($question, $state, $answer);
 
     }
     function compare_responses(&$question, $state, $teststate) {
 
-                 $virtualqtype = $this->get_virtual_qtype();
+        $virtualqtype = $this->get_virtual_qtype();
         return $virtualqtype->compare_responses($question, $state, $teststate);
     }
 
     function convert_answers (&$question, &$state){
-            foreach ($question->options->answers as $key => $answer) {
-                $answer = fullclone($question->options->answers[$key]);
-                $question->options->answers[$key]->answer = $this->substitute_variables_and_eval($answer->answer,
+        foreach ($question->options->answers as $key => $answer) {
+            $answer = fullclone($question->options->answers[$key]);
+            $question->options->answers[$key]->answer = $this->substitute_variables_and_eval($answer->answer,
                 $state->options->dataset);
-            }
         }
+    }
     function convert_questiontext (&$question, &$state){
         $tolerancemax =0.01;
-         $tolerancetypemax = 1 ;
-         $correctanswerlengthmax = 2 ;
-         $correctanswerformatmax = 1 ;
-         $tolerancemaxset = false ;
+        $tolerancetypemax = 1 ;
+        $correctanswerlengthmax = 2 ;
+        $correctanswerformatmax = 1 ;
+        $tolerancemaxset = false ;
         foreach ($question->options->answers as $key => $answer) {
-             if($answer->fraction == 1.0 && !$tolerancemaxset){
+            if($answer->fraction == 1.0 && !$tolerancemaxset){
                 $tolerancemax = $answer->tolerance;
                 $tolerancetypemax = $answer->tolerancetype ;
                 $correctanswerlengthmax = $answer->correctanswerlength;
@@ -780,44 +781,43 @@ class question_calculated_qtype extends default_questiontype {
                 $tolerancemaxset = true ;
             }
         }
-        $question->questiontext = $this->substitute_variables(
-        $question->questiontext, $state->options->dataset);
+        $question->questiontext = $this->substitute_variables($question->questiontext, $state->options->dataset);
         //evaluate the equations i.e {=5+4)
         $qtext = "";
         $qtextremaining = $question->questiontext ;
         while  (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
-      //  while  (preg_match('~\{=|%=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
+            //  while  (preg_match('~\{=|%=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
             $qtextsplits = explode($regs1[0], $qtextremaining, 2);
             $qtext =$qtext.$qtextsplits[0];
             $qtextremaining = $qtextsplits[1];
             if (empty($regs1[1])) {
-                    $str = '';
-                } else {
-                    if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
-                        $str=$formulaerrors ;
-                    }else {
-                       eval('$str = '.$regs1[1].';');
-                       $texteval= qtype_calculated_calculate_answer(
-                     $str, $state->options->dataset, $tolerancemax,
-                     $tolerancetypemax, $correctanswerlengthmax,
+                $str = '';
+            } else {
+                if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
+                    $str=$formulaerrors ;
+                }else {
+                    eval('$str = '.$regs1[1].';');
+                    $texteval= qtype_calculated_calculate_answer(
+                        $str, $state->options->dataset, $tolerancemax,
+                        $tolerancetypemax, $correctanswerlengthmax,
                         $correctanswerformatmax, '');
-                        $str = $texteval->answer;
+                    $str = $texteval->answer;
 
-                        ;
-                    }
+                    ;
                 }
-                $qtext = $qtext.$str ;
+            }
+            $qtext = $qtext.$str ;
         }
         $question->questiontext = $qtext.$qtextremaining ; // end replace equations
-     }
+    }
 
     function get_default_numerical_unit($question,$virtualqtype){
-            if($unit = $virtualqtype->get_default_numerical_unit($question)){
-                 $unit = $unit->unit;
-            } else {
-                $unit = '';
-            }
-            return $unit ;
+        if($unit = $virtualqtype->get_default_numerical_unit($question)){
+            $unit = $unit->unit;
+        } else {
+            $unit = '';
+        }
+        return $unit ;
 
     }
     function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
@@ -828,8 +828,8 @@ class question_calculated_qtype extends default_questiontype {
         $unit = $this-> get_default_numerical_unit($question,$virtualqtype);
         // We modify the question to look like a numerical question
         $numericalquestion = fullclone($question);
-            $this->convert_answers($numericalquestion, $state);
-            $this->convert_questiontext($numericalquestion, $state);
+        $this->convert_answers($numericalquestion, $state);
+        $this->convert_questiontext($numericalquestion, $state);
  /*        $tolerancemax =0.01;
          $tolerancetypemax = 1 ;
          $correctanswerlengthmax = 2 ;
@@ -874,7 +874,7 @@ class question_calculated_qtype extends default_questiontype {
                 $qtext = $qtext.$str ;
         }
         $numericalquestion->questiontext = $qtext.$qtextremaining ; // end replace equations
-        */
+  */
 
         $virtualqtype->print_question_formulation_and_controls($numericalquestion, $state, $cmoptions, $options);
     }
@@ -882,12 +882,12 @@ class question_calculated_qtype extends default_questiontype {
         // Forward the grading to the virtual qtype
         // We modify the question to look like a numerical question
         $numericalquestion = fullclone($question);
-       foreach ($numericalquestion->options->answers as $key => $answer) {
+        foreach ($numericalquestion->options->answers as $key => $answer) {
             $answer = $numericalquestion->options->answers[$key]->answer; // for PHP 4.x
-          $numericalquestion->options->answers[$key]->answer = $this->substitute_variables_and_eval($answer,
-             $state->options->dataset);
-       }
-         $virtualqtype = $this->get_virtual_qtype();
+            $numericalquestion->options->answers[$key]->answer = $this->substitute_variables_and_eval($answer,
+                $state->options->dataset);
+        }
+        $virtualqtype = $this->get_virtual_qtype();
         return $virtualqtype->grade_responses($numericalquestion, $state, $cmoptions) ;
     }
 
@@ -904,7 +904,7 @@ class question_calculated_qtype extends default_questiontype {
         foreach ($numericalquestion->options->answers as $key => $answer) {
             $answer = &$numericalquestion->options->answers[$key]; // for PHP 4.x
             $answer->answer = $this->substitute_variables_and_eval($answer->answer,
-             $state->options->dataset);
+                $state->options->dataset);
         }
         $virtualqtype = $this->get_virtual_qtype();
         return $virtualqtype->check_response($numericalquestion, $state) ;
@@ -926,11 +926,11 @@ class question_calculated_qtype extends default_questiontype {
         foreach ($numericalquestion->options->answers as $key => $answer) {
             $answer = &$numericalquestion->options->answers[$key]; // for PHP 4.x
             $answer->answer = $this->substitute_variables_and_eval($answer->answer,
-             $state->options->dataset);
+                $state->options->dataset);
             // apply_unit
         }
         $numericalquestion->questiontext = $this->substitute_variables_and_eval(
-                                  $numericalquestion->questiontext, $state->options->dataset);
+            $numericalquestion->questiontext, $state->options->dataset);
         $responses = $virtualqtype->get_all_responses($numericalquestion, $state);
         $response = reset($responses->responses);
         $correct = $response->answer.' : ';
@@ -946,12 +946,12 @@ class question_calculated_qtype extends default_questiontype {
 
     function create_virtual_qtype() {
         global $CFG;
-            require_once("$CFG->dirroot/question/type/numerical/questiontype.php");
-            return new question_numerical_qtype();
+        require_once("$CFG->dirroot/question/type/numerical/questiontype.php");
+        return new question_numerical_qtype();
     }
 
     function supports_dataset_item_generation() {
-    // Calcualted support generation of randomly distributed number data
+        // Calcualted support generation of randomly distributed number data
         return true;
     }
     function custom_generator_tools_part(&$mform, $idx, $j){
@@ -968,8 +968,6 @@ class question_calculated_qtype extends default_questiontype {
 
         $distriboptions = array('uniform' => get_string('uniform', 'qtype_calculated'), 'loguniform' => get_string('loguniform', 'qtype_calculated'));
         $mform->addElement('select', "calcdistribution[$idx]", get_string('calcdistribution', 'qtype_calculated'), $distriboptions);
-
-
     }
 
     function custom_generator_set_data($datasetdefs, $formdata){
@@ -990,18 +988,18 @@ class question_calculated_qtype extends default_questiontype {
     function custom_generator_tools($datasetdef) {
         global $OUTPUT;
         if (preg_match('~^(uniform|loguniform):([^:]*):([^:]*):([0-9]*)$~',
-                $datasetdef->options, $regs)) {
-            $defid = "$datasetdef->type-$datasetdef->category-$datasetdef->name";
-            for ($i = 0 ; $i<10 ; ++$i) {
-                $lengthoptions[$i] = get_string(($regs[1] == 'uniform'
-                                                ? 'decimals'
-                                                : 'significantfigures'), 'quiz', $i);
-            }
-            $menu1 = html_writer::select($lengthoptions, 'calclength[]', $regs[4], null);
-
-            $options = array('uniform' => get_string('uniform', 'quiz'), 'loguniform' => get_string('loguniform', 'quiz'));
-            $menu2 = html_writer::select($options, 'calcdistribution[]', $regs[1], null);
-            return '<input type="submit" onclick="'
+            $datasetdef->options, $regs)) {
+                $defid = "$datasetdef->type-$datasetdef->category-$datasetdef->name";
+                for ($i = 0 ; $i<10 ; ++$i) {
+                    $lengthoptions[$i] = get_string(($regs[1] == 'uniform'
+                        ? 'decimals'
+                        : 'significantfigures'), 'quiz', $i);
+                }
+                $menu1 = html_writer::select($lengthoptions, 'calclength[]', $regs[4], null);
+
+                $options = array('uniform' => get_string('uniform', 'quiz'), 'loguniform' => get_string('loguniform', 'quiz'));
+                $menu2 = html_writer::select($options, 'calcdistribution[]', $regs[1], null);
+                return '<input type="submit" onclick="'
                     . "getElementById('addform').regenerateddefid.value='$defid'; return true;"
                     .'" value="'. get_string('generatevalue', 'quiz') . '"/><br/>'
                     . '<input type="text" size="3" name="calcmin[]" '
@@ -1009,41 +1007,41 @@ class question_calculated_qtype extends default_questiontype {
                     . ' type="text" size="3" value="' . $regs[3] .'"/> '
                     . $menu1 . '<br/>'
                     . $menu2;
-        } else {
-            return '';
-        }
+            } else {
+                return '';
+            }
     }
 
 
     function update_dataset_options($datasetdefs, $form) {
         // Do we have informatin about new options???
         if (empty($form->definition) || empty($form->calcmin)
-                || empty($form->calcmax) || empty($form->calclength)
-                || empty($form->calcdistribution)) {
-            // I guess not
+            || empty($form->calcmax) || empty($form->calclength)
+            || empty($form->calcdistribution)) {
+                // I guess not
 
-        } else {
-            // Looks like we just could have some new information here
-            $uniquedefs = array_values(array_unique($form->definition));
-            foreach ($uniquedefs as $key => $defid) {
-                if (isset($datasetdefs[$defid])
+            } else {
+                // Looks like we just could have some new information here
+                $uniquedefs = array_values(array_unique($form->definition));
+                foreach ($uniquedefs as $key => $defid) {
+                    if (isset($datasetdefs[$defid])
                         && is_numeric($form->calcmin[$key+1])
                         && is_numeric($form->calcmax[$key+1])
                         && is_numeric($form->calclength[$key+1])) {
-                    switch     ($form->calcdistribution[$key+1]) {
-                        case 'uniform': case 'loguniform':
-                            $datasetdefs[$defid]->options =
+                            switch     ($form->calcdistribution[$key+1]) {
+                            case 'uniform': case 'loguniform':
+                                $datasetdefs[$defid]->options =
                                     $form->calcdistribution[$key+1] . ':'
                                     . $form->calcmin[$key+1] . ':'
                                     . $form->calcmax[$key+1] . ':'
                                     . $form->calclength[$key+1];
-                            break;
-                        default:
-                            echo $OUTPUT->notification("Unexpected distribution ".$form->calcdistribution[$key+1]);
-                    }
+                                break;
+                            default:
+                                echo $OUTPUT->notification("Unexpected distribution ".$form->calcdistribution[$key+1]);
+                            }
+                        }
                 }
             }
-        }
 
         // Look for empty options, on which we set default values
         foreach ($datasetdefs as $defid => $def) {
@@ -1068,29 +1066,29 @@ class question_calculated_qtype extends default_questiontype {
         }
     }
 
-               /**
-               * This function get the dataset items using id as unique parameter and return an
-               * array with itemnumber as index sorted ascendant
-               * If the multiple records with the same itemnumber exist, only the newest one
-               * i.e with the greatest id is used, the others are ignored but not deleted.
-               * MDL-19210
-               */
+    /**
+     * This function get the dataset items using id as unique parameter and return an
+     * array with itemnumber as index sorted ascendant
+     * If the multiple records with the same itemnumber exist, only the newest one
+     * i.e with the greatest id is used, the others are ignored but not deleted.
+     * MDL-19210
+     */
     function get_database_dataset_items($definition){
-       global $CFG, $DB;
-               $databasedataitems = $DB->get_records_sql( // Use number as key!!
-                        " SELECT id , itemnumber, definition,  value
-                          FROM {question_dataset_items}
-                          WHERE definition = $definition order by id DESC ", array($definition));
-       $dataitems = Array();
-               foreach($databasedataitems as $id => $dataitem  ){
-               if (!isset($dataitems[$dataitem->itemnumber])){
-                           $dataitems[$dataitem->itemnumber] = $dataitem ;
-                         }else {
-                               // deleting the unused records could be added here
-                         }
-               }
-               ksort($dataitems);
-               return $dataitems ;
+        global $CFG, $DB;
+        $databasedataitems = $DB->get_records_sql( // Use number as key!!
+            " SELECT id , itemnumber, definition,  value
+            FROM {question_dataset_items}
+            WHERE definition = $definition order by id DESC ", array($definition));
+        $dataitems = Array();
+        foreach($databasedataitems as $id => $dataitem  ){
+            if (!isset($dataitems[$dataitem->itemnumber])){
+                $dataitems[$dataitem->itemnumber] = $dataitem ;
+            }else {
+                // deleting the unused records could be added here
+            }
+        }
+        ksort($dataitems);
+        return $dataitems ;
     }
 
     function save_dataset_items($question, $fromform){
@@ -1119,11 +1117,11 @@ class question_calculated_qtype extends default_questiontype {
         $maxnumber = -1;
         foreach ($datasetdefs as $defid => $datasetdef) {
             if (isset($datasetdef->id)
-             && $datasetdef->options != $olddatasetdefs[$defid]->options) {
-                // Save the new value for options
-                $DB->update_record('question_dataset_definitions', $datasetdef);
+                && $datasetdef->options != $olddatasetdefs[$defid]->options) {
+                    // Save the new value for options
+                    $DB->update_record('question_dataset_definitions', $datasetdef);
 
-            }
+                }
             // Get maxnumber
             if ($maxnumber == -1 || $datasetdef->itemcount < $maxnumber) {
                 $maxnumber = $datasetdef->itemcount;
@@ -1158,16 +1156,16 @@ class question_calculated_qtype extends default_questiontype {
             $i++;
         }
         if (isset($addeditem->itemnumber) && $maxnumber < $addeditem->itemnumber
-                && $addeditem->itemnumber < CALCULATEDQUESTIONMAXITEMNUMBER ){
-            $maxnumber = $addeditem->itemnumber;
-            foreach ($datasetdefs as $key => $newdef) {
-                if (isset($newdef->id) && $newdef->itemcount <= $maxnumber) {
-                    $newdef->itemcount = $maxnumber;
-                    // Save the new value for options
-                    $DB->update_record('question_dataset_definitions', $newdef);
+            && $addeditem->itemnumber < CALCULATEDQUESTIONMAXITEMNUMBER ){
+                $maxnumber = $addeditem->itemnumber;
+                foreach ($datasetdefs as $key => $newdef) {
+                    if (isset($newdef->id) && $newdef->itemcount <= $maxnumber) {
+                        $newdef->itemcount = $maxnumber;
+                        // Save the new value for options
+                        $DB->update_record('question_dataset_definitions', $newdef);
+                    }
                 }
             }
-        }
         // adding supplementary items
         $numbertoadd =0;
         if (isset($fromform->addbutton) && $fromform->selectadd > 1 && $maxnumber < CALCULATEDQUESTIONMAXITEMNUMBER ) {
@@ -1178,30 +1176,30 @@ class question_calculated_qtype extends default_questiontype {
             //add the other items.
             // Generate a new dataset item (or reuse an old one)
             foreach ($datasetdefs as $defid => $datasetdef) {
-               // in case that for category datasets some new items has been added
-               // get actual values
-               // fix regenerate for this datadefs
-               $defregenerate = 0 ;
-               if($synchronize && !empty ($fromform->nextpageparam["datasetregenerate[$datasetdef->name"])) {
-                   $defregenerate = 1 ;
-               }else if(!$synchronize && (($regenerate == 1 && $datasetdef->category == 0) ||$regenerate == 2 )){
-                   $defregenerate = 1 ;
-               }
+                // in case that for category datasets some new items has been added
+                // get actual values
+                // fix regenerate for this datadefs
+                $defregenerate = 0 ;
+                if($synchronize && !empty ($fromform->nextpageparam["datasetregenerate[$datasetdef->name"])) {
+                    $defregenerate = 1 ;
+                }else if(!$synchronize && (($regenerate == 1 && $datasetdef->category == 0) ||$regenerate == 2 )){
+                    $defregenerate = 1 ;
+                }
                 if (isset($datasetdef->id)) {
-                       $datasetdefs[$defid]->items = $this->get_database_dataset_items($datasetdef->id);
+                    $datasetdefs[$defid]->items = $this->get_database_dataset_items($datasetdef->id);
                 }
                 for ($numberadded =$maxnumber+1 ; $numberadded <= $maxnumber+$numbertoadd ; $numberadded++){
                     if (isset($datasetdefs[$defid]->items[$numberadded])  ){
-                               // in case of regenerate it modifies the already existing record
-                               if ( $defregenerate  ) {
-                                       $datasetitem = new stdClass;
-                                       $datasetitem->id = $datasetdefs[$defid]->items[$numberadded]->id;
-                                       $datasetitem->definition = $datasetdef->id ;
-                                       $datasetitem->itemnumber = $numberadded;
-                                               $datasetitem->value = $this->generate_dataset_item($datasetdef->options);
-                                                               $DB->update_record('question_dataset_items', $datasetitem);
-                                                     }
-                                             //if not regenerate do nothing as there is already a record
+                        // in case of regenerate it modifies the already existing record
+                        if ( $defregenerate  ) {
+                            $datasetitem = new stdClass;
+                            $datasetitem->id = $datasetdefs[$defid]->items[$numberadded]->id;
+                            $datasetitem->definition = $datasetdef->id ;
+                            $datasetitem->itemnumber = $numberadded;
+                            $datasetitem->value = $this->generate_dataset_item($datasetdef->options);
+                            $DB->update_record('question_dataset_items', $datasetitem);
+                        }
+                        //if not regenerate do nothing as there is already a record
                     } else {
                         $datasetitem = new stdClass;
                         $datasetitem->definition = $datasetdef->id ;
@@ -1235,14 +1233,14 @@ class question_calculated_qtype extends default_questiontype {
                     $DB->update_record('question_dataset_definitions', $datasetdef);
                 }
             }
-       }
+        }
     }
     function generate_dataset_item($options) {
         if (!preg_match('~^(uniform|loguniform):([^:]*):([^:]*):([0-9]*)$~',
-                $options, $regs)) {
-            // Unknown options...
-            return false;
-        }
+            $options, $regs)) {
+                // Unknown options...
+                return false;
+            }
         if ($regs[1] == 'uniform') {
             $nbr = $regs[2] + ($regs[3]-$regs[2])*mt_rand()/mt_getrandmax();
             return sprintf("%.".$regs[4]."f",$nbr);
@@ -1271,12 +1269,12 @@ class question_calculated_qtype extends default_questiontype {
             } else {
                 $strheader .= $delimiter.$answer->answer;
             }
-                $delimiter = '<br/><br/><br/>';
+            $delimiter = '<br/><br/><br/>';
         }
         return $strheader;
     }
 
-    function comment_on_datasetitems($qtypeobj,$questionid,$questiontext, $answers,$data, $number) {
+    function comment_on_datasetitems($qtypeobj, $questionid, $questiontext, $answers, $data, $number) {
         global $DB, $QTYPES;
         $comment = new stdClass;
         $comment->stranswers = array();
@@ -1298,16 +1296,16 @@ class question_calculated_qtype extends default_questiontype {
         foreach ($answers as $key => $answer) {
             $formula = $this->substitute_variables($answer->answer,$data);
             $formattedanswer = qtype_calculated_calculate_answer(
-                    $answer->answer, $data, $answer->tolerance,
-                    $answer->tolerancetype, $answer->correctanswerlength,
-                    $answer->correctanswerformat, $unit);
-                    if ( $formula === '*'){
-                        $answer->min = ' ';
-                        $formattedanswer->answer = $answer->answer ;
-                    }else {
-                        eval('$answer->answer = '.$formula.';') ;
-                        $virtualqtype->get_tolerance_interval($answer);
-                    }
+                $answer->answer, $data, $answer->tolerance,
+                $answer->tolerancetype, $answer->correctanswerlength,
+                $answer->correctanswerformat, $unit);
+            if ( $formula === '*'){
+                $answer->min = ' ';
+                $formattedanswer->answer = $answer->answer ;
+            }else {
+                eval('$answer->answer = '.$formula.';') ;
+                $virtualqtype->get_tolerance_interval($answer);
+            }
             if ($answer->min === '') {
                 // This should mean that something is wrong
                 $comment->stranswers[$key] = " $formattedanswer->answer".'<br/><br/>';
@@ -1332,7 +1330,7 @@ class question_calculated_qtype extends default_questiontype {
         }
         return fullclone($comment);
     }
-    function multichoice_comment_on_datasetitems($questionid,$questiontext, $answers,$data, $number) {
+    function multichoice_comment_on_datasetitems($questionid, $questiontext, $answers,$data, $number) {
         global $DB;
         $comment = new stdClass;
         $comment->stranswers = array();
@@ -1351,34 +1349,34 @@ class question_calculated_qtype extends default_questiontype {
         $errors = '';
         $delimiter = ': ';
         foreach ($answers as $key => $answer) {
-                $answer->answer = $this->substitute_variables($answer->answer, $data);
-                //evaluate the equations i.e {=5+4)
-                $qtext = "";
-                $qtextremaining = $answer->answer ;
-                while  (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
-                    $qtextsplits = explode($regs1[0], $qtextremaining, 2);
-                    $qtext =$qtext.$qtextsplits[0];
-                    $qtextremaining = $qtextsplits[1];
-                    if (empty($regs1[1])) {
-                            $str = '';
-                        } else {
-                            if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
-                                $str=$formulaerrors ;
-                            }else {
-                                eval('$str = '.$regs1[1].';');
+            $answer->answer = $this->substitute_variables($answer->answer, $data);
+            //evaluate the equations i.e {=5+4)
+            $qtext = "";
+            $qtextremaining = $answer->answer ;
+            while  (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
+                $qtextsplits = explode($regs1[0], $qtextremaining, 2);
+                $qtext =$qtext.$qtextsplits[0];
+                $qtextremaining = $qtextsplits[1];
+                if (empty($regs1[1])) {
+                    $str = '';
+                } else {
+                    if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
+                        $str=$formulaerrors ;
+                    }else {
+                        eval('$str = '.$regs1[1].';');
 
-                       $texteval= qtype_calculated_calculate_answer(
-                     $str, $data, $answer->tolerance,
-                     $answer->tolerancetype, $answer->correctanswerlength,
-                        $answer->correctanswerformat, '');
+                        $texteval= qtype_calculated_calculate_answer(
+                            $str, $data, $answer->tolerance,
+                            $answer->tolerancetype, $answer->correctanswerlength,
+                            $answer->correctanswerformat, '');
                         $str = $texteval->answer;
 
-                            }
-                        }
-                        $qtext = $qtext.$str ;
+                    }
                 }
-                $answer->answer = $qtext.$qtextremaining ; ;
-                $comment->stranswers[$key]= $answer->answer ;
+                $qtext = $qtext.$str ;
+            }
+            $answer->answer = $qtext.$qtextremaining ; ;
+            $comment->stranswers[$key]= $answer->answer ;
 
 
           /*  $formula = $this->substitute_variables($answer->answer,$data);
@@ -1413,24 +1411,24 @@ class question_calculated_qtype extends default_questiontype {
                     $comment->stranswers[$key] .=get_string('trueanswerinsidelimits','qtype_calculated',$correcttrue);//' True answer :'.$calculated->trueanswer.' inside limits';
                 }
                 $comment->stranswers[$key] .='';
-            }*/
+          }*/
         }
         return fullclone($comment);
     }
 
     function tolerance_types() {
         return array('1'  => get_string('relative', 'quiz'),
-                     '2'  => get_string('nominal', 'quiz'),
-                     '3'  => get_string('geometric', 'quiz'));
+            '2'  => get_string('nominal', 'quiz'),
+            '3'  => get_string('geometric', 'quiz'));
     }
 
     function dataset_options($form, $name, $mandatory=true,$renameabledatasets=false) {
-    // Takes datasets from the parent implementation but
-    // filters options that are currently not accepted by calculated
-    // It also determines a default selection...
-    //$renameabledatasets not implemented anmywhere
+        // Takes datasets from the parent implementation but
+        // filters options that are currently not accepted by calculated
+        // It also determines a default selection...
+        //$renameabledatasets not implemented anmywhere
         list($options, $selected) = $this->dataset_options_from_database($form, $name,'','qtype_calculated');
-  //  list($options, $selected) = $this->dataset_optionsa($form, $name);
+        //  list($options, $selected) = $this->dataset_optionsa($form, $name);
 
         foreach ($options as $key => $whatever) {
             if (!preg_match('~^1-~', $key) && $key != '0') {
@@ -1439,7 +1437,7 @@ class question_calculated_qtype extends default_questiontype {
         }
         if (!$selected) {
             if ($mandatory){
-            $selected =  "1-0-$name"; // Default
+                $selected =  "1-0-$name"; // Default
             }else {
                 $selected = "0"; // Default
             }
@@ -1448,26 +1446,26 @@ class question_calculated_qtype extends default_questiontype {
     }
 
     function construct_dataset_menus($form, $mandatorydatasets,
-                                     $optionaldatasets) {
-        global $OUTPUT;
-        $datasetmenus = array();
-        foreach ($mandatorydatasets as $datasetname) {
-            if (!isset($datasetmenus[$datasetname])) {
-                list($options, $selected) =
+        $optionaldatasets) {
+            global $OUTPUT;
+            $datasetmenus = array();
+            foreach ($mandatorydatasets as $datasetname) {
+                if (!isset($datasetmenus[$datasetname])) {
+                    list($options, $selected) =
                         $this->dataset_options($form, $datasetname);
-                unset($options['0']); // Mandatory...
-                $datasetmenus[$datasetname] = html_writer::select($options, 'dataset[]', $selected, null);
+                    unset($options['0']); // Mandatory...
+                    $datasetmenus[$datasetname] = html_writer::select($options, 'dataset[]', $selected, null);
+                }
             }
-        }
-        foreach ($optionaldatasets as $datasetname) {
-            if (!isset($datasetmenus[$datasetname])) {
-                list($options, $selected) =
+            foreach ($optionaldatasets as $datasetname) {
+                if (!isset($datasetmenus[$datasetname])) {
+                    list($options, $selected) =
                         $this->dataset_options($form, $datasetname);
-                $datasetmenus[$datasetname] = html_writer::select($options, 'dataset[]', $selected, null);
+                    $datasetmenus[$datasetname] = html_writer::select($options, 'dataset[]', $selected, null);
+                }
             }
+            return $datasetmenus;
         }
-        return $datasetmenus;
-    }
 
     function print_question_grading_details(&$question, &$state, &$cmoptions, &$options) {
         $virtualqtype = $this->get_virtual_qtype();
@@ -1479,14 +1477,14 @@ class question_calculated_qtype extends default_questiontype {
         $virtualqtype = $this->get_virtual_qtype();
         $unit = $this->get_default_numerical_unit($question,$virtualqtype);
         // We modify the question to look like a numerical question
-            $this->convert_answers($question, $state);
-            return $virtualqtype->get_correct_responses($question, $state) ;
+        $this->convert_answers($question, $state);
+        return $virtualqtype->get_correct_responses($question, $state) ;
     }
 
     function substitute_variables($str, $dataset) {
         global $OUTPUT ;
-          //  testing for wrong numerical values
-          // all calculations used this function so testing here should be OK
+        //  testing for wrong numerical values
+        // all calculations used this function so testing here should be OK
 
         foreach ($dataset as $name => $value) {
             $val = $value ;
@@ -1494,8 +1492,8 @@ class question_calculated_qtype extends default_questiontype {
                 $a = new stdClass;
                 $a->name = '{'.$name.'}' ;
                 $a->value = $value ;
-                    echo $OUTPUT->notification(get_string('notvalidnumber','qtype_calculated',$a));
-                    $val = 1.0 ;
+                echo $OUTPUT->notification(get_string('notvalidnumber','qtype_calculated',$a));
+                $val = 1.0 ;
             }
             if($val < 0 ){
                 $str = str_replace('{'.$name.'}', '('.$val.')', $str);
@@ -1507,7 +1505,7 @@ class question_calculated_qtype extends default_questiontype {
     }
     function evaluate_equations($str, $dataset){
         $formula = $this->substitute_variables($str, $dataset) ;
-       if ($error = qtype_calculated_find_formula_errors($formula)) {
+        if ($error = qtype_calculated_find_formula_errors($formula)) {
             return $error;
         }
         return $str;
@@ -1516,7 +1514,7 @@ class question_calculated_qtype extends default_questiontype {
 
     function substitute_variables_and_eval($str, $dataset) {
         $formula = $this->substitute_variables($str, $dataset) ;
-       if ($error = qtype_calculated_find_formula_errors($formula)) {
+        if ($error = qtype_calculated_find_formula_errors($formula)) {
             return $error;
         }
         /// Calculate the correct answer
@@ -1537,11 +1535,8 @@ class question_calculated_qtype extends default_questiontype {
         if (!empty($questionid)) {
             global $CFG;
             $sql = "SELECT i.*
-                    FROM {question_datasets} d,
-                         {question_dataset_definitions} i
-                    WHERE d.question = ?
-                    AND   d.datasetdefinition = i.id
-                   ";
+                      FROM {question_datasets} d, {question_dataset_definitions} i
+                     WHERE d.question = ? AND d.datasetdefinition = i.id";
             if ($records = $DB->get_records_sql($sql, array($questionid))) {
                 foreach ($records as $r) {
                     $datasetdefs["$r->type-$r->category-$r->name"] = $r;
@@ -1573,6 +1568,9 @@ class question_calculated_qtype extends default_questiontype {
         global $DB;
         // save synchronize
 
+        if (empty($form->dataset)) {
+            $form->dataset = array();
+        }
         // Save datasets
         $datasetdefinitions = $this->get_dataset_definitions($form->id, $form->dataset);
         $tmpdatasets = array_flip($form->dataset);
@@ -1581,7 +1579,7 @@ class question_calculated_qtype extends default_questiontype {
             $datasetdef = &$datasetdefinitions[$defid];
             if (isset($datasetdef->id)) {
                 if (!isset($tmpdatasets[$defid])) {
-                // This dataset is not used any more, delete it
+                    // This dataset is not used any more, delete it
                     $DB->delete_records('question_datasets', array('question' => $form->id, 'datasetdefinition' => $datasetdef->id));
                     if ($datasetdef->category == 0) { // Question local dataset
                         $DB->delete_records('question_dataset_definitions', array('id' => $datasetdef->id));
@@ -1601,12 +1599,12 @@ class question_calculated_qtype extends default_questiontype {
                 // By first creating the datasetdefinition above we
                 // can manage to automatically take care of
                 // some possible realtime concurrence
-                if ($olderdatasetdefs = $DB->get_records_select( 'question_dataset_definitions',
-                        "type = ?
-                        AND name = ?
-                        AND category = ?
-                        AND id < ?
-                        ORDER BY id DESC", array($datasetdef->type, $datasetdef->name, $datasetdef->category, $datasetdef->id))) {
+                if ($olderdatasetdefs = $DB->get_records_select('question_dataset_definitions',
+                         "type = ?
+                      AND name = ?
+                      AND category = ?
+                      AND id < ?
+                    ORDER BY id DESC", array($datasetdef->type, $datasetdef->name, $datasetdef->category, $datasetdef->id))) {
 
                     while ($olderdatasetdef = array_shift($olderdatasetdefs)) {
                         $DB->delete_records('question_dataset_definitions', array('id' => $datasetdef->id));
@@ -1637,12 +1635,12 @@ class question_calculated_qtype extends default_questiontype {
         }
     }
     /** This function create a copy of the datasets ( definition and dataitems)
-    * from the preceding question if they remain in the new question
-    * otherwise its create the datasets that have been added as in the
-    * save_dataset_definitions()
-    */
+     * from the preceding question if they remain in the new question
+     * otherwise its create the datasets that have been added as in the
+     * save_dataset_definitions()
+     */
     function save_as_new_dataset_definitions($form, $initialid) {
-    global $CFG, $DB;
+        global $CFG, $DB;
         // Get the datasets from the intial question
         $datasetdefinitions = $this->get_dataset_definitions($initialid, $form->dataset);
         // $tmpdatasets contains those of the new question
@@ -1659,13 +1657,13 @@ class question_calculated_qtype extends default_questiontype {
                 }
                 // create a copy but not for category one
                 if (0 == $datasetdef->category) {
-                   $olddatasetid = $datasetdef->id ;
-                   $olditemcount = $datasetdef->itemcount ;
-                   $datasetdef->itemcount =0;
-                   $datasetdef->id = $DB->insert_record('question_dataset_definitions', $datasetdef);
-                   //copy the dataitems
-                   $olditems = $this->get_database_dataset_items($olddatasetid);
-                   if (count($olditems) > 0 ) {
+                    $olddatasetid = $datasetdef->id ;
+                    $olditemcount = $datasetdef->itemcount ;
+                    $datasetdef->itemcount =0;
+                    $datasetdef->id = $DB->insert_record('question_dataset_definitions', $datasetdef);
+                    //copy the dataitems
+                    $olditems = $this->get_database_dataset_items($olddatasetid);
+                    if (count($olditems) > 0 ) {
                         $itemcount = 0;
                         foreach($olditems as $item ){
                             $item->definition = $datasetdef->id;
@@ -1701,18 +1699,18 @@ class question_calculated_qtype extends default_questiontype {
                 // can manage to automatically take care of
                 // some possible realtime concurrence
                 if ($olderdatasetdefs = $DB->get_records_select(
-                        'question_dataset_definitions',
-                        "type = ?
-                        AND name = ?
-                        AND category = ?
-                        AND id < ?
-                        ORDER BY id DESC", array($datasetdef->type, $datasetdef->name, $datasetdef->category, $datasetdef->id))) {
-
-                    while ($olderdatasetdef = array_shift($olderdatasetdefs)) {
-                        $DB->delete_records('question_dataset_definitions', array('id' => $datasetdef->id));
-                        $datasetdef = $olderdatasetdef;
+                    'question_dataset_definitions',
+                    "type = ?
+                    AND name = ?
+                    AND category = ?
+                    AND id < ?
+                    ORDER BY id DESC", array($datasetdef->type, $datasetdef->name, $datasetdef->category, $datasetdef->id))) {
+
+                        while ($olderdatasetdef = array_shift($olderdatasetdefs)) {
+                            $DB->delete_records('question_dataset_definitions', array('id' => $datasetdef->id));
+                            $datasetdef = $olderdatasetdef;
+                        }
                     }
-                }
             }
 
             // Create relation to this dataset:
@@ -1737,31 +1735,27 @@ class question_calculated_qtype extends default_questiontype {
         }
     }
 
-/// Dataset functionality
+    /// Dataset functionality
     function pick_question_dataset($question, $datasetitem) {
         // Select a dataset in the following format:
         // An array indexed by the variable names (d.name) pointing to the value
         // to be substituted
         global $CFG, $DB;
         if (!$dataitems = $DB->get_records_sql(
-                        "SELECT i.id, d.name, i.value
-                        FROM {question_dataset_definitions} d,
-                             {question_dataset_items} i,
-                             {question_datasets} q
-                        WHERE q.question = ?
-                        AND q.datasetdefinition = d.id
-                        AND d.id = i.definition
-                        AND i.itemnumber = ? ORDER by i.id DESC ", array($question->id, $datasetitem))) {
+            "SELECT i.id, d.name, i.value
+               FROM {question_dataset_definitions} d, {question_dataset_items} i, {question_datasets} q
+              WHERE q.question = ? AND q.datasetdefinition = d.id AND d.id = i.definition AND i.itemnumber = ?
+           ORDER by i.id DESC ", array($question->id, $datasetitem))) {
             print_error('cannotgetdsfordependent', 'question', '', array($question->id, $datasetitem));
         }
         $dataset = Array();
-               foreach($dataitems as $id => $dataitem  ){
-               if (!isset($dataset[$dataitem->name])){
-                           $dataset[$dataitem->name] = $dataitem->value ;
-                         }else {
-                               // deleting the unused records could be added here
-                         }
-               }
+        foreach($dataitems as $id => $dataitem  ){
+            if (!isset($dataset[$dataitem->name])){
+                $dataset[$dataitem->name] = $dataitem->value ;
+            }else {
+                // deleting the unused records could be added here
+            }
+        }
         return $dataset;
     }
 
@@ -1779,54 +1773,51 @@ class question_calculated_qtype extends default_questiontype {
             $currentdatasetdef->type = '0';
         }else {
 
-        // Construct question local options
-        if ( ! $currentdatasetdef = $DB->get_record_sql(
-                "SELECT a.*
-                   FROM {question_dataset_definitions} a,
-                        {question_datasets} b
-                  WHERE a.id = b.datasetdefinition
-                    AND a.type = '1'
-                    AND b.question = ?
-                    AND a.name = ?", array($form->id, $name))){
-            $currentdatasetdef->type = '0';
-         };
-        $key = "$type-0-$name";
-        if ($currentdatasetdef->type == $type
+            // Construct question local options
+            $sql = "SELECT a.*
+                FROM {question_dataset_definitions} a, {question_datasets} b
+               WHERE a.id = b.datasetdefinition AND a.type = '1' AND b.question = ? AND a.name = ?";
+            $currentdatasetdef = $DB->get_record_sql($sql, array($form->id, $name));
+            if (!$currentdatasetdef) {
+                $currentdatasetdef->type = '0';
+            }
+            $key = "$type-0-$name";
+            if ($currentdatasetdef->type == $type
                 and $currentdatasetdef->category == 0) {
-            $options[$key] = get_string($prefix."keptlocal$type", $langfile);
-        } else {
-            $options[$key] = get_string($prefix."newlocal$type", $langfile);
-        }
+                    $options[$key] = get_string($prefix."keptlocal$type", $langfile);
+                } else {
+                    $options[$key] = get_string($prefix."newlocal$type", $langfile);
+                }
         }
         // Construct question category options
         $categorydatasetdefs = $DB->get_records_sql(
-                "SELECT b.question, a.*
-                   FROM {question_datasets} b,
-                        {question_dataset_definitions} a
-                  WHERE a.id = b.datasetdefinition
-                    AND a.type = '1'
-                    AND a.category = ?
-                    AND a.name = ?", array($form->category, $name));
+            "SELECT b.question, a.*
+            FROM {question_datasets} b,
+            {question_dataset_definitions} a
+            WHERE a.id = b.datasetdefinition
+            AND a.type = '1'
+            AND a.category = ?
+            AND a.name = ?", array($form->category, $name));
         $type = 1 ;
         $key = "$type-$form->category-$name";
         if (!empty($categorydatasetdefs)){ // there is at least one with the same name
             if (isset($form->id) && isset($categorydatasetdefs[$form->id])) {// it is already used by this question
-                    $options[$key] = get_string($prefix."keptcategory$type", $langfile);
-                } else {
-                    $options[$key] = get_string($prefix."existingcategory$type", $langfile);
-                }
+                $options[$key] = get_string($prefix."keptcategory$type", $langfile);
+            } else {
+                $options[$key] = get_string($prefix."existingcategory$type", $langfile);
+            }
         } else {
             $options[$key] = get_string($prefix."newcategory$type", $langfile);
         }
         // All done!
         return array($options, $currentdatasetdef->type
-                ? "$currentdatasetdef->type-$currentdatasetdef->category-$name"
-                : '');
+            ? "$currentdatasetdef->type-$currentdatasetdef->category-$name"
+            : '');
     }
 
     function find_dataset_names($text) {
-    /// Returns the possible dataset names found in the text as an array
-    /// The array has the dataset name for both key and value
+        /// Returns the possible dataset names found in the text as an array
+        /// The array has the dataset name for both key and value
         $datasetnames = array();
         while (preg_match('~\\{([[:alpha:]][^>} <{"\']*)\\}~', $text, $regs)) {
             $datasetnames[$regs[1]] = $regs[1];
@@ -1836,37 +1827,34 @@ class question_calculated_qtype extends default_questiontype {
     }
 
     /**
-    * This function retrieve the item count of the available category shareable
-    * wild cards that is added as a comment displayed when a wild card with
-    * the same name is displayed in datasetdefinitions_form.php
-    */
+     * This function retrieve the item count of the available category shareable
+     * wild cards that is added as a comment displayed when a wild card with
+     * the same name is displayed in datasetdefinitions_form.php
+     */
     function get_dataset_definitions_category($form) {
         global $CFG, $DB;
         $datasetdefs = array();
         $lnamemax = 30;
         if (!empty($form->category)) {
             $sql = "SELECT i.*,d.*
-                    FROM {question_datasets} d,
-                         {question_dataset_definitions} i
-                  WHERE i.id = d.datasetdefinition
-                    AND i.category = ?";
-             if ($records = $DB->get_records_sql($sql, array($form->category))) {
-                   foreach ($records as $r) {
-                       if ( !isset ($datasetdefs["$r->name"])) $datasetdefs["$r->name"] = $r->itemcount;
-                    }
+                      FROM {question_datasets} d, {question_dataset_definitions} i
+                     WHERE i.id = d.datasetdefinition AND i.category = ?";
+            if ($records = $DB->get_records_sql($sql, array($form->category))) {
+                foreach ($records as $r) {
+                    if ( !isset ($datasetdefs["$r->name"])) $datasetdefs["$r->name"] = $r->itemcount;
                 }
+            }
         }
         return  $datasetdefs ;
     }
 
     /**
-    * This function build a table showing the available category shareable
-    * wild cards, their name, their definition (Min, Max, Decimal) , the item count
-    * and the name of the question where they are used.
-    * This table is intended to be add before the question text to help the user use
-    * these wild cards
-    */
-
+     * This function build a table showing the available category shareable
+     * wild cards, their name, their definition (Min, Max, Decimal) , the item count
+     * and the name of the question where they are used.
+     * This table is intended to be add before the question text to help the user use
+     * these wild cards
+     */
     function print_dataset_definitions_category($form) {
         global $CFG, $DB;
         $datasetdefs = array();
@@ -1877,19 +1865,19 @@ class question_calculated_qtype extends default_questiontype {
         $rangeofvaluestr=get_string('minmax','qtype_calculated');
         $questionusingstr = get_string('usedinquestion','qtype_calculated');
         $itemscountstr = get_string('itemscount','qtype_calculated');
-       $text ='';
+        $text ='';
         if (!empty($form->category)) {
             list($category) = explode(',', $form->category);
             $sql = "SELECT i.*,d.*
-                    FROM {question_datasets} d,
-                         {question_dataset_definitions} i
-                    WHERE i.id = d.datasetdefinition
-                    AND i.category = ?";
+                FROM {question_datasets} d,
+        {question_dataset_definitions} i
+        WHERE i.id = d.datasetdefinition
+        AND i.category = ?";
             if ($records = $DB->get_records_sql($sql, array($category))) {
                 foreach ($records as $r) {
                     $sql1 = "SELECT q.*
-                        FROM  {question} q
-                             WHERE q.id = ?";
+                               FROM {question} q
+                              WHERE q.id = ?";
                     if ( !isset ($datasetdefs["$r->type-$r->category-$r->name"])){
                         $datasetdefs["$r->type-$r->category-$r->name"]= $r;
                     }
@@ -1909,7 +1897,7 @@ class question_calculated_qtype extends default_questiontype {
                     //limit the name length displayed
                     if (!empty($qu->name)) {
                         $qu->name = (strlen($qu->name) > $lnamemax) ?
-                        substr($qu->name, 0, $lnamemax).'...' : $qu->name;
+                            substr($qu->name, 0, $lnamemax).'...' : $qu->name;
                     } else {
                         $qu->name = '';
                     }
@@ -1919,18 +1907,18 @@ class question_calculated_qtype extends default_questiontype {
             }
             $text .="</table>";
         }else{
-             $text .=get_string('nosharedwildcard', 'qtype_calculated');
+            $text .=get_string('nosharedwildcard', 'qtype_calculated');
         }
         return  $text ;
     }
 
     /**
-    * This function build a table showing the available category shareable
-    * wild cards, their name, their definition (Min, Max, Decimal) , the item count
-    * and the name of the question where they are used.
-    * This table is intended to be add before the question text to help the user use
-    * these wild cards
-    */
+     * This function build a table showing the available category shareable
+     * wild cards, their name, their definition (Min, Max, Decimal) , the item count
+     * and the name of the question where they are used.
+     * This table is intended to be add before the question text to help the user use
+     * these wild cards
+     */
 
     function print_dataset_definitions_category_shared($question,$datasetdefsq) {
         global $CFG, $DB;
@@ -1942,19 +1930,17 @@ class question_calculated_qtype extends default_questiontype {
         $rangeofvaluestr=get_string('minmax','qtype_calculated');
         $questionusingstr = get_string('usedinquestion','qtype_calculated');
         $itemscountstr = get_string('itemscount','qtype_calculated');
-       $text ='';
+        $text ='';
         if (!empty($question->category)) {
             list($category) = explode(',', $question->category);
             $sql = "SELECT i.*,d.*
-                    FROM {question_datasets} d,
-                         {question_dataset_definitions} i
-                    WHERE i.id = d.datasetdefinition
-                    AND i.category = ?";
+                      FROM {question_datasets} d, {question_dataset_definitions} i
+                     WHERE i.id = d.datasetdefinition AND i.category = ?";
             if ($records = $DB->get_records_sql($sql, array($category))) {
                 foreach ($records as $r) {
                     $sql1 = "SELECT q.*
-                        FROM  {question} q
-                             WHERE q.id = ?";
+                               FROM {question} q
+                              WHERE q.id = ?";
                     if ( !isset ($datasetdefs["$r->type-$r->category-$r->name"])){
                         $datasetdefs["$r->type-$r->category-$r->name"]= $r;
                     }
@@ -1982,7 +1968,7 @@ class question_calculated_qtype extends default_questiontype {
                     //limit the name length displayed
                     if (!empty($qu->name)) {
                         $qu->name = (strlen($qu->name) > $lnamemax) ?
-                        substr($qu->name, 0, $lnamemax).'...' : $qu->name;
+                            substr($qu->name, 0, $lnamemax).'...' : $qu->name;
                     } else {
                         $qu->name = '';
                     }
@@ -1991,42 +1977,42 @@ class question_calculated_qtype extends default_questiontype {
                     }
                     $line++;
                     $text .="<td align=\"left\" style=\"white-space:nowrap;\" >$qu->name</td>";
-                 $nb_of_quiz = 0;
-                 $nb_of_attempts=0;
-                 $used_in_quiz = false ;
-                 if ($list = $DB->get_records('quiz_question_instances', array( 'question'=> $qu->id))){
-                            $used_in_quiz = true;
-                    foreach($list as $key => $li){
-                        $nb_of_quiz ++;
-                        if($att = $DB->get_records('quiz_attempts',array( 'quiz'=> $li->quiz, 'preview'=> '0'))){
-                            $nb_of_attempts+= count($att);
+                    $nb_of_quiz = 0;
+                    $nb_of_attempts=0;
+                    $used_in_quiz = false ;
+                    if ($list = $DB->get_records('quiz_question_instances', array( 'question'=> $qu->id))){
+                        $used_in_quiz = true;
+                        foreach($list as $key => $li){
+                            $nb_of_quiz ++;
+                            if($att = $DB->get_records('quiz_attempts',array( 'quiz'=> $li->quiz, 'preview'=> '0'))){
+                                $nb_of_attempts+= count($att);
+                            }
                         }
                     }
-                  }
-                  if($used_in_quiz){
-                    $text .="<td align=\"center\">$nb_of_quiz</td>";
-                }else {
-                    $text .="<td align=\"center\">0</td>";
-                }
-                  if($used_in_quiz){
-                    $text .="<td align=\"center\">$nb_of_attempts";
-                }else {
-                     $text .="<td align=\"left\"><br/>";
-                }
+                    if($used_in_quiz){
+                        $text .="<td align=\"center\">$nb_of_quiz</td>";
+                    }else {
+                        $text .="<td align=\"center\">0</td>";
+                    }
+                    if($used_in_quiz){
+                        $text .="<td align=\"center\">$nb_of_attempts";
+                    }else {
+                        $text .="<td align=\"left\"><br/>";
+                    }
 
-                $text .="</td></tr>";
+                    $text .="</td></tr>";
                 }
             }
             $text .="</table>";
         }else{
-             $text .=get_string('nosharedwildcard', 'qtype_calculated');
+            $text .=get_string('nosharedwildcard', 'qtype_calculated');
         }
         return  $text ;
     }
 
     function find_math_equations($text) {
-    /// Returns the possible dataset names found in the text as an array
-    /// The array has the dataset name for both key and value
+        /// Returns the possible dataset names found in the text as an array
+        /// The array has the dataset name for both key and value
         $equations = array();
  /*               $qtext = "";
         $qtextremaining = $numericalquestion->questiontext ;
@@ -2038,7 +2024,7 @@ class question_calculated_qtype extends default_questiontype {
             if (empty($regs1[1])) {
                     $str = '';
                 } else {
-*/
+  */
         while (preg_match('~\{=([^[:space:]}]*)}~', $text, $regs)) {
             $equations[] = $regs[1];
             $text = str_replace($regs[0], '', $text);
@@ -2048,12 +2034,12 @@ class question_calculated_qtype extends default_questiontype {
 
     function get_virtual_qtype() {
         global $QTYPES;
-            $this->virtualqtype =& $QTYPES['numerical'];
+        $this->virtualqtype =& $QTYPES['numerical'];
         return $this->virtualqtype;
     }
 
 
-/// BACKUP FUNCTIONS ////////////////////////////
+    /// BACKUP FUNCTIONS ////////////////////////////
 
     /*
      * Backup the data in the question
@@ -2105,7 +2091,7 @@ class question_calculated_qtype extends default_questiontype {
         return $status;
     }
 
-/// RESTORE FUNCTIONS /////////////////
+    /// RESTORE FUNCTIONS /////////////////
 
     /*
      * Restores the data in the question
@@ -2154,43 +2140,43 @@ class question_calculated_qtype extends default_questiontype {
                 }
                 backup_flush(300);
             }
-        //Get the calculated_options array
-        // need to check as old questions don't have calculated_options record
-        if(isset($info['#']['CALCULATED_OPTIONS'])){
-            $calculatedoptions = $info['#']['CALCULATED_OPTIONS'];
-
-            //Iterate over calculated_options
-            for($i = 0; $i < sizeof($calculatedoptions); $i++){
-                $cal_info = $calculatedoptions[$i];
-                //traverse_xmlize($cal_info);                                                                 //Debug
-                //print_object ($GLOBALS['traverse_array']);                                                  //Debug
-                //$GLOBALS['traverse_array']="";                                                              //Debug
-
-                //Now, build the question_calculated_options record structure
-                $calculated_options->questionid = $new_question_id;
-                $calculated_options->synchronize = backup_todb($cal_info['#']['SYNCHRONIZE']['0']['#']);
-                $calculated_options->single = backup_todb($cal_info['#']['SINGLE']['0']['#']);
-                $calculated_options->shuffleanswers = isset($cal_info['#']['SHUFFLEANSWERS']['0']['#'])?backup_todb($mul_info['#']['SHUFFLEANSWERS']['0']['#']):'';
-                $calculated_options->correctfeedback = backup_todb($cal_info['#']['CORRECTFEEDBACK']['0']['#']);
-                $calculated_options->partiallycorrectfeedback = backup_todb($cal_info['#']['PARTIALLYCORRECTFEEDBACK']['0']['#']);
-                $calculated_options->incorrectfeedback = backup_todb($cal_info['#']['INCORRECTFEEDBACK']['0']['#']);
-                $calculated_options->answernumbering = backup_todb($cal_info['#']['ANSWERNUMBERING']['0']['#']);
-
-                //The structure is equal to the db, so insert the question_calculated_options
-                $newid = $DB->insert_record ("question_calculated_options",$calculated_options);
-
-                //Do some output
-                if (($i+1) % 50 == 0) {
-                    if (!defined('RESTORE_SILENTLY')) {
-                        echo ".";
-                        if (($i+1) % 1000 == 0) {
-                            echo "<br />";
+            //Get the calculated_options array
+            // need to check as old questions don't have calculated_options record
+            if(isset($info['#']['CALCULATED_OPTIONS'])){
+                $calculatedoptions = $info['#']['CALCULATED_OPTIONS'];
+
+                //Iterate over calculated_options
+                for($i = 0; $i < sizeof($calculatedoptions); $i++){
+                    $cal_info = $calculatedoptions[$i];
+                    //traverse_xmlize($cal_info);                                                                 //Debug
+                    //print_object ($GLOBALS['traverse_array']);                                                  //Debug
+                    //$GLOBALS['traverse_array']="";                                                              //Debug
+
+                    //Now, build the question_calculated_options record structure
+                    $calculated_options->questionid = $new_question_id;
+                    $calculated_options->synchronize = backup_todb($cal_info['#']['SYNCHRONIZE']['0']['#']);
+                    $calculated_options->single = backup_todb($cal_info['#']['SINGLE']['0']['#']);
+                    $calculated_options->shuffleanswers = isset($cal_info['#']['SHUFFLEANSWERS']['0']['#'])?backup_todb($mul_info['#']['SHUFFLEANSWERS']['0']['#']):'';
+                    $calculated_options->correctfeedback = backup_todb($cal_info['#']['CORRECTFEEDBACK']['0']['#']);
+                    $calculated_options->partiallycorrectfeedback = backup_todb($cal_info['#']['PARTIALLYCORRECTFEEDBACK']['0']['#']);
+                    $calculated_options->incorrectfeedback = backup_todb($cal_info['#']['INCORRECTFEEDBACK']['0']['#']);
+                    $calculated_options->answernumbering = backup_todb($cal_info['#']['ANSWERNUMBERING']['0']['#']);
+
+                    //The structure is equal to the db, so insert the question_calculated_options
+                    $newid = $DB->insert_record ("question_calculated_options",$calculated_options);
+
+                    //Do some output
+                    if (($i+1) % 50 == 0) {
+                        if (!defined('RESTORE_SILENTLY')) {
+                            echo ".";
+                            if (($i+1) % 1000 == 0) {
+                                echo "<br />";
+                            }
                         }
+                        backup_flush(300);
                     }
-                    backup_flush(300);
                 }
             }
-            }
             //Now restore numerical_units
             $status = question_restore_numerical_units ($old_question_id,$new_question_id,$cal_info,$restore);
             $status = question_restore_numerical_options($old_question_id,$new_question_id,$info,$restore);
@@ -2207,53 +2193,125 @@ class question_calculated_qtype extends default_questiontype {
         return $status;
     }
 
-       /**
-   * Runs all the code required to set up and save an essay question for testing purposes.
-   * Alternate DB table prefix may be used to facilitate data deletion.
-   */
-  function generate_test($name, $courseid = null) {
-      global $DB;
-      list($form, $question) = parent::generate_test($name, $courseid);
-      $form->feedback = 1;
-      $form->multiplier = array(1, 1);
-      $form->shuffleanswers = 1;
-      $form->noanswers = 1;
-      $form->qtype ='calculated';
-      $question->qtype ='calculated';
-      $form->answers = array('{a} + {b}');
-      $form->fraction = array(1);
-      $form->tolerance = array(0.01);
-      $form->tolerancetype = array(1);
-      $form->correctanswerlength = array(2);
-      $form->correctanswerformat = array(1);
-      $form->questiontext = "What is {a} + {b}?";
-
-      if ($courseid) {
-          $course = $DB->get_record('course', array('id'=> $courseid));
-      }
-
-      $new_question = $this->save_question($question, $form, $course);
-
-      $dataset_form = new stdClass();
-      $dataset_form->nextpageparam["forceregeneration"]= 1;
-      $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
-      $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
-      $dataset_form->calclength = array(1 => 1, 2 => 1);
-      $dataset_form->number = array(1 => 5.4 , 2 => 4.9);
-      $dataset_form->itemid = array(1 => '' , 2 => '');
-      $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
-      $dataset_form->definition = array(1 => "1-0-a",
-                                        2 => "1-0-b");
-      $dataset_form->nextpageparam = array('forceregeneration' => false);
-      $dataset_form->addbutton = 1;
-      $dataset_form->selectadd = 1;
-      $dataset_form->courseid = $courseid;
-      $dataset_form->cmid = 0;
-      $dataset_form->id = $new_question->id;
-      $this->save_dataset_items($new_question, $dataset_form);
-
-      return $new_question;
-  }
+    /**
+     * Runs all the code required to set up and save an essay question for testing purposes.
+     * Alternate DB table prefix may be used to facilitate data deletion.
+     */
+    function generate_test($name, $courseid = null) {
+        global $DB;
+        list($form, $question) = parent::generate_test($name, $courseid);
+        $form->feedback = 1;
+        $form->multiplier = array(1, 1);
+        $form->shuffleanswers = 1;
+        $form->noanswers = 1;
+        $form->qtype ='calculated';
+        $question->qtype ='calculated';
+        $form->answers = array('{a} + {b}');
+        $form->fraction = array(1);
+        $form->tolerance = array(0.01);
+        $form->tolerancetype = array(1);
+        $form->correctanswerlength = array(2);
+        $form->correctanswerformat = array(1);
+        $form->questiontext = "What is {a} + {b}?";
+
+        if ($courseid) {
+            $course = $DB->get_record('course', array('id'=> $courseid));
+        }
+
+        $new_question = $this->save_question($question, $form, $course);
+
+        $dataset_form = new stdClass();
+        $dataset_form->nextpageparam["forceregeneration"]= 1;
+        $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
+        $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
+        $dataset_form->calclength = array(1 => 1, 2 => 1);
+        $dataset_form->number = array(1 => 5.4 , 2 => 4.9);
+        $dataset_form->itemid = array(1 => '' , 2 => '');
+        $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
+        $dataset_form->definition = array(1 => "1-0-a",
+            2 => "1-0-b");
+        $dataset_form->nextpageparam = array('forceregeneration' => false);
+        $dataset_form->addbutton = 1;
+        $dataset_form->selectadd = 1;
+        $dataset_form->courseid = $courseid;
+        $dataset_form->cmid = 0;
+        $dataset_form->id = $new_question->id;
+        $this->save_dataset_items($new_question, $dataset_form);
+
+        return $new_question;
+    }
+
+    /**
+     * When move the category of questions, the belonging files should be moved as well
+     * @param object $question, question information
+     * @param object $newcategory, target category information
+     */
+    function move_files($question, $newcategory) {
+        global $DB;
+        parent::move_files($question, $newcategory);
+
+        $fs = get_file_storage();
+        // process files in answer
+        if (!$oldanswers = $DB->get_records('question_answers', array('question' =>  $question->id), 'id ASC')) {
+            $oldanswers = array();
+        }
+        $component = 'question';
+        $filearea = 'answerfeedback';
+        foreach ($oldanswers as $answer) {
+            $files = $fs->get_area_files($question->contextid, $component, $filearea, $answer->id);
+            foreach ($files as $storedfile) {
+                if (!$storedfile->is_directory()) {
+                    $newfile = new object();
+                    $newfile->contextid = (int)$newcategory->contextid;
+                    $fs->create_file_from_storedfile($newfile, $storedfile);
+                    $storedfile->delete();
+                }
+            }
+        }
+        $component = 'qtype_numerical';
+        $filearea = 'instruction';
+        $files = $fs->get_area_files($question->contextid, $component, $filearea, $question->id);
+        foreach ($files as $storedfile) {
+            if (!$storedfile->is_directory()) {
+                $newfile = new object();
+                $newfile->contextid = (int)$newcategory->contextid;
+                $fs->create_file_from_storedfile($newfile, $storedfile);
+                $storedfile->delete();
+            }
+        }
+    }
+
+    function check_file_access($question, $state, $options, $contextid, $component,
+            $filearea, $args) {
+        $itemid = reset($args);
+        if ($component == 'question' && $filearea == 'answerfeedback') {
+
+            // check if answer id exists
+            $result = $options->feedback && array_key_exists($itemid, $question->options->answers);
+            if (!$result) {
+                return false;
+            }
+            // check response
+            if (!$this->check_response($question, $state)) {
+                return false;
+            }
+            return true;
+        } else if ($filearea == 'instruction') {
+            // TODO: should it be display all the time like questiontext?
+            // check if question id exists
+            if ($itemid != $question->id) {
+                return false;
+            } else {
+                return true;
+            }
+        } else if (in_array($filearea, array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'))) {
+            // TODO: calculated type doesn't display question feedback yet
+            return false;
+        } else {
+            return parent::check_file_access($question, $state, $options, $contextid, $component,
+                    $filearea, $args);
+        }
+    }
 }
 //// END OF CLASS ////
 
@@ -2267,11 +2325,11 @@ if ( ! defined ("CALCULATEDQUESTIONMAXITEMNUMBER")) {
 }
 
 function qtype_calculated_calculate_answer($formula, $individualdata,
-        $tolerance, $tolerancetype, $answerlength, $answerformat='1', $unit='') {
-/// The return value has these properties:
-/// ->answer    the correct answer
-/// ->min       the lower bound for an acceptable response
-/// ->max       the upper bound for an accetpable response
+    $tolerance, $tolerancetype, $answerlength, $answerformat='1', $unit='') {
+    /// The return value has these properties:
+    /// ->answer    the correct answer
+    /// ->min       the lower bound for an acceptable response
+    /// ->max       the upper bound for an accetpable response
 
     /// Exchange formula variables with the correct values...
     global $QTYPES;
@@ -2290,12 +2348,12 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
 
             if (preg_match('~^(.*\\.)(.*)$~', $calculated->answer, $regs)) {
                 $calculated->answer = $regs[1] . substr(
-                        $regs[2] . '00000000000000000000000000000000000000000x',
-                        0, $answerlength)
-                        . $unit;
+                    $regs[2] . '00000000000000000000000000000000000000000x',
+                    0, $answerlength)
+                    . $unit;
             } else {
                 $calculated->answer .=
-                        substr('.00000000000000000000000000000000000000000x',
+                    substr('.00000000000000000000000000000000000000000x',
                         0, $answerlength + 1) . $unit;
             }
         } else {
@@ -2337,9 +2395,9 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
             } else {
                 // Attach additional zeros at the end of $answer,
                 $answer .= (1==strlen($answer) ? '.' : '')
-                        . '00000000000000000000000000000000000000000x';
+                    . '00000000000000000000000000000000000000000x';
                 $calculated->answer = $sign
-                        .substr($answer, 0, $answerlength +1).$eX.$unit;
+                    .substr($answer, 0, $answerlength +1).$eX.$unit;
             }
         } else {
             // Stick to plain numeric format
@@ -2349,7 +2407,7 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
             } else {
                 // Could be an idea to add some zeros here
                 $answer .= (preg_match('~^[0-9]*$~', $answer) ? '.' : '')
-                        . '00000000000000000000000000000000000000000x';
+                    . '00000000000000000000000000000000000000000x';
                 $oklen = $answerlength + ($p10 < 1 ? 2-$p10 : 1);
                 $calculated->answer = $sign.substr($answer, 0, $oklen).$unit;
             }
@@ -2365,9 +2423,9 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
 
 
 function qtype_calculated_find_formula_errors($formula) {
-/// Validates the formula submitted from the question edit page.
-/// Returns false if everything is alright.
-/// Otherwise it constructs an error message
+    /// Validates the formula submitted from the question edit page.
+    /// Returns false if everything is alright.
+    /// Otherwise it constructs an error message
     // Strip away dataset names
     while (preg_match('~\\{[[:alpha:]][^>} <{"\']*\\}~', $formula, $regs)) {
         $formula = str_replace($regs[0], '1', $formula);
@@ -2380,59 +2438,58 @@ function qtype_calculated_find_formula_errors($formula) {
     $operatorornumber = "[$safeoperatorchar.0-9eE]";
 
     while ( preg_match("~(^|[$safeoperatorchar,(])([a-z0-9_]*)\\(($operatorornumber+(,$operatorornumber+((,$operatorornumber+)+)?)?)?\\)~",
-            $formula, $regs)) {
-
+        $formula, $regs)) {
         switch ($regs[2]) {
             // Simple parenthesis
-            case '':
-                if ($regs[4] || strlen($regs[3])==0) {
-                    return get_string('illegalformulasyntax', 'quiz', $regs[0]);
-                }
-                break;
+        case '':
+            if ($regs[4] || strlen($regs[3])==0) {
+                return get_string('illegalformulasyntax', 'quiz', $regs[0]);
+            }
+            break;
 
             // Zero argument functions
-            case 'pi':
-                if ($regs[3]) {
-                    return get_string('functiontakesnoargs', 'quiz', $regs[2]);
-                }
-                break;
+        case 'pi':
+            if ($regs[3]) {
+                return get_string('functiontakesnoargs', 'quiz', $regs[2]);
+            }
+            break;
 
             // Single argument functions (the most common case)
-            case 'abs': case 'acos': case 'acosh': case 'asin': case 'asinh':
-            case 'atan': case 'atanh': case 'bindec': case 'ceil': case 'cos':
-            case 'cosh': case 'decbin': case 'decoct': case 'deg2rad':
-            case 'exp': case 'expm1': case 'floor': case 'is_finite':
-            case 'is_infinite': case 'is_nan': case 'log10': case 'log1p':
-            case 'octdec': case 'rad2deg': case 'sin': case 'sinh': case 'sqrt':
-            case 'tan': case 'tanh':
-                if (!empty($regs[4]) || empty($regs[3])) {
-                    return get_string('functiontakesonearg','quiz',$regs[2]);
-                }
-                break;
+        case 'abs': case 'acos': case 'acosh': case 'asin': case 'asinh':
+        case 'atan': case 'atanh': case 'bindec': case 'ceil': case 'cos':
+        case 'cosh': case 'decbin': case 'decoct': case 'deg2rad':
+        case 'exp': case 'expm1': case 'floor': case 'is_finite':
+        case 'is_infinite': case 'is_nan': case 'log10': case 'log1p':
+        case 'octdec': case 'rad2deg': case 'sin': case 'sinh': case 'sqrt':
+        case 'tan': case 'tanh':
+            if (!empty($regs[4]) || empty($regs[3])) {
+                return get_string('functiontakesonearg','quiz',$regs[2]);
+            }
+            break;
 
             // Functions that take one or two arguments
-            case 'log': case 'round':
-                if (!empty($regs[5]) || empty($regs[3])) {
-                    return get_string('functiontakesoneortwoargs','quiz',$regs[2]);
-                }
-                break;
+        case 'log': case 'round':
+            if (!empty($regs[5]) || empty($regs[3])) {
+                return get_string('functiontakesoneortwoargs','quiz',$regs[2]);
+            }
+            break;
 
             // Functions that must have two arguments
-            case 'atan2': case 'fmod': case 'pow':
-                if (!empty($regs[5]) || empty($regs[4])) {
-                    return get_string('functiontakestwoargs', 'quiz', $regs[2]);
-                }
-                break;
+        case 'atan2': case 'fmod': case 'pow':
+            if (!empty($regs[5]) || empty($regs[4])) {
+                return get_string('functiontakestwoargs', 'quiz', $regs[2]);
+            }
+            break;
 
             // Functions that take two or more arguments
-            case 'min': case 'max':
-                if (empty($regs[4])) {
-                    return get_string('functiontakesatleasttwo','quiz',$regs[2]);
-                }
-                break;
+        case 'min': case 'max':
+            if (empty($regs[4])) {
+                return get_string('functiontakesatleasttwo','quiz',$regs[2]);
+            }
+            break;
 
-            default:
-                return get_string('unsupportedformulafunction','quiz',$regs[2]);
+        default:
+            return get_string('unsupportedformulafunction','quiz',$regs[2]);
         }
 
         // Exchange the function call with '1' and then chack for
@@ -2452,13 +2509,4 @@ function qtype_calculated_find_formula_errors($formula) {
         // Formula just might be valid
         return false;
     }
-
 }
-
-function dump($obj) {
-    echo "<pre>\n";
-    var_dump($obj);
-    echo "</pre><br />\n";
-}
-
-
index aa56eb9..6b690e7 100644 (file)
@@ -1,5 +1,5 @@
 <?PHP
 
-$plugin->version  = 2010020800;
+$plugin->version  = 2010020801;
 $plugin->requires = 2007101000;
 
index 3b81121..a2e736f 100644 (file)
@@ -18,11 +18,11 @@ class question_edit_calculatedmulti_form extends question_edit_form {
      *
      * @var question_calculatedmulti_qtype
      */
-    var $qtypeobj;
+    public $qtypeobj;
     public $questiondisplay ;
     public $initialname = '';
     public $reload = false ;
-    function question_edit_calculatedmulti_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
+    function question_edit_calculatedmulti_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true) {
         global $QTYPES, $SESSION, $CFG, $DB;
         $this->question = $question;
         $this->qtypeobj =& $QTYPES[$this->question->qtype];
@@ -38,22 +38,21 @@ class question_edit_calculatedmulti_form extends question_edit_form {
                 $regs= array();
                 if(preg_match('~#\{([^[:space:]]*)#~',$question->name , $regs)){
                     $question->name = str_replace($regs[0], '', $question->name);
-                };                             
+                };
             }
         }else {
-        }    
+        }
         parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
     }
 
     function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
-   //     $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
-           $repeated = array();
+        //     $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
+        $repeated = array();
         $repeated[] =& $mform->createElement('header', 'answerhdr', $label);
-     //   if ($this->editasmultichoice == 1){
+        //   if ($this->editasmultichoice == 1){
         $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
         $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
-        $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
-                                array('course' => $this->coursefilesid));
+        $repeated[] =& $mform->createElement('editor', 'feedback', get_string('feedback', 'quiz'), null, $this->editoroptions);
         $repeatedoptions['answer']['type'] = PARAM_RAW;
         $repeatedoptions['fraction']['default'] = 0;
         $answersoption = 'answers';
@@ -61,8 +60,8 @@ class question_edit_calculatedmulti_form extends question_edit_form {
         $mform->setType('answer', PARAM_NOTAGS);
 
         $addrepeated = array();
-            $addrepeated[] =& $mform->createElement('hidden', 'tolerance');
-            $addrepeated[] =& $mform->createElement('hidden', 'tolerancetype',1);
+        $addrepeated[] =& $mform->createElement('hidden', 'tolerance');
+        $addrepeated[] =& $mform->createElement('hidden', 'tolerancetype',1);
         $repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
         $repeatedoptions['tolerance']['default'] = 0.01;
 
@@ -72,7 +71,7 @@ class question_edit_calculatedmulti_form extends question_edit_form {
         $answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz'));
         $addrepeated[] =&  $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
         array_splice($repeated, 3, 0, $addrepeated);
-             $repeated[1]->setLabel('...<strong>{={x}+..}</strong>...');       
+        $repeated[1]->setLabel('...<strong>{={x}+..}</strong>...');
 
         return $repeated;
     }
@@ -85,79 +84,78 @@ class question_edit_calculatedmulti_form extends question_edit_form {
     function definition_inner(&$mform) {
         global $QTYPES;
         $this->qtypeobj =& $QTYPES[$this->qtype()];
-      // echo code left for testing period 
-       // echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
+        // echo code left for testing period
+        // echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
         $label = get_string("sharedwildcards", "qtype_calculated");
         $mform->addElement('hidden', 'initialcategory', 1);
         $mform->addElement('hidden', 'reload', 1);
         $mform->setType('initialcategory', PARAM_INT);
 
-   //     $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question);
-   $html2 ="";
+        //     $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question);
+        $html2 ="";
         $mform->insertElementBefore($mform->createElement('static','listcategory',$label,$html2),'name');
         if(isset($this->question->id )){
             $mform->insertElementBefore($mform->createElement('static','initialname',get_string('questionstoredname','qtype_calculated'),$this->initialname),'name');
         };
         $addfieldsname='updatecategory';
         $addstring=get_string("updatecategory", "qtype_calculated");
-                $mform->registerNoSubmitButton($addfieldsname);
+        $mform->registerNoSubmitButton($addfieldsname);
         $this->editasmultichoice =  1 ;
-            
+
 
         $mform->insertElementBefore(    $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
         $mform->registerNoSubmitButton('createoptionbutton');
-            $mform->addElement('hidden', 'multichoice',$this->editasmultichoice);
-            $mform->setType('multichoice', PARAM_INT);
-                                            
+        $mform->addElement('hidden', 'multichoice',$this->editasmultichoice);
+        $mform->setType('multichoice', PARAM_INT);
 
-//            $mform->addElement('header', 'choicehdr',get_string('multichoicecalculatedquestion', 'qtype_calculated'));
-            $menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice'));
-            $mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu);
-            $mform->setDefault('single', 1);
-    
-            $mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0,1));
-            $mform->addHelpButton('shuffleanswers', 'shuffleanswers', 'qtype_multichoice');
-            $mform->setDefault('shuffleanswers', 1);
-    
-            $numberingoptions = $QTYPES['multichoice']->get_numbering_styles();
-            $menu = array();
-            foreach ($numberingoptions as $numberingoption) {
-                $menu[$numberingoption] = get_string('answernumbering' . $numberingoption, 'qtype_multichoice');
-            }
-            $mform->addElement('select', 'answernumbering', get_string('answernumbering', 'qtype_multichoice'), $menu);
-            $mform->setDefault('answernumbering', 'abc');
+
+        //            $mform->addElement('header', 'choicehdr',get_string('multichoicecalculatedquestion', 'qtype_calculated'));
+        $menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice'));
+        $mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu);
+        $mform->setDefault('single', 1);
+
+        $mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0,1));
+        $mform->setHelpButton('shuffleanswers', array('multichoiceshuffle', get_string('shuffleanswers','qtype_multichoice'), 'qtype_multichoice'));
+        $mform->setDefault('shuffleanswers', 1);
+
+        $numberingoptions = $QTYPES['multichoice']->get_numbering_styles();
+        $menu = array();
+        foreach ($numberingoptions as $numberingoption) {
+            $menu[$numberingoption] = get_string('answernumbering' . $numberingoption, 'qtype_multichoice');
+        }
+        $mform->addElement('select', 'answernumbering', get_string('answernumbering', 'qtype_multichoice'), $menu);
+        $mform->setDefault('answernumbering', 'abc');
 
         $creategrades = get_grade_options();
-            $this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'),
-                $creategrades->gradeoptionsfull, max(5, QUESTION_NUMANS_START));
-            
+        $this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'),
+            $creategrades->gradeoptionsfull, max(5, QUESTION_NUMANS_START));
+
 
         $repeated = array();
-     //   if ($this->editasmultichoice == 1){
-            $nounits = optional_param('nounits', 1, PARAM_INT);
-            $mform->addElement('hidden', 'nounits', $nounits);
-            $mform->setType('nounits', PARAM_INT);
-            $mform->setConstants(array('nounits'=>$nounits));
-            for ($i=0; $i< $nounits; $i++) {
-                $mform->addElement('hidden','unit'."[$i]", optional_param('unit'."[$i]", '', PARAM_NOTAGS));
-                $mform->setType('unit'."[$i]", PARAM_NOTAGS); 
-                $mform->addElement('hidden', 'multiplier'."[$i]", optional_param('multiplier'."[$i]", '', PARAM_NUMBER));
-                $mform->setType('multiplier'."[$i]", PARAM_NUMBER);
-            }  
-          $mform->addElement('hidden','unitgradingtype',optional_param('unitgradingtype', '', PARAM_INT)) ;
-          $mform->addElement('hidden','unitpenalty',optional_param('unitpenalty', '', PARAM_NUMBER)) ;
-          $mform->addElement('hidden','showunits',optional_param('showunits', '', PARAM_INT)) ;
-          $mform->addElement('hidden','unitsleft',optional_param('unitsleft', '', PARAM_INT)) ; 
-          $mform->addElement('hidden','instructions',optional_param('instructions', '', PARAM_RAW)) ;  
+        //   if ($this->editasmultichoice == 1){
+        $nounits = optional_param('nounits', 1, PARAM_INT);
+        $mform->addElement('hidden', 'nounits', $nounits);
+        $mform->setType('nounits', PARAM_INT);
+        $mform->setConstants(array('nounits'=>$nounits));
+        for ($i=0; $i< $nounits; $i++) {
+            $mform->addElement('hidden','unit'."[$i]", optional_param('unit'."[$i]", '', PARAM_NOTAGS));
+            $mform->setType('unit'."[$i]", PARAM_NOTAGS);
+            $mform->addElement('hidden', 'multiplier'."[$i]", optional_param('multiplier'."[$i]", '', PARAM_NUMBER));
+            $mform->setType('multiplier'."[$i]", PARAM_NUMBER);
+        }
+        $mform->addElement('hidden','unitgradingtype',optional_param('unitgradingtype', '', PARAM_INT)) ;
+        $mform->addElement('hidden','unitpenalty',optional_param('unitpenalty', '', PARAM_NUMBER)) ;
+        $mform->addElement('hidden','showunits',optional_param('showunits', '', PARAM_INT)) ;
+        $mform->addElement('hidden','unitsleft',optional_param('unitsleft', '', PARAM_INT)) ;
+        $mform->addElement('hidden','instructions',optional_param('instructions', '', PARAM_RAW)) ;
 
-            $mform->setType('addunits','hidden');
-            $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
+        $mform->setType('addunits','hidden');
+        $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
 
-            foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
-                $mform->addElement('htmleditor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'),
-                                    array('course' => $this->coursefilesid));
-                $mform->setType($feedbackname, PARAM_RAW);
-            }
+        foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
+            $mform->addElement('editor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'), null, $this->editoroptions);
+            $mform->setType($feedbackname, PARAM_RAW);
+        }
         //hidden elements
         $mform->addElement('hidden', 'synchronize', '');
         $mform->setType('synchronize', PARAM_INT);
@@ -168,40 +166,43 @@ class question_edit_calculatedmulti_form extends question_edit_form {
         }
         $mform->addElement('hidden', 'wizard', 'datasetdefinitions');
         $mform->setType('wizard', PARAM_ALPHA);
-
-
     }
 
-    function set_data($question) {
-             $default_values['multichoice']= $this->editasmultichoice ; //$this->editasmultichoice ;
+    function data_preprocessing($question) {
+        $default_values['multichoice']= $this->editasmultichoice ; //$this->editasmultichoice ;
         if (isset($question->options)){
             $answers = $question->options->answers;
             if (count($answers)) {
                 $key = 0;
                 foreach ($answers as $answer){
+                    $draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
                     $default_values['answer['.$key.']'] = $answer->answer;
                     $default_values['fraction['.$key.']'] = $answer->fraction;
                     $default_values['tolerance['.$key.']'] = $answer->tolerance;
                     $default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
                     $default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
                     $default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
-                    $default_values['feedback['.$key.']'] = $answer->feedback;
+                    $default_values['feedback['.$key.']'] = array();
+                    // prepare draftarea
+                    $default_values['feedback['.$key.']']['text'] = file_prepare_draft_area($draftid, $this->context->id, 'question', 'answerfeedback', empty($answer->id)?null:(int)$answer->id, null, $answer->feedback);
+                    $default_values['feedback['.$key.']']['format'] = $answer->feedbackformat;
+                    $default_values['feedback['.$key.']']['itemid'] = $draftid;
                     $key++;
                 }
             }
-        //  $default_values['unitgradingtype'] = $question->options->unitgradingtype ;
-        //  $default_values['unitpenalty'] = $question->options->unitpenalty ;
-        //  $default_values['showunits'] = $question->options->showunits ;
-        //  $default_values['unitsleft'] = $question->options->unitsleft ;
-        //  $default_values['instructions'] = $question->options->instructions  ;
-          $default_values['synchronize'] = $question->options->synchronize ;
+            //  $default_values['unitgradingtype'] = $question->options->unitgradingtype ;
+            //  $default_values['unitpenalty'] = $question->options->unitpenalty ;
+            //  $default_values['showunits'] = $question->options->showunits ;
+            //  $default_values['unitsleft'] = $question->options->unitsleft ;
+            //  $default_values['instructions'] = $question->options->instructions  ;
+            $default_values['synchronize'] = $question->options->synchronize ;
 
             if (isset($question->options->units)){
                 $units  = array_values($question->options->units);
                 // make sure the default unit is at index 0
                 usort($units, create_function('$a, $b',
-                'if (1.0 === (float)$a->multiplier) { return -1; } else '.
-                'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
+                    'if (1.0 === (float)$a->multiplier) { return -1; } else '.
+                    'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
                 if (count($units)) {
                     $key = 0;
                     foreach ($units as $unit){
@@ -213,30 +214,51 @@ class question_edit_calculatedmulti_form extends question_edit_form {
             }
         }
         if (isset($question->options->single)){
-        $default_values['single'] =  $question->options->single;
-        $default_values['answernumbering'] =  $question->options->answernumbering;
-        $default_values['shuffleanswers'] =  $question->options->shuffleanswers;
-        $default_values['correctfeedback'] =  $question->options->correctfeedback;
-        $default_values['partiallycorrectfeedback'] =  $question->options->partiallycorrectfeedback;
-        $default_values['incorrectfeedback'] =  $question->options->incorrectfeedback;
-    }
+            $default_values['single'] =  $question->options->single;
+            $default_values['answernumbering'] =  $question->options->answernumbering;
+            $default_values['shuffleanswers'] =  $question->options->shuffleanswers;
+        }
         $default_values['submitbutton'] = get_string('nextpage', 'qtype_calculated');
         $default_values['makecopy'] = get_string('makecopynextpage', 'qtype_calculated');
-        /* set the wild cards category display given that on loading the category element is
-        unselected when processing this function but have a valid value when processing the
-        update category button. The value can be obtain by
-         $qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
-         but is coded using existing functions
-        */
-         $qu = new stdClass;
-         $el = new stdClass;
-         /* no need to call elementExists() here */
-         if ($this->_form->elementExists('category')){
+
+        // prepare draft files
+        foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
+            if (!isset($question->options->$feedbackname)) {
+                continue;
+            }
+            $text = $question->options->$feedbackname;
+            $draftid = file_get_submitted_draft_itemid($feedbackname);
+            $feedbackformat = $feedbackname . 'format';
+            $format = $question->options->$feedbackformat;
+            $default_values[$feedbackname] = array();
+            $default_values[$feedbackname]['text'] = file_prepare_draft_area(
+                $draftid,                // draftid
+                $this->context->id,      // context
+                'qtype_calculatedmulti', // component
+                $feedbackname,           // filarea
+                !empty($question->id)?(int)$question->id:null, // itemid
+                $this->fileoptions,      // options
+                $text                    // text
+            );
+            $default_values[$feedbackname]['format'] = $format;
+            $default_values[$feedbackname]['itemid'] = $draftid;
+        }
+        /**
+         * set the wild cards category display given that on loading the category element is
+         * unselected when processing this function but have a valid value when processing the
+         * update category button. The value can be obtain by
+         * $qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
+         * but is coded using existing functions
+         */
+        $qu = new stdClass;
+        $el = new stdClass;
+        /* no need to call elementExists() here */
+        if ($this->_form->elementExists('category')){
             $el=$this->_form->getElement('category');
-         } else {
+        } else {
             $el=$this->_form->getElement('categorymoveto');
-         }
-         if($value =$el->getSelected()) {
+        }
+        if($value =$el->getSelected()) {
             $qu->category =$value[0];
         }else {
             $qu->category=$question->category;// on load  $question->category is set by question.php
@@ -244,8 +266,7 @@ class question_edit_calculatedmulti_form extends question_edit_form {
         $html2 = $this->qtypeobj->print_dataset_definitions_category($qu);
         $this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ;
         $question = (object)((array)$question + $default_values);
-
-        parent::set_data($question);
+        return $question;
     }
 
     function qtype() {
@@ -253,21 +274,20 @@ class question_edit_calculatedmulti_form extends question_edit_form {
     }
 
     function validation($data, $files) {
-              // echo code left for testing period 
-
-              //  echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
-              //  echo "<p>data <pre>";print_r($data);echo "</pre></p>";
+        // echo code left for testing period
+        // echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
+        // echo "<p>data <pre>";print_r($data);echo "</pre></p>";
 
         $errors = parent::validation($data, $files);
         //verifying for errors in {=...} in question text;
         $qtext = "";
-        $qtextremaining = $data['questiontext'] ;
-        $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
-            foreach ($possibledatasets as $name => $value) {
+        $qtextremaining = $data['questiontext']['text'];
+        $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
+        foreach ($possibledatasets as $name => $value) {
             $qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
         }
-    //     echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
-        while  (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
+        // echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
+        while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
             $qtextsplits = explode($regs1[0], $qtextremaining, 2);
             $qtext =$qtext.$qtextsplits[0];
             $qtextremaining = $qtextsplits[1];
@@ -282,13 +302,13 @@ class question_edit_calculatedmulti_form extends question_edit_form {
         $answers = $data['answer'];
         $answercount = 0;
         $maxgrade = false;
-        $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
+        $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
         $mandatorydatasets = array();
         foreach ($answers as $key => $answer){
             $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
         }
         if ( count($mandatorydatasets )==0){
-          //  $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
+            //  $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
             foreach ($answers as $key => $answer){
                 $errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
             }
@@ -296,15 +316,15 @@ class question_edit_calculatedmulti_form extends question_edit_form {
         if ($data['multichoice']== 1 ){
             foreach ($answers as $key => $answer){
                 $trimmedanswer = trim($answer);
-                if (($trimmedanswer!='')||$answercount==0){    
+                if (($trimmedanswer!='')||$answercount==0){
                     //verifying for errors in {=...} in answer text;
                     $qanswer = "";
                     $qanswerremaining =  $trimmedanswer ;
                     $possibledatasets = $this->qtypeobj->find_dataset_names($trimmedanswer);
-                        foreach ($possibledatasets as $name => $value) {
+                    foreach ($possibledatasets as $name => $value) {
                         $qanswerremaining = str_replace('{'.$name.'}', '1', $qanswerremaining);
                     }
-                //     echo "numericalquestion qanswerremaining <pre>";print_r($possibledatasets);
+                    //     echo "numericalquestion qanswerremaining <pre>";print_r($possibledatasets);
                     while  (preg_match('~\{=([^[:space:]}]*)}~', $qanswerremaining, $regs1)) {
                         $qanswersplits = explode($regs1[0], $qanswerremaining, 2);
                         $qanswer =$qanswer.$qanswersplits[0];
@@ -320,16 +340,16 @@ class question_edit_calculatedmulti_form extends question_edit_form {
                 }
                 if ($trimmedanswer!=''){
                     if ('2' == $data['correctanswerformat'][$key]
-                            && '0' == $data['correctanswerlength'][$key]) {
-                        $errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
-                    }
+                        && '0' == $data['correctanswerlength'][$key]) {
+                            $errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
+                        }
                     if (!is_numeric($data['tolerance'][$key])){
                         $errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
                     }
                     if ($data['fraction'][$key] == 1) {
-                       $maxgrade = true;
+                        $maxgrade = true;
                     }
-    
+
                     $answercount++;
                 }
                 //check grades
@@ -342,14 +362,14 @@ class question_edit_calculatedmulti_form extends question_edit_form {
                     if ($data['fraction'][$key] > $maxfraction) {
                         $maxfraction = $data['fraction'][$key];
                     }
-                }        
+                }
             }
             if ($answercount==0){
                 $errors['answer[0]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
                 $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
             } elseif ($answercount==1){
                 $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
-    
+
             }
 
             /// Perform sanity checks on fractional grades
@@ -365,17 +385,15 @@ class question_edit_calculatedmulti_form extends question_edit_form {
                     $errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
                 }
             }
-            
-        
+
             if ($answercount==0){
                 $errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
             }
             if ($maxgrade == false) {
                 $errors['fraction[0]'] = get_string('fractionsnomax', 'question');
             }
-        
+
         }
         return $errors;
     }
 }
-
diff --git a/question/type/calculatedmulti/lib.php b/question/type/calculatedmulti/lib.php
new file mode 100644 (file)
index 0000000..220f828
--- /dev/null
@@ -0,0 +1,31 @@
+<?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/>.
+
+/**
+ * Serve question type files
+ *
+ * @since 2.0
+ * @package questionbank
+ * @subpackage questiontypes
+ * @author Dongsheng Cai <dongsheng@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+function qtype_calculatedmulti_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
+    global $DB, $CFG;
+    require_once($CFG->libdir . '/questionlib.php');
+    question_pluginfile($course, $context, 'qtype_calculatedmulti', $filearea, $args, $forcedownload);
+}
index 0d87898..c648899 100644 (file)
@@ -1,17 +1,30 @@
 <?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/>.
+
 /////////////////
 // CALCULATED ///
 /////////////////
 
 /// QUESTION TYPE CLASS //////////////////
 
-
-
 class question_calculatedmulti_qtype extends question_calculated_qtype {
 
     // Used by the function custom_generator_tools:
-    var $calcgenerateidhasbeenadded = false;
+    public $calcgenerateidhasbeenadded = false;
     public $virtualqtype = false;
 
     function name() {
@@ -28,14 +41,13 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
 
 
     function save_question_options($question) {
-        //$options = $question->subtypeoptions;
-        // Get old answers:
         global $CFG, $DB, $QTYPES ;
+        $context = $question->context;
         if (isset($question->answer) && !isset($question->answers)) {
             $question->answers = $question->answer;
         }
         // calculated options
-        $update = true ; 
+        $update = true ;
         $options = $DB->get_record("question_calculated_options", array("question" => $question->id));
         if (!$options) {
             $update = false;
@@ -46,19 +58,20 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
         $options->single = $question->single;
         $options->answernumbering = $question->answernumbering;
         $options->shuffleanswers = $question->shuffleanswers;
-        $options->correctfeedback = trim($question->correctfeedback);
-        $options->partiallycorrectfeedback = trim($question->partiallycorrectfeedback);
-        $options->incorrectfeedback = trim($question->incorrectfeedback);
+
+        // save question feedback files
+        foreach (array('correct', 'partiallycorrect', 'incorrect') as $feedbacktype) {
+            $feedbackname = $feedbacktype . 'feedback';
+            $feedbackformat = $feedbackname . 'format';
+            $feedback = $question->$feedbackname;
+            $options->$feedbackformat = $feedback['format'];
+            $options->$feedbackname = file_save_draft_area_files($feedback['itemid'], $context->id, 'qtype_calculatedmulti', $feedbackname, $question->id, self::$fileoptions, trim($feedback['text']));
+        }
+
         if ($update) {
-            if (!$DB->update_record("question_calculated_options", $options)) {
-                $result->error = "Could not update calculated question options! (id=$options->id)";
-                return $result;
-            }
+            $DB->update_record("question_calculated_options", $options);
         } else {
-            if (!$DB->insert_record("question_calculated_options", $options)) {
-                $result->error = "Could not insert calculated question options!";
-                return $result;
-            }
+            $DB->insert_record("question_calculated_options", $options);
         }
 
         // Get old versions of the objects
@@ -71,8 +84,9 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
         }
 
         // Save the units.
-        $virtualqtype = $this->get_virtual_qtype( $question);
-       // $result = $virtualqtype->save_numerical_units($question);
+        $virtualqtype = $this->get_virtual_qtype($question);
+        // TODO: What is this?
+        // $result = $virtualqtype->save_numerical_units($question);
         if (isset($result->error)) {
             return $result;
         } else {
@@ -80,21 +94,25 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
         }
         // Insert all the new answers
         if (isset($question->answer) && !isset($question->answers)) {
-            $question->answers=$question->answer;
+            $question->answers = $question->answer;
         }
         foreach ($question->answers as $key => $dataanswer) {
-            if (  trim($dataanswer) != '' ) {
+            if ( trim($dataanswer) != '' ) {
                 $answer = new stdClass;
                 $answer->question = $question->id;
                 $answer->answer = trim($dataanswer);
                 $answer->fraction = $question->fraction[$key];
-                $answer->feedback = trim($question->feedback[$key]);
+                $answer->feedback = trim($question->feedback[$key]['text']);
+                $answer->feedbackformat = $question->feedback[$key]['format'];
 
                 if ($oldanswer = array_shift($oldanswers)) {  // Existing answer, so reuse it
                     $answer->id = $oldanswer->id;
+                    $answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $answer->feedback);
                     $DB->update_record("question_answers", $answer);
                 } else { // This is a completely new answer
                     $answer->id = $DB->insert_record("question_answers", $answer);
+                    $feedbacktext = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $answer->feedback);
+                    $DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$answer->id));
                 }
 
                 // Set up the options object
@@ -129,15 +147,15 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
                 $DB->delete_records('question_calculated', array('id' => $oo->id));
             }
         }
-      //  $result = $QTYPES['numerical']->save_numerical_options($question);
-      //  if (isset($result->error)) {
-      //      return $result;
-      //  }
+        //  $result = $QTYPES['numerical']->save_numerical_options($question);
+        //  if (isset($result->error)) {
+        //      return $result;
+        //  }
 
 
         if( isset($question->import_process)&&$question->import_process){
             $this->import_datasets($question);
-         }
+        }
         // Report any problems.
         if (!empty($result->notice)) {
             return $result;
@@ -148,30 +166,25 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
     function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
         // Find out how many datasets are available
         global $CFG, $DB, $QTYPES, $OUTPUT ;
-        if(!$maxnumber = (int)$DB->get_field_sql(
-                            "SELECT MIN(a.itemcount)
-                            FROM {question_dataset_definitions} a,
-                                 {question_datasets} b
-                            WHERE b.question = ?
-                            AND   a.id = b.datasetdefinition", array($question->id))) {
+        $maxnumber = (int)$DB->get_field_sql(
+            "SELECT MIN(a.itemcount)
+               FROM {question_dataset_definitions} a, {question_datasets} b
+              WHERE b.question = ? AND a.id = b.datasetdefinition", array($question->id));
+        if (!$maxnumber) {
             print_error('cannotgetdsforquestion', 'question', '', $question->id);
         }
-                    $sql = "SELECT i.*
-                    FROM {question_datasets} d,
-                         {question_dataset_definitions} i
-                    WHERE d.question = ?
-                    AND   d.datasetdefinition = i.id  
-                    AND   i.category != 0 
-                   ";
+        $sql = "SELECT i.*
+                  FROM {question_datasets} d, {question_dataset_definitions} i
+                 WHERE d.question = ? AND d.datasetdefinition = i.id AND i.category != 0";
         if (!$question->options->synchronize || !$records = $DB->get_records_sql($sql, array($question->id))) {
-            $synchronize_calculated  =  false ; 
-        }else {
-           // i.e records is true so test coherence
-           $coherence = true ;
-                $a = new stdClass ;
-                $a->qid = $question->id ;
-                $a->qcat = $question->category ;
-           foreach($records as $def ){
+            $synchronize_calculated  =  false ;
+        } else {
+            // i.e records is true so test coherence
+            $coherence = true ;
+            $a = new stdClass ;
+            $a->qid = $question->id ;
+            $a->qcat = $question->category ;
+            foreach($records as $def ){
                 if ($def->category != $question->category){
                     $a->name = $def->name;
                     $a->sharedcat = $def->category ;
@@ -180,11 +193,11 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
                 }
             }
             if(!$coherence){
-                         echo $OUTPUT->notification(get_string('nocoherencequestionsdatyasetcategory','qtype_calculated',$a));
-          } 
-            
-            $synchronize_calculated  = true ; 
-        }    
+                echo $OUTPUT->notification(get_string('nocoherencequestionsdatyasetcategory','qtype_calculated',$a));
+            }
+
+            $synchronize_calculated  = true ;
+        }
 
         // Choose a random dataset
         // maxnumber sould not be breater than 100
@@ -194,41 +207,40 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
         if ( $synchronize_calculated === false ) {
             $state->options->datasetitem = rand(1, $maxnumber);
         }else{
-            $state->options->datasetitem = intval( $maxnumber * substr($attempt->timestart,-2) /100 ) ;            
+            $state->options->datasetitem = intval( $maxnumber * substr($attempt->timestart,-2) /100 ) ;
             if ($state->options->datasetitem < 1) {
                 $state->options->datasetitem =1 ;
             } else if ($state->options->datasetitem > $maxnumber){
                 $state->options->datasetitem = $maxnumber ;
             }
-           
-        };  
+
+        };
         $state->options->dataset =
-         $this->pick_question_dataset($question,$state->options->datasetitem);
-                    // create an array of answerids ??? why so complicated ???
-            $answerids = array_values(array_map(create_function('$val',
-             'return $val->id;'), $question->options->answers));
-            // Shuffle the answers if required
-            if (!empty($cmoptions->shuffleanswers) and !empty($question->options->shuffleanswers)) {
-               $answerids = swapshuffle($answerids);
-            }
-            $state->options->order = $answerids;
-            // Create empty responses
-            if ($question->options->single) {
-                $state->responses = array('' => '');
-            } else {
-                $state->responses = array();
-            }
-            return true;
-        
+            $this->pick_question_dataset($question,$state->options->datasetitem);
+        // create an array of answerids ??? why so complicated ???
+        $answerids = array_values(array_map(create_function('$val',
+            'return $val->id;'), $question->options->answers));
+        // Shuffle the answers if required
+        if (!empty($cmoptions->shuffleanswers) and !empty($question->options->shuffleanswers)) {
+            $answerids = swapshuffle($answerids);
+        }
+        $state->options->order = $answerids;
+        // Create empty responses
+        if ($question->options->single) {
+            $state->responses = array('' => '');
+        } else {
+            $state->responses = array();
+        }
+        return true;
     }
-    
+
     function save_session_and_responses(&$question, &$state) {
         global $DB;
-        $responses = 'dataset'.$state->options->datasetitem.'-' ;       
+        $responses = 'dataset'.$state->options->datasetitem.'-' ;
         $responses .= implode(',', $state->options->order) . ':';
         $responses .= implode(',', $state->responses);
-         
-        // Set the legacy answer field        
+
+        // Set the legacy answer field
         if (!$DB->set_field('question_states', 'answer', $responses, array('id'=> $state->id))) {
             return false;
         }
@@ -241,7 +253,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
         foreach ($form->answers as $key => $answer) {
             $a->answer              = trim($form->answer[$key]);
             $a->fraction              = $form->fraction[$key];//new
-           $a->tolerance           = $form->tolerance[$key];
+            $a->tolerance           = $form->tolerance[$key];
             $a->tolerancetype       = $form->tolerancetype[$key];
             $a->correctanswerlength = $form->correctanswerlength[$key];
             $a->correctanswerformat = $form->correctanswerformat[$key];
@@ -251,57 +263,53 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
         return $question;
     }
 
-
-
-
-
     function convert_answers (&$question, &$state){
-            foreach ($question->options->answers as $key => $answer) {
-                $answer->answer = $this->substitute_variables($answer->answer, $state->options->dataset);
-                //evaluate the equations i.e {=5+4)
-                $qtext = "";
-                $qtextremaining = $answer->answer ;
-             //   while  (preg_match('~\{(=)|%[[:digit]]\.=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
-                while  (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
-
-                    $qtextsplits = explode($regs1[0], $qtextremaining, 2);
-                    $qtext =$qtext.$qtextsplits[0];
-                    $qtextremaining = $qtextsplits[1];
-                    if (empty($regs1[1])) {
-                            $str = '';
-                        } else {
-                            if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
-                                $str=$formulaerrors ;
-                            }else {
-                                eval('$str = '.$regs1[1].';');
-                       $texteval= qtype_calculated_calculate_answer(
-                     $str, $state->options->dataset, $answer->tolerance,
-                     $answer->tolerancetype, $answer->correctanswerlength,
-                        $answer->correctanswerformat, '');
+        foreach ($question->options->answers as $key => $answer) {
+            $answer->answer = $this->substitute_variables($answer->answer, $state->options->dataset);
+            //evaluate the equations i.e {=5+4)
+            $qtext = "";
+            $qtextremaining = $answer->answer ;
+            //   while  (preg_match('~\{(=)|%[[:digit]]\.=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
+            while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
+
+                $qtextsplits = explode($regs1[0], $qtextremaining, 2);
+                $qtext = $qtext.$qtextsplits[0];
+                $qtextremaining = $qtextsplits[1];
+                if (empty($regs1[1])) {
+                    $str = '';
+                } else {
+                    if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
+                        $str=$formulaerrors ;
+                    }else {
+                        eval('$str = '.$regs1[1].';');
+                        $texteval= qtype_calculated_calculate_answer(
+                            $str, $state->options->dataset, $answer->tolerance,
+                            $answer->tolerancetype, $answer->correctanswerlength,
+                            $answer->correctanswerformat, '');
                         $str = $texteval->answer;
-                            }
-                        }
-                        $qtext = $qtext.$str ;
+                    }
                 }
-                $answer->answer = $qtext.$qtextremaining ; ;
+                $qtext = $qtext.$str ;
             }
+            $answer->answer = $qtext.$qtextremaining ; ;
         }
+    }
 
-    function get_default_numerical_unit($question,$virtualqtype){
-                $unit = '';
-            return $unit ;        
-    }    
+    function get_default_numerical_unit($question, $virtualqtype){
+        $unit = '';
+        return $unit ;
+    }
     function grade_responses(&$question, &$state, $cmoptions) {
         // Forward the grading to the virtual qtype
         // We modify the question to look like a multichoice question
-        // for grading nothing to do 
+        // for grading nothing to do
 /*        $numericalquestion = fullclone($question);
        foreach ($numericalquestion->options->answers as $key => $answer) {
             $answer = $numericalquestion->options->answers[$key]->answer; // for PHP 4.x
           $numericalquestion->options->answers[$key]->answer = $this->substitute_variables_and_eval($answer,
              $state->options->dataset);
-       }*/
-         $virtualqtype = $this->get_virtual_qtype( $question);
+}*/
+        $virtualqtype = $this->get_virtual_qtype( $question);
         return $virtualqtype->grade_responses($question, $state, $cmoptions) ;
     }
 
@@ -319,7 +327,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
         $this->convert_answers ($numericalquestion, $state);
         $this->convert_questiontext ($numericalquestion, $state);
      /*   $numericalquestion->questiontext = $this->substitute_variables_and_eval(
-                                  $numericalquestion->questiontext, $state->options->dataset);*/
+     $numericalquestion->questiontext, $state->options->dataset);*/
         $responses = $virtualqtype->get_all_responses($numericalquestion, $state);
         $response = reset($responses->responses);
         $correct = $response->answer.' : ';
@@ -335,8 +343,8 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
 
     function create_virtual_qtype() {
         global $CFG;
-            require_once("$CFG->dirroot/question/type/multichoice/questiontype.php");
-            return new question_multichoice_qtype();
+        require_once("$CFG->dirroot/question/type/multichoice/questiontype.php");
+        return new question_multichoice_qtype();
     }
 
 
@@ -353,7 +361,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
             } else {
                 $strheader .= $delimiter.$answer->answer;
             }
-                $delimiter = '<br/>';            
+            $delimiter = '<br/>';
         }
         return $strheader;
     }
@@ -369,7 +377,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
             $unit = $unit->unit;
         } else {
             $unit = '';
-        }*/
+    }*/
 
         $answers = fullclone($answers);
         $strmin = get_string('min', 'quiz');
@@ -377,29 +385,29 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
         $errors = '';
         $delimiter = ': ';
         foreach ($answers as $key => $answer) {
-                $answer->answer = $this->substitute_variables($answer->answer, $data);
-                //evaluate the equations i.e {=5+4)
-                $qtext = "";
-                $qtextremaining = $answer->answer ;
-                while  (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
-                    $qtextsplits = explode($regs1[0], $qtextremaining, 2);
-                    $qtext =$qtext.$qtextsplits[0];
-                    $qtextremaining = $qtextsplits[1];
-                    if (empty($regs1[1])) {
-                            $str = '';
-                        } else {
-                            if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
-                                $str=$formulaerrors ;
-                            }else {
-                                eval('$str = '.$regs1[1].';');
-                            }
-                        }
-                        $qtext = $qtext.$str ;
+            $answer->answer = $this->substitute_variables($answer->answer, $data);
+            //evaluate the equations i.e {=5+4)
+            $qtext = "";
+            $qtextremaining = $answer->answer ;
+            while  (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
+                $qtextsplits = explode($regs1[0], $qtextremaining, 2);
+                $qtext =$qtext.$qtextsplits[0];
+                $qtextremaining = $qtextsplits[1];
+                if (empty($regs1[1])) {
+                    $str = '';
+                } else {
+                    if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
+                        $str=$formulaerrors ;
+                    }else {
+                        eval('$str = '.$regs1[1].';');
+                    }
                 }
-                $answer->answer = $qtext.$qtextremaining ; ;
-                $comment->stranswers[$key]= $answer->answer ;
-            
-            
+                $qtext = $qtext.$str ;
+            }
+            $answer->answer = $qtext.$qtextremaining;
+            $comment->stranswers[$key] = $answer->answer;
+
+
           /*  $formula = $this->substitute_variables($answer->answer,$data);
             $formattedanswer = qtype_calculated_calculate_answer(
                     $answer->answer, $data, $answer->tolerance,
@@ -411,7 +419,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
                     }else {
                         eval('$answer->answer = '.$formula.';') ;
                         $virtualqtype->get_tolerance_interval($answer);
-                    } 
+                    }
             if ($answer->min === '') {
                 // This should mean that something is wrong
                 $comment->stranswers[$key] = " $formattedanswer->answer".'<br/><br/>';
@@ -432,15 +440,11 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
                     $comment->stranswers[$key] .=get_string('trueanswerinsidelimits','qtype_calculated',$correcttrue);//' True answer :'.$calculated->trueanswer.' inside limits';
                 }
                 $comment->stranswers[$key] .='';
-            }*/
+          }*/
         }
         return fullclone($comment);
     }
 
-
-
-
-
     function get_correct_responses1(&$question, &$state) {
         $virtualqtype = $this->get_virtual_qtype( $question);
     /*    if ($question->options->multichoice != 1 ) {
@@ -464,71 +468,148 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
                     return $correct;
                 }
             }
-        }else{**/
-            return $virtualqtype->get_correct_responses($question, $state) ;
-       // }
+    }else{**/
+        return $virtualqtype->get_correct_responses($question, $state) ;
+        // }
         return null;
     }
 
     function get_virtual_qtype() {
         global $QTYPES;
-    //    if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){
-            $this->virtualqtype =& $QTYPES['multichoice'];
-     //   }else {
-     //       $this->virtualqtype =& $QTYPES['numerical'];
-     //   }
+        //    if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){
+        $this->virtualqtype =& $QTYPES['multichoice'];
+        //   }else {
+        //       $this->virtualqtype =& $QTYPES['numerical'];
+        //   }
         return $this->virtualqtype;
     }
 
 
-       /**
-   * Runs all the code required to set up and save an essay question for testing purposes.
-   * Alternate DB table prefix may be used to facilitate data deletion.
-   */
-  function generate_test($name, $courseid = null) {
-      global $DB;
-      list($form, $question) = parent::generate_test($name, $courseid);
-      $form->feedback = 1;
-      $form->multiplier = array(1, 1);
-      $form->shuffleanswers = 1;
-      $form->noanswers = 1;
-      $form->qtype ='calculatedmulti';
-      $question->qtype ='calculatedmulti';
-      $form->answers = array('{a} + {b}');
-      $form->fraction = array(1);
-      $form->tolerance = array(0.01);
-      $form->tolerancetype = array(1);
-      $form->correctanswerlength = array(2);
-      $form->correctanswerformat = array(1);
-      $form->questiontext = "What is {a} + {b}?";
-
-      if ($courseid) {
-          $course = $DB->get_record('course', array('id'=> $courseid));
-      }
-
-      $new_question = $this->save_question($question, $form, $course);
-
-      $dataset_form = new stdClass();
-      $dataset_form->nextpageparam["forceregeneration"]= 1;
-      $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
-      $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
-      $dataset_form->calclength = array(1 => 1, 2 => 1);
-      $dataset_form->number = array(1 => 5.4 , 2 => 4.9);
-      $dataset_form->itemid = array(1 => '' , 2 => '');
-      $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
-      $dataset_form->definition = array(1 => "1-0-a",
-                                        2 => "1-0-b");
-      $dataset_form->nextpageparam = array('forceregeneration' => false);
-      $dataset_form->addbutton = 1;
-      $dataset_form->selectadd = 1;
-      $dataset_form->courseid = $courseid;
-      $dataset_form->cmid = 0;
-      $dataset_form->id = $new_question->id;
-      $this->save_dataset_items($new_question, $dataset_form);
-
-      return $new_question;
-  }
+    /**
+     * Runs all the code required to set up and save an essay question for testing purposes.
+     * Alternate DB table prefix may be used to facilitate data deletion.
+     */
+    function generate_test($name, $courseid = null) {
+        global $DB;
+        list($form, $question) = parent::generate_test($name, $courseid);
+        $form->feedback = 1;
+        $form->multiplier = array(1, 1);
+        $form->shuffleanswers = 1;
+        $form->noanswers = 1;
+        $form->qtype ='calculatedmulti';
+        $question->qtype ='calculatedmulti';
+        $form->answers = array('{a} + {b}');
+        $form->fraction = array(1);
+        $form->tolerance = array(0.01);
+        $form->tolerancetype = array(1);
+        $form->correctanswerlength = array(2);
+        $form->correctanswerformat = array(1);
+        $form->questiontext = "What is {a} + {b}?";
+
+        if ($courseid) {
+            $course = $DB->get_record('course', array('id'=> $courseid));
+        }
+
+        $new_question = $this->save_question($question, $form, $course);
+
+        $dataset_form = new stdClass();
+        $dataset_form->nextpageparam["forceregeneration"]= 1;
+        $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
+        $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
+        $dataset_form->calclength = array(1 => 1, 2 => 1);
+        $dataset_form->number = array(1 => 5.4 , 2 => 4.9);
+        $dataset_form->itemid = array(1 => '' , 2 => '');
+        $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
+        $dataset_form->definition = array(1 => "1-0-a",
+            2 => "1-0-b");
+        $dataset_form->nextpageparam = array('forceregeneration' => false);
+        $dataset_form->addbutton = 1;
+        $dataset_form->selectadd = 1;
+        $dataset_form->courseid = $courseid;
+        $dataset_form->cmid = 0;
+        $dataset_form->id = $new_question->id;
+        $this->save_dataset_items($new_question, $dataset_form);
+
+        return $new_question;
+    }
+
+    /**
+     * When move the category of questions, the belonging files should be moved as well
+     * @param object $question, question information
+     * @param object $newcategory, target category information
+     */
+    function move_files($question, $newcategory) {
+        global $DB;
+        // move files belonging to question component
+        parent::move_files($question, $newcategory);
+
+        // move files belonging to qtype_calculatedmulti
+        $fs = get_file_storage();
+        // process files in answer
+        if (!$oldanswers = $DB->get_records('question_answers', array('question' =>  $question->id), 'id ASC')) {
+            $oldanswers = array();
+        }
+        $component = 'question';
+        $filearea = 'answerfeedback';
+        foreach ($oldanswers as $answer) {
+            $files = $fs->get_area_files($question->contextid, $component, $filearea, $answer->id);
+            foreach ($files as $storedfile) {
+                if (!$storedfile->is_directory()) {
+                    $newfile = new object();
+                    $newfile->contextid = (int)$newcategory->contextid;
+                    $fs->create_file_from_storedfile($newfile, $storedfile);
+                    $storedfile->delete();
+                }
+            }
+        }
+
+        $component = 'qtype_calculatedmulti';
+        foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $filearea) {
+            $files = $fs->get_area_files($question->contextid, $component, $filearea, $question->id);
+            foreach ($files as $storedfile) {
+                if (!$storedfile->is_directory()) {
+                    $newfile = new object();
+                    $newfile->contextid = (int)$newcategory->contextid;
+                    $fs->create_file_from_storedfile($newfile, $storedfile);
+                    $storedfile->delete();
+                }
+            }
+        }
+    }
+
+    function check_file_access($question, $state, $options, $contextid, $component,
+            $filearea, $args) {
+        $itemid = reset($args);
+
+        if (empty($question->maxgrade)) {
+            $question->maxgrade = $question->defaultgrade;
+        }
+
+        if (in_array($filearea, array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'))) {
+            $result = $options->feedback && ($itemid == $question->id);
+            if (!$result) {
+                return false;
+            }
+            if ($state->raw_grade >= $question->maxgrade/1.01) {
+                $feedbacktype = 'correctfeedback';
+            } else if ($state->raw_grade > 0) {
+                $feedbacktype = 'partiallycorrectfeedback';
+            } else {
+                $feedbacktype = 'incorrectfeedback';
+            }
+            if ($feedbacktype != $filearea) {
+                return false;
+            }
+            return true;
+        } else if ($component == 'question' && $filearea == 'answerfeedback') {
+            return $options->feedback && (array_key_exists($itemid, $question->options->answers));
+        } else {
+            return parent::check_file_access($question, $state, $options, $contextid, $component,
+                    $filearea, $args);
+        }
+    }
 }
+
 //// END OF CLASS ////
 
 //////////////////////////////////////////////////////////////////////////
index f63b72c..8c84517 100644 (file)
@@ -1,4 +1,20 @@
 <?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/>.
+
 /**
  * Defines the editing form for the calculated simplequestion type.
  *
  * @package questionbank
  * @subpackage questiontypes
  */
-
-/**
- * calculatedsimple editing form definition.
- */
 class question_edit_calculatedsimple_form extends question_edit_form {
     /**
      * Handle to the question type for this question.
      *
      * @var question_calculatedsimple_qtype
      */
-    var $qtypeobj;
+    public $qtypeobj;
 
-    var $wildcarddisplay ;
+    public $wildcarddisplay ;
 
-    var $questiondisplay ;
+    public $questiondisplay ;
 
     public $datasetdefs;
 
@@ -46,8 +58,6 @@ class question_edit_calculatedsimple_form extends question_edit_form {
 
     public $formdata = array();
 
-
-
     function question_edit_calculatedsimple_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
         global $QTYPES, $SESSION, $CFG, $DB;
         $this->regenerate = true;
@@ -60,17 +70,17 @@ class question_edit_calculatedsimple_form extends question_edit_form {
         //so this should be removed from here
         // get priority to paramdatasets
 
-        if  (  "1" == optional_param('reload','', PARAM_INT )) {
-            $this->reload = true ;
+        if ("1" == optional_param('reload','', PARAM_INT )) {
+            $this->reload = true;
         }else {
-            $this->reload = false ;
+            $this->reload = false;
         }
-        if(!$this->reload ){ // use database data as this is first pass
+        if (!$this->reload) { // use database data as this is first pass
             // question->id == 0 so no stored datasets
             // else get datasets
             //   echo "<p>question  <pre>";print_r($question);echo "</pre></p>";
-            if ( !empty($question->id)) {
-                
+            if (!empty($question->id)) {
+
               /*  if (empty($question->options)) {
                     $this->get_question_options($question);
                 }*/
@@ -127,7 +137,7 @@ class question_edit_calculatedsimple_form extends question_edit_form {
             $mandatorydatasets = array();
             // should not test on adding a new answer
             // should test if there are already olddatasets or if the 'analyzequestion' submit button has been clicked
-            if (''  != optional_param('datasetdef', '', PARAM_RAW) || ''  != optional_param('analyzequestion', '', PARAM_RAW)){
+            if ('' != optional_param('datasetdef', '', PARAM_RAW) || '' != optional_param('analyzequestion', '', PARAM_RAW)){
 
                 if  (  $dummyform->answer = optional_param('answer', '', PARAM_NOTAGS)) { // there is always at least one answer...
                     $fraction = optional_param('fraction', '', PARAM_NUMBER);
@@ -297,12 +307,12 @@ class question_edit_calculatedsimple_form extends question_edit_form {
         $addfieldsname='updatequestion value';
         $addstring=get_string("updatecategory", "qtype_calculated");
         $mform->registerNoSubmitButton($addfieldsname);
-//put a submit button to stop supplementary answers on update answers parameters
-//        $mform->insertElementBefore(    $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
+        // put a submit button to stop supplementary answers on update answers parameters
+        // $mform->insertElementBefore($mform->createElement('submit', $addfieldsname, $addstring), 'listcategory');
 
         $creategrades = get_grade_options();
         $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'),
-                $creategrades->gradeoptions, 1, 1);
+            $creategrades->gradeoptions, 1, 1);
 
         $QTYPES['numerical']->add_units_options($mform,$this);
 
@@ -327,7 +337,7 @@ class question_edit_calculatedsimple_form extends question_edit_form {
             $this->noofitems = 0;
         }
         if(!empty($this->datasetdefs)){//So there are some datadefs
-        // we put them on the page
+            // we put them on the page
             $key = 0;
             $mform->addElement('header', 'additemhdr', get_string('wildcardparam', 'qtype_calculatedsimple'));
             $idx = 1;
@@ -347,180 +357,183 @@ class question_edit_calculatedsimple_form extends question_edit_form {
             }
             //this should be done before the elements are created and stored as $this->formdata ;
             //fill out all data sets and also the fields for the next item to add.
-        /*Here we do already the values error analysis so that
-        * we could force all wild cards values display if there is an error in values.
-        * as using a , in a number */
-        $this->numbererrors = array();
+            /*Here we do already the values error analysis so that
+             * we could force all wild cards values display if there is an error in values.
+             * as using a , in a number */
+            $this->numbererrors = array();
             if(!empty($this->datasetdefs)){
-                    $j = $this->noofitems * count($this->datasetdefs);
-                    for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){
-                        $data = array();
-                        $numbererrors = array() ;
-                        $comment = new stdClass;
-                            $comment->stranswers = array();
-                            $comment->outsidelimit = false ;
-                            $comment->answers = array();
-
-                        foreach ($this->datasetdefs as $defid => $datasetdef){
-                            if (isset($datasetdef->items[$itemnumber])){
-                                $this->formdata["definition[$j]"] = $defid;
-                                $this->formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id;
-                                $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
-                                $this->formdata["number[$j]"] = $number = $datasetdef->items[$itemnumber]->value;
-                                        if(! is_numeric($number)){
-                                        $a = new stdClass;
-                                        $a->name = '{'.$datasetdef->name.'}' ;
-                                        $a->value = $datasetdef->items[$itemnumber]->value ;
-                if (stristr($number,',')){
+                $j = $this->noofitems * count($this->datasetdefs);
+                for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){
+                    $data = array();
+                    $numbererrors = array() ;
+                    $comment = new stdClass;
+                    $comment->stranswers = array();
+                    $comment->outsidelimit = false ;
+                    $comment->answers = array();
+
+                    foreach ($this->datasetdefs as $defid => $datasetdef){
+                        if (isset($datasetdef->items[$itemnumber])){
+                            $this->formdata["definition[$j]"] = $defid;
+                            $this->formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id;
+                            $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
+                            $this->formdata["number[$j]"] = $number = $datasetdef->items[$itemnumber]->value;
+                            if(! is_numeric($number)){
+                                $a = new stdClass;
+                                $a->name = '{'.$datasetdef->name.'}' ;
+                                $a->value = $datasetdef->items[$itemnumber]->value ;
+                                if (stristr($number,',')){
                                     $this->numbererrors["number[$j]"]=get_string('nocommaallowed', 'qtype_calculated');
-                                $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
+                                    $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
 
-                }else {
+                                }else {
                                     $this->numbererrors["number[$j]"]= get_string('notvalidnumber','qtype_calculated',$a);
                                     $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
                                     //$comment->outsidelimit = false ;
-                                  }
-            }else if( stristr($number,'x')){ // hexa will pass the test
-                $a = new stdClass;
-                $a->name = '{'.$datasetdef->name.'}' ;
-                $a->value = $datasetdef->items[$itemnumber]->value ;
-                    $this->numbererrors['number['.$j.']']= get_string('hexanotallowed','qtype_calculated',$a);
-                                    $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
-                    } else if( is_nan($number)){
-                        $a = new stdClass;
-                        $a->name = '{'.$datasetdef->name.'}' ;
-                        $a->value = $datasetdef->items[$itemnumber]->value ;
-                                            $this->numbererrors["number[$j]"]= get_string('notvalidnumber','qtype_calculated',$a);
-                                            $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
-                         //   $val = 1.0 ;
-                    }
+                                }
+                            }else if( stristr($number,'x')){ // hexa will pass the test