Commit fe6ce234 authored by Dongsheng Cai's avatar Dongsheng Cai
Browse files

MDL-16094 File storage conversion Quiz and Questions

parent ba206b3a
<?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"
>
......@@ -1246,8 +1246,9 @@
<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>
......@@ -1266,10 +1267,10 @@
<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"/>
......@@ -1293,9 +1294,11 @@
<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"/>
......@@ -1338,8 +1341,9 @@
<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"/>
......@@ -1575,7 +1579,7 @@
<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 Not met, Borderline, Met (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 “Not met, Borderline, Met” (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"/>
......@@ -2709,4 +2713,4 @@
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
\ No newline at end of file
</XMLDB>
......@@ -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;
}
......
......@@ -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);
}
......@@ -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.
*/
......
<?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"
>
......@@ -100,8 +100,9 @@
<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>
......
......@@ -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;
}
......@@ -155,7 +155,7 @@
$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;
......
......@@ -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_st