MDL-16094 File storage conversion Quiz and Questions

This commit is contained in:
Dongsheng Cai 2010-08-10 09:56:48 +00:00
parent ba206b3a2f
commit fe6ce23489
80 changed files with 4386 additions and 2214 deletions

View 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"
>
@ -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"/>

View 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;
}

View 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
@ -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
@ -3192,3 +3206,123 @@ class question_edit_contexts {
}
}
}
/**
* 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);
}

View 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.
*/

View file

@ -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"
>
@ -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>

View 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;
}

View file

@ -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;

View 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);
// access granted if the current user submitted this file
if ($attempt->userid == $USER->id) {
return true;
// 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;
// does question exist?
if (!$question = $DB->get_record('question', array('id' => $questionid))) {
return false;
}
// otherwise, this user does not have permission
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 false;
// access granted if the current user has permission to grade quizzes in this course
}
if (!(has_capability('mod/quiz:viewreports', $context) || has_capability('mod/quiz:grade', $context))) {
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);
}

View 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;
}
/**

View 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);
}
}

View file

@ -200,7 +200,7 @@
}
/// 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>';

View 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)?

View file

@ -277,7 +277,7 @@
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[] = '';
}
@ -330,7 +330,7 @@
}
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) {

View 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);

View file

@ -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);
}

View file

@ -28,7 +28,7 @@
*
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank
*//** */
*/
require_once("../config.php");
require_once("editlib.php");

View file

@ -1426,7 +1426,8 @@ class question_bank_view {
} else {
$returnurl = str_replace($CFG->wwwroot . '/', '', $returnurl);
$movecontexturl = new moodle_url('/question/contextmoveq.php',
array('returnurl' => $returnurl, 'ids' => $questionids,
array('returnurl' => $returnurl,
'ids' => implode(',', $questionids),
'tocatid' => $tocategoryid));
if (!empty($cm->id)){
$movecontexturl->param('cmid', $cm->id);

View 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);
}
}

View file

@ -219,7 +219,7 @@
}
$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);

108
question/previewlib.php Normal file
View file

@ -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);
}

View 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;
@ -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()){

View 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"
>
@ -29,10 +29,13 @@
<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"/>

View 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;
}

View 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.
*
@ -48,14 +64,14 @@ class question_edit_calculated_form extends question_edit_form {
}
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[] =& $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';
@ -115,28 +131,27 @@ class question_edit_calculated_form extends question_edit_form {
$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);
// 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');
@ -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() {
@ -208,8 +255,8 @@ class question_edit_calculated_form extends question_edit_form {
$errors = parent::validation($data, $files);
//verifying for errors in {=...} in question text;
$qtext = "";
$qtextremaining = $data['questiontext'] ;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
$qtextremaining = $data['questiontext']['text'];
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
}
@ -229,7 +276,7 @@ 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);
@ -323,4 +370,3 @@ class question_edit_calculated_form extends question_edit_form {
return $errors;
}
}

View 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';
@ -93,6 +95,7 @@ $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['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';

View file

@ -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);
}

View 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() {
@ -40,7 +54,6 @@ class question_calculated_qtype extends default_questiontype {
$question->options->correctfeedback = '';
$question->options->partiallycorrectfeedback = '';
$question->options->incorrectfeedback = '';
}
if (!$question->options->answers = $DB->get_records_sql(
@ -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 ;
@ -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;
}
@ -136,19 +144,19 @@ class question_calculated_qtype extends default_questiontype {
$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
@ -178,13 +186,17 @@ class question_calculated_qtype extends default_questiontype {
$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,12 +231,12 @@ 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);
}
@ -316,19 +328,15 @@ class question_calculated_qtype extends default_questiontype {
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))) {
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
";
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 {
@ -373,8 +381,6 @@ class question_calculated_qtype extends default_questiontype {
return $virtualqtype->create_session_and_responses($question, $state, $cmoptions, $attempt);
}
function save_session_and_responses(&$question, &$state) {
global $DB;
$responses = 'dataset'.$state->options->datasetitem.'-' ;
@ -536,9 +542,7 @@ class question_calculated_qtype extends default_questiontype {
break;
}
$mform->display();
}
/**
@ -558,7 +562,7 @@ class question_calculated_qtype extends default_questiontype {
$possibledatasets = $this->find_dataset_names($form->questiontext);
$mandatorydatasets = array();
foreach ($form->answers as $answer) {
$mandatorydatasets += $this->find_dataset_names($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
@ -600,12 +604,9 @@ class question_calculated_qtype extends default_questiontype {
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));
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)){
@ -780,8 +781,7 @@ 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 ;
@ -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){
@ -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);
@ -1745,13 +1743,9 @@ class question_calculated_qtype extends default_questiontype {
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))) {
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();
@ -1780,16 +1774,13 @@ class question_calculated_qtype extends default_questiontype {
}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))){
$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) {
@ -1846,10 +1837,8 @@ class question_calculated_qtype extends default_questiontype {
$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 = ?";
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;
@ -1866,7 +1855,6 @@ class question_calculated_qtype extends default_questiontype {
* 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();
@ -1946,10 +1934,8 @@ class question_calculated_qtype extends default_questiontype {
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.*
@ -2254,6 +2240,78 @@ class question_calculated_qtype extends default_questiontype {
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 ////
@ -2381,7 +2439,6 @@ function qtype_calculated_find_formula_errors($formula) {
while ( preg_match("~(^|[$safeoperatorchar,(])([a-z0-9_]*)\\(($operatorornumber+(,$operatorornumber+((,$operatorornumber+)+)?)?)?\\)~",
$formula, $regs)) {
switch ($regs[2]) {
// Simple parenthesis
case '':
@ -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";
}

View file

@ -1,5 +1,5 @@
<?PHP
$plugin->version = 2010020800;
$plugin->version = 2010020801;
$plugin->requires = 2007101000;

View file

@ -18,7 +18,7 @@ class question_edit_calculatedmulti_form extends question_edit_form {
*
* @var question_calculatedmulti_qtype
*/
var $qtypeobj;
public $qtypeobj;
public $questiondisplay ;
public $initialname = '';
public $reload = false ;
@ -52,8 +52,7 @@ class question_edit_calculatedmulti_form extends question_edit_form {
// 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';
@ -116,7 +115,7 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$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->setHelpButton('shuffleanswers', array('multichoiceshuffle', get_string('shuffleanswers','qtype_multichoice'), 'qtype_multichoice'));
$mform->setDefault('shuffleanswers', 1);
$numberingoptions = $QTYPES['multichoice']->get_numbering_styles();
@ -154,8 +153,7 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$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->addElement('editor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'), null, $this->editoroptions);
$mform->setType($feedbackname, PARAM_RAW);
}
//hidden elements
@ -168,24 +166,27 @@ class question_edit_calculatedmulti_form extends question_edit_form {
}
$mform->addElement('hidden', 'wizard', 'datasetdefinitions');
$mform->setType('wizard', PARAM_ALPHA);
}
function set_data($question) {
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++;
}
}
@ -216,17 +217,38 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$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['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
// 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;
@ -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() {
@ -254,15 +275,14 @@ 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>";
$errors = parent::validation($data, $files);
//verifying for errors in {=...} in question text;
$qtext = "";
$qtextremaining = $data['questiontext'] ;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
$qtextremaining = $data['questiontext']['text'];
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
}
@ -282,7 +302,7 @@ 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);
@ -366,7 +386,6 @@ class question_edit_calculatedmulti_form extends question_edit_form {
}
}
if ($answercount==0){
$errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
}
@ -378,4 +397,3 @@ class question_edit_calculatedmulti_form extends question_edit_form {
return $errors;
}
}

View file

@ -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);
}

View 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,9 +41,8 @@ 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;
}
@ -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
@ -72,6 +85,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
// Save the units.
$virtualqtype = $this->get_virtual_qtype($question);
// TODO: What is this?
// $result = $virtualqtype->save_numerical_units($question);
if (isset($result->error)) {
return $result;
@ -88,13 +102,17 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$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
@ -148,21 +166,16 @@ 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(
$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))) {
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
";
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 {
@ -219,7 +232,6 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$state->responses = array();
}
return true;
}
function save_session_and_responses(&$question, &$state) {
@ -251,10 +263,6 @@ 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);
@ -396,7 +404,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
}
$qtext = $qtext.$str ;
}
$answer->answer = $qtext.$qtextremaining ; ;
$answer->answer = $qtext.$qtextremaining;
$comment->stranswers[$key] = $answer->answer;
@ -437,10 +445,6 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
return fullclone($comment);
}
function get_correct_responses1(&$question, &$state) {
$virtualqtype = $this->get_virtual_qtype( $question);
/* if ($question->options->multichoice != 1 ) {
@ -528,7 +532,84 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
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 ////
//////////////////////////////////////////////////////////////////////////

View 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.
*
@ -8,21 +24,17 @@
* @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;
@ -399,8 +409,11 @@ class question_edit_calculatedsimple_form extends question_edit_form {
}
if($this->noofitems != 0 ) {
if (empty($numbererrors)) {
if(!isset($this->question->id)) $this->question->id = 0 ;
$comment = $this->qtypeobj->comment_on_datasetitems($this->qtypeobj,$this->question->id,$this->question->questiontext,$this->nonemptyanswer, $data, $itemnumber);//$this->
if (!isset($this->question->id)) {
$this->question->id = 0 ;
}
$this->question->questiontext = !empty($this->question->questiontext)?$this->question->questiontext:'';
$comment = $this->qtypeobj->comment_on_datasetitems($this->qtypeobj, $this->question->id, $this->question->questiontext, $this->nonemptyanswer, $data, $itemnumber);
if ($comment->outsidelimit) {
$this->outsidelimit=$comment->outsidelimit ;
}
@ -546,9 +559,8 @@ class question_edit_calculatedsimple_form extends question_edit_form {
}
}
function set_data($question) {
function data_preprocessing($question) {
global $QTYPES;
$answer = $this->answer;
$default_values = array();
if (count($answer)) {
@ -560,11 +572,26 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
$default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
$default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
// prepare draft files
$draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
$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
!empty($answer->feedback)?$answer->feedback:'' // text
);
$default_values['feedback['.$key.']']['format'] = !empty($answer->feedbackformat)?$answer->feedbackformat:editors_get_preferred_format();
$default_values['feedback['.$key.']']['itemid'] = $draftid;
$key++;
}
}
$default_values['synchronize'] = 0 ;
$QTYPES['numerical']->set_numerical_unit_data($question,$default_values);
$QTYPES['numerical']->set_numerical_unit_data($this, $question, $default_values);
/* if (isset($question->options)){
$default_values['unitgradingtype'] = $question->options->unitgradingtype ;
$default_values['unitpenalty'] = $question->options->unitpenalty ;
@ -647,7 +674,7 @@ class question_edit_calculatedsimple_form extends question_edit_form {
}*/
$question = (object)((array)$question + $default_values+$this->formdata );
parent::set_data($question);
return $question;
}
function qtype() {
@ -659,8 +686,8 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$errors = parent::validation($data, $files);
//verifying for errors in {=...} in question text;
$qtext = "";
$qtextremaining = $data['questiontext'] ;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
$qtextremaining = $data['questiontext']['text'];
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
}
@ -679,7 +706,7 @@ class question_edit_calculatedsimple_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);
@ -797,4 +824,3 @@ class question_edit_calculatedsimple_form extends question_edit_form {
return $errors;
}
}

View file

@ -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_calculatedsimple_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_calculatedsimple', $filearea, $args, $forcedownload);
}

View file

@ -1,30 +1,41 @@
<?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_calculatedsimple_qtype extends question_calculated_qtype {
// Used by the function custom_generator_tools:
var $calcgenerateidhasbeenadded = false;
public $calcgenerateidhasbeenadded = false;
public $virtualqtype = false;
function name() {
return 'calculatedsimple';
}
function save_question_options($question) {
global $CFG, $DB , $QTYPES;
$context = $question->context;
//$options = $question->subtypeoptions;
// Get old answers:
global $CFG, $DB , $QTYPES;
if (isset($question->answer) && !isset($question->answers)) {
$question->answers = $question->answer;
@ -57,13 +68,17 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
$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', $answer->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
@ -99,7 +114,6 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
}
}
if(isset($question->import_process)&&$question->import_process) {
$this->import_datasets($question);
} else {
@ -164,6 +178,7 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
}
$i++;
}
$maxnumber = -1;
if (isset($addeditem->itemnumber) && $maxnumber < $addeditem->itemnumber){
$maxnumber = $addeditem->itemnumber;
foreach ($datasetdefs as $key => $newdef) {
@ -194,9 +209,6 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
return true ; //isset($form->backtoquiz);
}
/**
* this version save the available data at the different steps of the question editing process
* without using global $SESSION as storage between steps
@ -221,8 +233,6 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
return $question;
}
function custom_generator_tools_part(&$mform, $idx, $j){
$minmaxgrp = array();
@ -338,6 +348,76 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
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 = 'feedback';
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_calculatedsimple';
$filearea = 'feedback';
$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 {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
return true;
}
}
//// END OF CLASS ////
@ -349,5 +429,3 @@ question_register_questiontype(new question_calculatedsimple_qtype());
if ( ! defined ("CALCULATEDSIMPLE")) {
define("CALCULATEDSIMPLE", "calculatedsimple");
}

View 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 description question type.
*

View file

@ -6,10 +6,6 @@
<div class="qtext">
<?php echo $questiontext; ?>
</div>
<?php if ($image) { ?>
<img class="qimage" src="<?php echo $image; ?>" alt="" />
<?php } ?>
</div>
<?php if ($generalfeedback) { ?>
<div class="generalfeedback">

View file

@ -68,7 +68,6 @@ class description_qtype extends default_questiontype {
$editlink = $this->get_question_edit_link($question, $cmoptions, $options);
$questiontext = $this->format_text($question->questiontext, $question->questiontextformat, $cmoptions);
$image = get_question_image($question);
$generalfeedback = '';
if ($isfinished && $options->generalfeedback) {
@ -91,4 +90,3 @@ class description_qtype extends default_questiontype {
}
// Register this question type with questionlib.php.
question_register_questiontype(new description_qtype());

View 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/>.
/**
* A base class for question editing forms.
*
@ -7,7 +23,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank
* @subpackage questiontypes
*//** */
*/
/**
* Form definition base class. This defines the common fields that
@ -27,24 +43,44 @@ class question_edit_form extends moodleform {
* @var object
*/
public $question;
public $contexts;
public $category;
public $categorycontext;
public $coursefilesid;
/** @var object current context */
public $context;
/** @var array html editor options */
public $editoroptions;
/** @var array options to preapre draft area */
public $fileoptions;
/** @var object instance of question type */
public $instance;
function question_edit_form($submiturl, $question, $category, $contexts, $formeditable = true){
global $DB;
$this->question = $question;
$this->contexts = $contexts;
$record = $DB->get_record('question_categories', array('id'=>$question->category), 'contextid');
$this->context = get_context_instance_by_id($record->contextid);
$this->editoroptions = array('maxfiles' => EDITOR_UNLIMITED_FILES, 'context'=>$this->context);
$this->fileoptions = array('subdir'=>true, 'maxfiles'=>-1, 'maxbytes'=>-1);
$this->category = $category;
$this->categorycontext = get_context_instance_by_id($category->contextid);
//** *
//course id or site id depending on question cat context
$this->coursefilesid = get_filesdir_from_context(get_context_instance_by_id($category->contextid));
if (!empty($question->id)) {
$question->id = (int)$question->id;
//$this->instance = new
}
parent::moodleform($submiturl, null, 'post', '', null, $formeditable);
}
@ -106,26 +142,9 @@ class question_edit_form extends moodleform {
$mform->setType('name', PARAM_TEXT);
$mform->addRule('name', null, 'required', null, 'client');
//TODO: MDL-16094 convert to new editor element
$mform->addElement('htmleditor', 'questiontext', get_string('questiontext', 'quiz'),
array('rows' => 15, 'course' => $this->coursefilesid));
$mform->addElement('editor', 'questiontext', get_string('questiontext', 'quiz'),
array('rows' => 15, 'course' => $this->coursefilesid), $this->editoroptions);
$mform->setType('questiontext', PARAM_RAW);
//$mform->addElement('format', 'questiontextformat', get_string('format'));
$mform->addElement('hidden', 'questiontextformat');
$mform->setType('questiontextformat', PARAM_INT);
make_upload_directory($this->coursefilesid); // Just in case
$coursefiles = get_directory_list("$CFG->dataroot/$this->coursefilesid", $CFG->moddata);
foreach ($coursefiles as $filename) {
if (mimeinfo("icon", $filename) == "image.gif") {
$images["$filename"] = $filename;
}
}
if (empty($images)) {
$mform->addElement('static', 'image', get_string('imagedisplay', 'quiz'), get_string('noimagesyet'));
} else {
$mform->addElement('select', 'image', get_string('imagedisplay', 'quiz'), array_merge(array(''=>get_string('none')), $images));
}
$mform->addElement('text', 'defaultgrade', get_string('defaultgrade', 'quiz'),
array('size' => 3));
@ -140,8 +159,8 @@ class question_edit_form extends moodleform {
$mform->addHelpButton('penalty', 'penaltyfactor', 'question');
$mform->setDefault('penalty', 0.1);
$mform->addElement('htmleditor', 'generalfeedback', get_string('generalfeedback', 'quiz'),
array('rows' => 10, 'course' => $this->coursefilesid));
$mform->addElement('editor', 'generalfeedback', get_string('generalfeedback', 'quiz'),
array('rows' => 10, 'course' => $this->coursefilesid), $this->editoroptions);
$mform->setType('generalfeedback', PARAM_RAW);
$mform->addHelpButton('generalfeedback', 'generalfeedback', 'quiz');
@ -264,8 +283,8 @@ class question_edit_form extends moodleform {
$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'),
array('course' => $this->coursefilesid), $this->editoroptions);
$repeatedoptions['answer']['type'] = PARAM_RAW;
$repeatedoptions['fraction']['default'] = 0;
$answersoption = 'answers';
@ -302,9 +321,33 @@ class question_edit_form extends moodleform {
function set_data($question) {
global $QTYPES;
if (empty($question->image)){
unset($question->image);
// prepare question text
$draftid = file_get_submitted_draft_itemid('questiontext');
if (!empty($question->questiontext)) {
$questiontext = $question->questiontext;
} else {
$questiontext = '';
}
$questiontext = file_prepare_draft_area($draftid, $this->context->id, 'question', 'questiontext', empty($question->id)?null:(int)$question->id, null, $questiontext);
$question->questiontext = array();
$question->questiontext['text'] = $questiontext;
$question->questiontext['format'] = empty($question->questiontextformat) ? editors_get_preferred_format() : $question->questiontextformat;
$question->questiontext['itemid'] = $draftid;
// prepare general feedback
$draftid = file_get_submitted_draft_itemid('generalfeedback');
if (empty($question->generalfeedback)) {
$question->generalfeedback = '';
}
$feedback = file_prepare_draft_area($draftid, $this->context->id, 'question', 'generalfeedback', empty($question->id)?null:(int)$question->id, null, $question->generalfeedback);
$question->generalfeedback = array();
$question->generalfeedback['text'] = $feedback;
$question->generalfeedback['format'] = empty($question->generalfeedbackformat) ? editors_get_preferred_format() : $question->generalfeedbackformat;
$question->generalfeedback['itemid'] = $draftid;
// Remove unnecessary trailing 0s form grade fields.
if (isset($question->defaultgrade)) {
@ -324,10 +367,20 @@ class question_edit_form extends moodleform {
}
}
}
// subclass adds data_preprocessing code here
$question = $this->data_preprocessing($question);
parent::set_data($question);
}
/**
* Any preprocessing needed for the settings form for the question type
*
* @param array $question - array to fill in with the default values
*/
function data_preprocessing($question) {
return $question;
}
/**
* Override this in the subclass to question type name.
* @return the question type name, should be the same as the name() method in the question type class.
@ -336,5 +389,3 @@ class question_edit_form extends moodleform {
return '';
}
}

View file

@ -2,10 +2,6 @@
<?php echo $questiontext; ?>
</div>
<?php if ($image) { ?>
<img class="qimage" src="<?php echo $image; ?>" alt="" />
<?php } ?>
<div class="ablock clearfix">
<div class="prompt">
<?php echo $stranswer; ?>

View 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 essay question type.
*
@ -19,8 +35,7 @@ class question_edit_essay_form extends question_edit_form {
* @param MoodleQuickForm $mform the form being built.
*/
function definition_inner(&$mform) {
$mform->addElement('htmleditor', 'feedback', get_string("feedback", "quiz"),
array('course' => $this->coursefilesid));
$mform->addElement('editor', 'feedback', get_string('feedback', 'quiz'), null, $this->editoroptions);
$mform->setType('feedback', PARAM_RAW);
$mform->addElement('hidden', 'fraction', 0);
@ -32,17 +47,29 @@ class question_edit_essay_form extends question_edit_form {
$mform->setType('penalty', PARAM_RAW);
}
function set_data($question) {
function data_preprocessing($question) {
if (!empty($question->options) && !empty($question->options->answers)) {
$answer = reset($question->options->answers);
$question->feedback = $answer->feedback;
$question->feedback = array();
$draftid = file_get_submitted_draft_itemid('feedback');
$question->feedback['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
);
$question->feedback['text'] = $answer->feedback;
$question->feedback['format'] = $answer->feedbackformat;
$question->feedback['itemid'] = $draftid;
}
$question->penalty = 0;
parent::set_data($question);
return $question;
}
function qtype() {
return 'essay';
}
}

View file

@ -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_essay_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_essay', $filearea, $args, $forcedownload);
}

View file

@ -1,5 +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/>.
//////////////////
/// ESSAY ///
/////////////////
@ -21,6 +36,7 @@ class question_essay_qtype extends default_questiontype {
function save_question_options($question) {
global $DB;
$context = $question->context;
$result = true;
$update = true;
$answer = $DB->get_record("question_answers", array("question" => $question->id));
@ -29,13 +45,20 @@ class question_essay_qtype extends default_questiontype {
$answer->question = $question->id;
$update = false;
}
$answer->answer = $question->feedback;
$answer->feedback = $question->feedback;
$answer->feedbackformat = $question->feedback['format'];
$answer->answerformat = $question->feedback['format'];
$answer->fraction = $question->fraction;
if ($update) {
$answer->feedback = file_save_draft_area_files($question->feedback['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback['text']));
$answer->answer = $answer->feedback;
$DB->update_record("question_answers", $answer);
} else {
$answer->id = $DB->insert_record("question_answers", $answer);
$answer->feedback = $question->feedback['text'];
$answer->answer = $answer->feedback;
$answer->id = $DB->insert_record('question_answers', $answer);
$answer->feedback = file_save_draft_area_files($question->feedback['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback['text']));
$answer->answer = $answer->feedback;
$DB->update_record('question_answers', $answer);
}
return $result;
}
@ -43,6 +66,8 @@ class question_essay_qtype extends default_questiontype {
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG;
$context = $this->get_context_by_category_id($question->category);
$answers = &$question->options->answers;
$readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
@ -60,13 +85,12 @@ class question_essay_qtype extends default_questiontype {
$question->questiontextformat,
$formatoptions, $cmoptions->course);
$image = get_question_image($question);
// feedback handling
$feedback = '';
if ($options->feedback && !empty($answers)) {
foreach ($answers as $answer) {
$feedback = format_text($answer->feedback, '', $formatoptions, $cmoptions->course);
$feedback = quiz_rewrite_question_urls($answer->feedback, 'pluginfile.php', $context->id, 'qtype_essay', 'feedback', array($state->attempt, $state->question), $answer->id);
$feedback = format_text($feedback, $answer->feedbackformat, $formatoptions, $cmoptions->course);
}
}
@ -139,6 +163,54 @@ class question_essay_qtype extends default_questiontype {
return $this->save_question($question, $form, $course);
}
/**
* 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();
}
}
}
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
if ($component == 'question' && $filearea == 'answerfeedback') {
$answerid = reset($args); // itemid is answer id.
$answers = &$question->options->answers;
if (isset($state->responses[''])) {
$response = $state->responses[''];
} else {
$response = '';
}
return $options->feedback && !empty($response);
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
}
// Restore method not needed.
}
//// END OF CLASS ////
@ -147,4 +219,3 @@ class question_essay_qtype extends default_questiontype {
//// INITIATION - Without this line the question type is not in use... ///
//////////////////////////////////////////////////////////////////////////
question_register_questiontype(new question_essay_qtype());

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="question/type/match/db" VERSION="20060812" COMMENT="XMLDB file for Moodle question/type/match">
<XMLDB PATH="question/type/match/db" VERSION="20100721" COMMENT="XMLDB file for Moodle question/type/match"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="question_match" COMMENT="Defines fixed matching questions" NEXT="question_match_sub">
<FIELDS>
@ -18,8 +21,9 @@
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="code"/>
<FIELD NAME="code" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Should this point to parent question_match-&amp;gt;id ?" PREVIOUS="id" NEXT="question"/>
<FIELD NAME="question" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="code" NEXT="questiontext"/>
<FIELD NAME="questiontext" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="question" NEXT="answertext"/>
<FIELD NAME="answertext" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="questiontext"/>
<FIELD NAME="questiontext" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="question" NEXT="questiontextformat"/>
<FIELD NAME="questiontextformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="questiontext" NEXT="answertext"/>
<FIELD NAME="answertext" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="questiontextformat"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/>

View file

@ -0,0 +1,58 @@
<?php
// This file keeps track of upgrades to
// the match qtype plugin
//
// Sometimes, changes between versions involve
// alterations to database structures and other
// major things that may break installations.
//
// The upgrade function in this file will attempt
// to perform all the necessary actions to upgrade
// your older installation to the current version.
//
// If there's something it cannot do itself, it
// will tell you what you need to do.
//
// The commands in here will all be database-neutral,
// using the methods of database_manager class
//
// Please do not forget to use upgrade_set_timeout()
// before any action that may take longer time to finish.
function xmldb_qtype_match_upgrade($oldversion) {
global $CFG, $DB, $QTYPES;
$dbman = $DB->get_manager();
if ($oldversion < 2009072100) {
// Define field questiontextformat to be added to question_match_sub
$table = new xmldb_table('question_match_sub');
$field = new xmldb_field('questiontextformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'questiontext');
// Conditionally launch add field questiontextformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
$rs = $DB->get_recordset('question_match_sub');
foreach ($rs as $record) {
if ($CFG->texteditors !== 'textarea') {
if (!empty($record->questiontext)) {
$record->questiontext = text_to_html($record->questiontext);
}
$record->questiontextformat = FORMAT_HTML;
} else {
$record->questiontextformat = FORMAT_MOODLE;
}
$DB->update_record('question_match_sub', $record);
}
$rs->close();
// match savepoint reached
upgrade_plugin_savepoint(true, 2009072100, 'qtype', 'match');
}
return true;
}

View file

@ -2,10 +2,6 @@
<?php echo $questiontext; ?>
</div>
<?php if ($image) { ?>
<img class="qimage" src="<?php echo $image; ?>" alt="" />
<?php } ?>
<div class="ablock clearfix">
<table class="answer">

View file

@ -17,7 +17,7 @@ class question_edit_match_form extends question_edit_form {
function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
$repeated = array();
$repeated[] =& $mform->createElement('header', 'answerhdr', $label);
$repeated[] =& $mform->createElement('textarea', 'subquestions', get_string('question', 'quiz'), array('cols'=>40, 'rows'=>3));
$repeated[] =& $mform->createElement('editor', 'subquestions', get_string('question', 'quiz'), null, $this->editoroptions);
$repeated[] =& $mform->createElement('text', 'subanswers', get_string('answer', 'quiz'), array('size'=>50));
$repeatedoptions['subquestions']['type'] = PARAM_RAW;
$repeatedoptions['subanswers']['type'] = PARAM_TEXT;
@ -41,21 +41,35 @@ class question_edit_match_form extends question_edit_form {
$this->add_per_answer_fields($mform, get_string('questionno', 'quiz', '{no}'), 0);
}
function set_data($question) {
function data_preprocessing($question) {
if (isset($question->options)) {
$subquestions = $question->options->subquestions;
if (count($subquestions)) {
$key = 0;
foreach ($subquestions as $subquestion){
$default_values['subanswers['.$key.']'] = $subquestion->answertext;
$default_values['subquestions['.$key.']'] = $subquestion->questiontext;
$draftid = file_get_submitted_draft_itemid('subquestions['.$key.']');
$default_values['subquestions['.$key.']'] = array();
$default_values['subquestions['.$key.']']['format'] = $subquestion->questiontextformat;
$default_values['subquestions['.$key.']']['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'qtype_match', // component
'subquestion', // filarea
!empty($subquestion->id)?(int)$subquestion->id:null, // itemid
$this->fileoptions, // options
$subquestion->questiontext // text
);
$default_values['subquestions['.$key.']']['itemid'] = $draftid;
$key++;
}
}
$default_values['shuffleanswers'] = $question->options->shuffleanswers;
$question = (object)((array)$question + $default_values);
}
parent::set_data($question);
return $question;
}
function qtype() {
@ -69,7 +83,7 @@ class question_edit_match_form extends question_edit_form {
$questioncount = 0;
$answercount = 0;
foreach ($questions as $key => $question){
$trimmedquestion = trim($question);
$trimmedquestion = trim($question['text']);
$trimmedanswer = trim($answers[$key]);
if ($trimmedquestion != ''){
$questioncount++;

View file

@ -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_match_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $DB, $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_match', $filearea, $args, $forcedownload);
}

View file

@ -1,5 +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/>.
/////////////
/// MATCH ///
/////////////
@ -24,6 +39,7 @@ class question_match_qtype extends default_questiontype {
function save_question_options($question) {
global $DB;
$context = $question->context;
$result = new stdClass;
if (!$oldsubquestions = $DB->get_records("question_match_sub", array("question" => $question->id), "id ASC")) {
@ -35,12 +51,15 @@ class question_match_qtype extends default_questiontype {
// Insert all the new question+answer pairs
foreach ($question->subquestions as $key => $questiontext) {
$questiontext = trim($questiontext);
$itemid = $questiontext['itemid'];
$format = $questiontext['format'];
$questiontext = trim($questiontext['text']);
$answertext = trim($question->subanswers[$key]);
if ($questiontext != '' || $answertext != '') {
if ($subquestion = array_shift($oldsubquestions)) { // Existing answer, so reuse it
$subquestion->questiontext = $questiontext;
$subquestion->answertext = $answertext;
$subquestion->questiontext = file_save_draft_area_files($itemid, $context->id, 'qtype_match', 'subquestion', $subquestion->id, self::$fileoptions, $questiontext);
$subquestion->questiontextformat = $format;
$DB->update_record("question_match_sub", $subquestion);
} else {
$subquestion = new stdClass;
@ -51,8 +70,11 @@ class question_match_qtype extends default_questiontype {
}
$subquestion->question = $question->id;
$subquestion->questiontext = $questiontext;
$subquestion->questiontextformat = $format;
$subquestion->answertext = $answertext;
$subquestion->id = $DB->insert_record("question_match_sub", $subquestion);
$questiontext = file_save_draft_area_files($itemid, $context->id, 'qtype_match', 'subquestion', $subquestion->id, self::$fileoptions, $questiontext);
$DB->set_field('question_match_sub', 'questiontext', $questiontext, array('id'=>$subquestion->id));
}
$subquestions[] = $subquestion->id;
}
@ -228,6 +250,7 @@ class question_match_qtype extends default_questiontype {
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG, $OUTPUT;
$context = $this->get_context_by_category_id($question->category);
$subquestions = $state->options->subquestions;
$correctanswers = $this->get_correct_responses($question, $state);
$nameprefix = $question->name_prefix;
@ -263,15 +286,14 @@ class question_match_qtype extends default_questiontype {
// Print formulation
$questiontext = $this->format_text($question->questiontext,
$question->questiontextformat, $cmoptions);
$image = get_question_image($question);
// Print the input controls
foreach ($subquestions as $key => $subquestion) {
if ($subquestion->questiontext !== '' && !is_null($subquestion->questiontext)) {
// Subquestion text:
$a = new stdClass;
$a->text = $this->format_text($subquestion->questiontext,
$question->questiontextformat, $cmoptions);
$text = quiz_rewrite_question_urls($subquestion->questiontext, 'pluginfile.php', $context->id, 'qtype_match', 'subquestion', array($state->attempt, $state->question), $subquestion->id);
$a->text = $this->format_text($text, $subquestion->questiontextformat, $cmoptions);
// Drop-down list:
$menuname = $nameprefix.$subquestion->id;
@ -757,6 +779,53 @@ class question_match_qtype extends default_questiontype {
return $this->save_question($question, $form, $course);
}
function move_files($question, $newcategory) {
global $DB;
// move files belonging to question component
parent::move_files($question, $newcategory);
// move files belonging to qtype_multichoice
$fs = get_file_storage();
// process files in answer
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
}
// process files in sub questions
if (!$subquestions = $DB->get_records('question_match_sub', array('question' => $question->id), 'id ASC')) {
$subquestions = array();
}
$component = 'qtype_match';
$filearea = 'subquestion';
foreach ($subquestions as $sub) {
$files = $fs->get_area_files($question->contextid, $component, $filearea, $sub->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 ($filearea == 'subquestion') {
// always display quetion images
// itemid is sub question id
if ($itemid != $question->id) {
return false;
}
return true;
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
}
}
//// END OF CLASS ////
@ -764,4 +833,3 @@ class question_match_qtype extends default_questiontype {
//// INITIATION - Without this line the question type is not in use... ///
//////////////////////////////////////////////////////////////////////////
question_register_questiontype(new question_match_qtype());

View file

@ -1,6 +1,6 @@
<?php
$plugin->version = 2006032200;
$plugin->version = 2009072100;
$plugin->requires = 2007101000;

View file

@ -2,10 +2,6 @@
<?php echo $questiontext; ?>
</div>
<?php if ($image) { ?>
<img class="qimage" src="<?php echo $image; ?>" alt="" />
<?php } ?>
<?php if ($anss) { ?>
<div class="ablock clearfix">
<div class="prompt">

View file

@ -42,7 +42,6 @@ class question_missingtype_qtype extends default_questiontype {
$questiontext = format_text($question->questiontext,
$question->questiontextformat,
$formatoptions, $cmoptions->course);
$image = get_question_image($question);
// Print each answer in a separate row if there are any
$anss = array();

View file

@ -15,15 +15,15 @@
class question_edit_multianswer_form extends question_edit_form {
// $questiondisplay will contain the qtype_multianswer_extract_question from the questiontext
var $questiondisplay ;
public $questiondisplay ;
// $savedquestiondisplay will contain the qtype_multianswer_extract_question from the questiontext in database
var $savedquestion ;
var $savedquestiondisplay ;
var $used_in_quiz = false ;
var $qtype_change = false ;
var $negative_diff = 0 ;
var $nb_of_quiz = 0;
var $nb_of_attempts = 0;
public $savedquestion ;
public $savedquestiondisplay ;
public $used_in_quiz = false ;
public $qtype_change = false ;
public $negative_diff = 0 ;
public $nb_of_quiz = 0;
public $nb_of_attempts = 0;
public $confirm = 0 ;
public $reload = false ;
@ -51,15 +51,9 @@ class question_edit_multianswer_form extends question_edit_form {
}
}
parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
}
function definition_inner(&$mform) {
$mform->addElement('hidden', 'reload', 1);
$mform->setType('reload', PARAM_INT);
@ -181,11 +175,9 @@ class question_edit_multianswer_form extends question_edit_form {
}
if($this->negative_diff > 0) {
//$this->used_in_quiz
$mform->addElement('static', 'alert1', "<strong>".get_string('questiondeleted','qtype_multianswer')."</strong>",get_string('questionsless','qtype_multianswer',$this->negative_diff));
}
if($this->qtype_change )
{
if($this->qtype_change ) {
$mform->addElement('static', 'alert1', "<strong>".get_string('questiontypechanged','qtype_multianswer')."</strong>",get_string('questiontypechangedcomment','qtype_multianswer'));
}
echo '</div>';
@ -359,12 +351,9 @@ class question_edit_multianswer_form extends question_edit_form {
function validation($data, $files) {
$errors = parent::validation($data, $files);
$questiondisplay = qtype_multianswer_extract_question($data['questiontext']) ;
$questiondisplay = qtype_multianswer_extract_question($data['questiontext']['text']);
if (isset($questiondisplay->options->questions)) {
$subquestions = fullclone($questiondisplay->options->questions) ;
if (count($subquestions)) {
$sub =1;
@ -422,4 +411,3 @@ class question_edit_multianswer_form extends question_edit_form {
return 'multianswer';
}
}

View file

@ -307,10 +307,6 @@ class embedded_cloze_qtype extends default_questiontype {
}
echo '<div class="ablock clearfix">';
// For this question type, we better print the image on top:
if ($image = get_question_image($question)) {
echo('<img class="qimage" src="' . $image . '" alt="" /><br />');
}
$qtextremaining = format_text($question->questiontext,
$question->questiontextformat, $formatoptions, $cmoptions->course);
@ -986,9 +982,7 @@ function qtype_multianswer_extract_question($text) {
$question->options->questions = array();
$question->defaultgrade = 0; // Will be increased for each answer norm
for ($positionkey=1
; preg_match('/'.ANSWER_REGEX.'/', $question->questiontext, $answerregs)
; ++$positionkey ) {
for ($positionkey=1; preg_match('/'.ANSWER_REGEX.'/', $question->questiontext['text'], $answerregs); ++$positionkey ) {
$wrapped = new stdClass;
if (isset($answerregs[ANSWER_REGEX_NORM])&& $answerregs[ANSWER_REGEX_NORM]!== ''){
$wrapped->defaultgrade = $answerregs[ANSWER_REGEX_NORM];
@ -1087,4 +1081,3 @@ function qtype_multianswer_extract_question($text) {
$question->questiontext = $question->questiontext;
return $question;
}
?>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="question/type/multichoice/db" VERSION="20070413" COMMENT="XMLDB file for Moodle question/type/multichoice"
<XMLDB PATH="question/type/multichoice/db" VERSION="20100716" COMMENT="XMLDB file for Moodle question/type/multichoice"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
@ -12,10 +12,13 @@
<FIELD NAME="answers" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="Redundant. Comma-separated list of question_answer ids. SELECT id FROM question_answers WHERE question = ? ORDER BY id." PREVIOUS="layout" 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="answers" NEXT="shuffleanswers"/>
<FIELD NAME="shuffleanswers" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="1" SEQUENCE="false" COMMENT="Whether the choices can be randomly shuffled." PREVIOUS="single" NEXT="correctfeedback"/>
<FIELD NAME="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any correct response." PREVIOUS="shuffleanswers" NEXT="partiallycorrectfeedback"/>
<FIELD NAME="partiallycorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any partially correct response." PREVIOUS="correctfeedback" NEXT="incorrectfeedback"/>
<FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="true" 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="true" 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="true" 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="true" 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"/>

View file

@ -52,6 +52,61 @@ function xmldb_qtype_multichoice_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2008021800, 'qtype', 'multichoice');
}
if ($oldversion < 2009021801) {
/// Define field correctfeedbackformat to be added to question_multichoice
$table = new xmldb_table('question_multichoice');
$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_multichoice
$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_multichoice
$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);
}
$rs = $DB->get_recordset('question_multichoice');
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_multichoice', $record);
}
$rs->close();
/// multichoice savepoint reached
upgrade_plugin_savepoint(true, 2009021801, 'qtype', 'multichoice');
}
return true;
}

View file

@ -2,10 +2,6 @@
<?php echo $questiontext; ?>
</div>
<?php if ($image) { ?>
<img class="qimage" src="<?php echo $image; ?>" alt="" />
<?php } ?>
<div class="ablock clearfix">
<div class="prompt">
<?php echo $answerprompt; ?>

View file

@ -1,4 +1,22 @@
<?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/>.
defined('MOODLE_INTERNAL') || die();
/**
* Defines the editing form for the multichoice question type.
*
@ -47,14 +65,14 @@ class question_edit_multichoice_form extends question_edit_form {
$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->addElement('editor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'),
array('course' => $this->coursefilesid), $this->editoroptions);
$mform->setType($feedbackname, PARAM_RAW);
}
}
function set_data($question) {
function data_preprocessing($question) {
if (isset($question->options)){
$answers = $question->options->answers;
if (count($answers)) {
@ -62,19 +80,44 @@ class question_edit_multichoice_form extends question_edit_form {
foreach ($answers as $answer){
$default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction;
$default_values['feedback['.$key.']'] = $answer->feedback;
// prepare question text
$draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
$default_values['feedback['.$key.']'] = array();
$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['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;
// 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_multichoice', // 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;
}
// prepare files code block ends
$question = (object)((array)$question + $default_values);
}
parent::set_data($question);
return $question;
}
function qtype() {

View file

@ -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_multichoice_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_multichoice', $filearea, $args, $forcedownload);
}

View file

@ -38,6 +38,7 @@ class question_multichoice_qtype extends default_questiontype {
function save_question_options($question) {
global $DB;
$context = $question->context;
$result = new stdClass;
if (!$oldanswers = $DB->get_records("question_answers", array("question" => $question->id), "id ASC")) {
$oldanswers = array();
@ -65,18 +66,23 @@ class question_multichoice_qtype extends default_questiontype {
foreach ($question->answer as $key => $dataanswer) {
if ($dataanswer != "") {
$feedbackformat = $question->feedback[$key]['format'];
if ($answer = array_shift($oldanswers)) { // Existing answer, so reuse it
$answer->answer = $dataanswer;
$answer->fraction = $question->fraction[$key];
$answer->feedback = $question->feedback[$key];
$answer->feedbackformat = $feedbackformat;
$answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $question->feedback[$key]['text']);
$DB->update_record("question_answers", $answer);
} else {
unset($answer);
$answer->answer = $dataanswer;
$answer->question = $question->id;
$answer->fraction = $question->fraction[$key];
$answer->feedback = $question->feedback[$key];
$answer->feedback = '';
$answer->feedbackformat = $feedbackformat;
$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, $question->feedback[$key]['text']);
$DB->set_field('question_answers', 'feedback', $answer->feedback, array('id'=>$answer->id));
}
$answers[] = $answer->id;
@ -104,9 +110,15 @@ class question_multichoice_qtype extends default_questiontype {
}
$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('correct', 'partiallycorrect', 'incorrect') as $feedbacktype) {
$feedbackname = $feedbacktype . 'feedback';
$feedbackformat = $feedbackname . 'format';
$feedback = $question->$feedbackname;
$options->$feedbackformat = trim($feedback['format']);
$options->$feedbackname = file_save_draft_area_files($feedback['itemid'], $context->id, 'qtype_multichoice', $feedbackname, $question->id, self::$fileoptions, trim($feedback['text']));
}
if ($update) {
$DB->update_record("question_multichoice", $options);
} else {
@ -252,6 +264,10 @@ class question_multichoice_qtype extends default_questiontype {
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG;
// required by file api
$context = $this->get_context_by_category_id($question->category);
$component = 'qtype_' . $question->qtype;
$answers = &$question->options->answers;
$correctanswers = $this->get_correct_responses($question, $state);
$readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
@ -261,10 +277,8 @@ class question_multichoice_qtype extends default_questiontype {
$formatoptions->para = false;
// Print formulation
$questiontext = format_text($question->questiontext,
$question->questiontextformat,
$questiontext = format_text($question->questiontext, $question->questiontextformat,
$formatoptions, $cmoptions->course);
$image = get_question_image($question);
$answerprompt = ($question->options->single) ? get_string('singleanswer', 'quiz') :
get_string('multipleanswers', 'quiz');
@ -311,11 +325,13 @@ class question_multichoice_qtype extends default_questiontype {
// Print the answer text
$a->text = $this->number_in_style($key, $question->options->answernumbering) .
format_text($answer->answer, FORMAT_MOODLE, $formatoptions, $cmoptions->course);
format_text($answer->answer, $answer->answerformat, $formatoptions, $cmoptions->course);
// Print feedback if feedback is on
if (($options->feedback || $options->correct_responses) && $checked) {
$a->feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course);
// feedback for each answer
$a->feedback = quiz_rewrite_question_urls($answer->feedback, 'pluginfile.php', $context->id, 'question', 'answerfeedback', array($state->attempt, $state->question), $answer->id);
$a->feedback = format_text($a->feedback, $answer->feedbackformat, $formatoptions, $cmoptions->course);
} else {
$a->feedback = '';
}
@ -327,14 +343,18 @@ class question_multichoice_qtype extends default_questiontype {
if ($options->feedback) {
if ($state->raw_grade >= $question->maxgrade/1.01) {
$feedback = $question->options->correctfeedback;
$feedbacktype = 'correctfeedback';
} else if ($state->raw_grade > 0) {
$feedback = $question->options->partiallycorrectfeedback;
$feedbacktype = 'partiallycorrectfeedback';
} else {
$feedback = $question->options->incorrectfeedback;
$feedbacktype = 'incorrectfeedback';
}
$feedback = format_text($feedback,
$question->questiontextformat,
$formatoptions, $cmoptions->course);
$feedback = quiz_rewrite_question_urls($feedback, 'pluginfile.php', $context->id, $component, $feedbacktype, array($state->attempt, $state->question), $question->id);
$feedbackformat = $feedbacktype . 'format';
$feedback = format_text($feedback, $question->options->$feedbackformat, $formatoptions, $cmoptions->course);
}
include("$CFG->dirroot/question/type/multichoice/display.html");
@ -708,8 +728,82 @@ class question_multichoice_qtype extends default_questiontype {
return $this->save_question($question, $form, $course);
}
/**
* 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_multichoice
$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_multichoice';
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);
}
}
}
// Register this question type with the question bank.
question_register_questiontype(new question_multichoice_qtype());

View file

@ -1,6 +1,6 @@
<?php
$plugin->version = 2009021800;
$plugin->version = 2009021801;
$plugin->requires = 2007101000;

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="question/type/numerical/db" VERSION="20091001" COMMENT="XMLDB file for Moodle question/type/numerical"
<XMLDB PATH="question/type/numerical/db" VERSION="20100720" COMMENT="XMLDB file for Moodle question/type/numerical"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
@ -23,8 +23,9 @@
<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="instructions"/>
<FIELD NAME="instructions" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="question" NEXT="showunits"/>
<FIELD NAME="showunits" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="display units as multichoice" PREVIOUS="instructions" NEXT="unitsleft"/>
<FIELD NAME="instructions" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="question" NEXT="instructionsformat"/>
<FIELD NAME="instructionsformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="instructions" NEXT="showunits"/>
<FIELD NAME="showunits" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="display units as multichoice" PREVIOUS="instructionsformat" NEXT="unitsleft"/>
<FIELD NAME="unitsleft" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="display the unit at left as in $1.00" PREVIOUS="showunits" NEXT="unitgradingtype"/>
<FIELD NAME="unitgradingtype" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="0 no penalty, 1 total grade, 2 response grade" PREVIOUS="unitsleft" NEXT="unitpenalty"/>
<FIELD NAME="unitpenalty" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="true" DEFAULT="0.1" SEQUENCE="false" DECIMALS="7" PREVIOUS="unitgradingtype"/>

View file

@ -51,6 +51,35 @@ function xmldb_qtype_numerical_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2009100100, 'qtype', 'numerical');
}
if ($oldversion < 2009100101) {
// Define field instructionsformat to be added to question_numerical_options
$table = new xmldb_table('question_numerical_options');
$field = new xmldb_field('instructionsformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'instructions');
// Conditionally launch add field instructionsformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
$rs = $DB->get_recordset('question_numerical_options');
foreach ($rs as $record) {
if ($CFG->texteditors !== 'textarea') {
if (!empty($record->instructions)) {
$record->instructions = text_to_html($record->instructions);
}
$record->instructionsformat = FORMAT_HTML;
} else {
$record->instructionsformat = FORMAT_MOODLE;
}
$DB->update_record('question_numerical_options', $record);
}
$rs->close();
// numerical savepoint reached
upgrade_plugin_savepoint(true, 2009100101, 'qtype', 'numerical');
}
return true;
}

View file

@ -2,10 +2,6 @@
<?php echo $questiontext; ?>
</div>
<?php if ($image) { ?>
<img class="qimage" src="<?php echo $image; ?>" alt="" />
<?php } ?>
<div class="ablock clearfix">
<div class="prompt">
<?php echo get_string("answer", "quiz").': '; ?>
@ -237,7 +233,7 @@
</div>
<?php if (!empty($question->options->instructions)){?>
<div>
<?php echo format_text($question->options->instructions, true, $formatoptions, $cmoptions->course);?>
<?php echo format_text($question->options->instructions, $question->options->instructionsformat, $formatoptions, $cmoptions->course);?>
</div>
<?php }?>
</fieldset>
@ -250,5 +246,3 @@
<?php } ?>
<?php $this->print_question_submit_buttons($question, $state, $cmoptions, $options); ?>
</div>

View 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 numerical question type.
*
@ -42,27 +58,33 @@ class question_edit_numerical_form extends question_edit_form {
$QTYPES['numerical']->add_units_elements($mform,$this);
}
function set_data($question) {
function data_preprocessing($question) {
global $QTYPES ;
if (isset($question->options)){
/* $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 ;
*/
$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['feedback['.$key.']'] = $answer->feedback;
$default_values['feedback['.$key.']'] = array();
$default_values['feedback['.$key.']']['format'] = $answer->feedbackformat;
$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.']']['itemid'] = $draftid;
$key++;
}
}
$QTYPES['numerical']->set_numerical_unit_data($question,$default_values);
$QTYPES['numerical']->set_numerical_unit_data($this, $question, $default_values);
/* if (isset($question->options->units)){
$units = array_values($question->options->units);
@ -75,8 +97,9 @@ class question_edit_numerical_form extends question_edit_form {
}*/
$question = (object)((array)$question + $default_values);
}
parent::set_data($question);
return $question;
}
function validation($data, $files) {
global $QTYPES;
$errors = parent::validation($data, $files);
@ -95,7 +118,7 @@ class question_edit_numerical_form extends question_edit_form {
if ($data['fraction'][$key] == 1) {
$maxgrade = true;
}
} else if ($data['fraction'][$key] != 0 || !html_is_blank($data['feedback'][$key])) {
} else if ($data['fraction'][$key] != 0 || !html_is_blank($data['feedback'][$key]['text'])) {
$errors["answer[$key]"] = get_string('answermustbenumberorstar', 'qtype_numerical');
$answercount++;
}
@ -110,6 +133,7 @@ class question_edit_numerical_form extends question_edit_form {
return $errors;
}
function qtype() {
return 'numerical';
}

View file

@ -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_numerical_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_numerical', $filearea, $args, $forcedownload);
}

View file

@ -1,10 +1,26 @@
<?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/>.
/**
* @author Martin Dougiamas and many others. Tim Hunt.
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank
* @subpackage questiontypes
*//** */
*/
require_once("$CFG->dirroot/question/type/shortanswer/questiontype.php");
@ -19,6 +35,7 @@ require_once("$CFG->dirroot/question/type/shortanswer/questiontype.php");
* @package questionbank
* @subpackage questiontypes
*/
class question_numerical_qtype extends question_shortanswer_qtype {
public $virtualqtype = false;
@ -86,17 +103,19 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}
$question->options->unitsleft = 0 ;
$question->options->instructions = '';
$question->options->instructionsformat = editors_get_preferred_format();
} else {
$question->options->unitgradingtype = $options->unitgradingtype;
$question->options->unitpenalty = $options->unitpenalty;
$question->options->showunits = $options->showunits;
$question->options->unitsleft = $options->unitsleft;
$question->options->instructions = $options->instructions;
$question->options->instructionsformat = $options->instructionsformat;
}
return true;
}
function get_numerical_units(&$question) {
global $DB;
if ($units = $DB->get_records('question_numerical_units', array('question' => $question->id), 'id ASC')) {
@ -127,6 +146,8 @@ class question_numerical_qtype extends question_shortanswer_qtype {
*/
function save_question_options($question) {
global $DB;
$context = $question->context;
// Get old versions of the objects
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
@ -148,7 +169,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
foreach ($question->answer as $key => $dataanswer) {
// Check for, and ingore, completely blank answer from the form.
if (trim($dataanswer) == '' && $question->fraction[$key] == 0 &&
html_is_blank($question->feedback[$key])) {
html_is_blank($question->feedback[$key]['text'])) {
continue;
}
@ -163,13 +184,23 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}
}
$answer->fraction = $question->fraction[$key];
$answer->feedback = trim($question->feedback[$key]);
$feedbacktext = trim($question->feedback[$key]['text']);
$draftid = $question->feedback[$key]['itemid'];
$answer->feedbackformat = $question->feedback[$key]['format'];
if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
$feedbacktext = file_save_draft_area_files($draftid, $context->id, 'question', 'answerfeedback', $oldanswer->id, self::$fileoptions, $feedbacktext);
$answer->feedback = $feedbacktext;
$answer->id = $oldanswer->id;
$DB->update_record("question_answers", $answer);
} else { // This is a completely new answer
$answer->feedback = $feedbacktext;
$answer->id = $DB->insert_record("question_answers", $answer);
$feedbacktext = file_save_draft_area_files($draftid, $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $feedbacktext);
$DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$answer->id));
}
// Set up the options object
@ -220,10 +251,11 @@ class question_numerical_qtype extends question_shortanswer_qtype {
function save_numerical_options(&$question) {
global $DB;
$result = new stdClass;
// numerical options
$update = true ;
$options = $DB->get_record("question_numerical_options", array("question" => $question->id));
$options = $DB->get_record('question_numerical_options', array('question' => $question->id));
if (!$options) {
$update = false;
$options = new stdClass;
@ -264,22 +296,27 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}else {
$options->unitsleft = 0 ;
}
$options->instructionsformat = $question->instructions['format'];
if(isset($question->instructions)){
$options->instructions = trim($question->instructions);
$options->instructions = trim($question->instructions['text']);
}else {
$options->instructions = '' ;
}
$component = 'qtype_' . $question->qtype;
$options->instructions = file_save_draft_area_files($question->instructions['itemid'],
$question->context->id, // context
$component, // component
'instruction', // filearea
$question->id, // itemid
self::$fileoptions, // options
$question->instructions['text'] // text
);
if ($update) {
if (!$DB->update_record("question_numerical_options", $options)) {
$result->error = "Could not update numerical question options! (id=$options->id)";
return $result;
}
$DB->update_record("question_numerical_options", $options);
} else {
if (!$DB->insert_record("question_numerical_options", $options)) {
$result->error = "Could not insert numerical question options!";
return $result;
}
$id = $DB->insert_record("question_numerical_options", $options);
}
return $result;
}
@ -404,18 +441,21 @@ class question_numerical_qtype extends question_shortanswer_qtype {
*/
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG;
$context = $this->get_context_by_category_id($question->category);
$readonly = empty($options->readonly) ? '' : 'readonly="readonly"';
$formatoptions = new stdClass;
$formatoptions->noclean = true;
$formatoptions->para = false;
$nameprefix = $question->name_prefix;
$component = 'qtype_' . $question->qtype;
// rewrite instructions text
$question->options->instructions = quiz_rewrite_question_urls($question->options->instructions, 'pluginfile.php', $context->id, $component, 'instruction', array($state->attempt, $state->question), $question->id);
/// Print question text and media
$questiontext = format_text($question->questiontext,
$question->questiontextformat,
$formatoptions, $cmoptions->course);
$image = get_question_image($question);
/// Print input controls
// as the entry is controlled the question type here is numerical
@ -426,8 +466,8 @@ class question_numerical_qtype extends question_shortanswer_qtype {
// radio elements display the defined unit
// The code allows the input number elememt to be displayed
// before i.e. at left or after at rigth of the unit variants.
$nameunit = "name=\"".$question->name_prefix."unit\"";
$nameanswer = "name=\"".$question->name_prefix."answer\"";
$nameunit = "name=\"".$question->name_prefix."unit\"";
if (isset($state->responses['']) && $state->responses[''] != '' && !isset($state->responses['answer'])){
$this->split_old_answer($state->responses[''], $question->options->units, $state->responses['answer'] ,$state->responses['unit'] );
}
@ -471,6 +511,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$this->get_tolerance_interval($answer);
}
$answer->feedback = quiz_rewrite_question_urls($answer->feedback, 'pluginfile.php', $context->id, 'question', 'answerfeedback', array($state->attempt, $state->question), $answer->id);
if ($answer->answer === '*') {
$answerasterisk = true ;
$rawgrade = $answer->fraction ;
@ -516,11 +557,13 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$class = question_get_feedback_class($answer->fraction);
$feedbackimg = question_get_feedback_image($answer->fraction);
if ($answer->feedback) {
$feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course);
$feedback = format_text($answer->feedback, $answer->feedbackformat, $formatoptions, $cmoptions->course);
}
$break = 1 ;
}
if ($break) break;
if ($break) {
break;
}
}
}
$state->options->raw_unitpenalty = 0 ;
@ -540,7 +583,6 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$state->options->raw_unitpenalty = $raw_unitpenalty ;
}
/// Removed correct answer, to be displayed later MDL-7496
include("$CFG->dirroot/question/type/numerical/display.html");
}
@ -938,8 +980,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$showunits0grp[] =& $mform->createElement('radio', 'showunits0', get_string('unitedit', 'qtype_numerical'), get_string('editableunittext', 'qtype_numerical'),0);
$showunits0grp[] =& $mform->createElement('radio', 'showunits0', get_string('selectunits', 'qtype_numerical') , get_string('unitchoice', 'qtype_numerical'),1);
$mform->addGroup($showunits0grp, 'showunits0grp', get_string('studentunitanswer', 'qtype_numerical'),' OR ' , false);
$mform->addElement('htmleditor', 'instructions', get_string('instructions', 'qtype_numerical'),
array('rows' => 10, 'course' => $that->coursefilesid));
$mform->addElement('editor', 'instructions', get_string('instructions', 'qtype_numerical'), null, $that->editoroptions);
$mform->addElement('static', 'separator1', '<HR/>', '<HR/>');
// Units are not graded
$mform->addElement('radio', 'unitrole', get_string('unitnotgraded', 'qtype_numerical'), get_string('onlynumerical', 'qtype_numerical'),1);
@ -967,6 +1008,8 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$mform->disabledIf('unitsleft', 'showunits1','eq','3');
$mform->disabledIf('showunits1','unitrole','eq','0');
$mform->disabledIf('showunits0','unitrole','eq','1');
}
/**
@ -1006,11 +1049,13 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}
/**
* function used in in function setdata ()
* of edit_..._form.php for
* function used in in function data_preprocessing() of edit_numerical_form.php for
* numerical, calculated, calculatedsimple
*/
function set_numerical_unit_data(&$question,&$default_values){
function set_numerical_unit_data($mform, &$question, &$default_values){
list($categoryid) = explode(',', $question->category);
$context = $this->get_context_by_category_id($categoryid);
if (isset($question->options)){
$default_values['unitgradingtype'] = $question->options->unitgradingtype ;
@ -1028,7 +1073,22 @@ class question_numerical_qtype extends question_shortanswer_qtype {
break;
}
$default_values['unitsleft'] = $question->options->unitsleft ;
$default_values['instructions'] = $question->options->instructions ;
// processing files
$component = 'qtype_' . $question->qtype;
$draftid = file_get_submitted_draft_itemid('instructions');
$default_values['instructions'] = array();
$default_values['instructions']['format'] = $question->options->instructionsformat;
$default_values['instructions']['text'] = file_prepare_draft_area(
$draftid, // draftid
$context->id, // context
$component, // component
'instruction', // filarea
!empty($question->id)?(int)$question->id:null, // itemid
$mform->fileoptions, // options
$question->options->instructions // text
);
$default_values['instructions']['itemid'] = $draftid;
if (isset($question->options->units)) {
$units = array_values($question->options->units);
@ -1284,6 +1344,71 @@ class question_numerical_qtype extends question_shortanswer_qtype {
return $this->save_question($question, $form, $course);
}
/**
* 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') {
$result = $options->feedback && array_key_exists($itemid, $question->options->answers);
if (!$result) {
return false;
}
foreach($question->options->answers as $answer) {
if ($this->test_response($question, $state, $answer)) {
return true;
}
}
return false;
} else if ($filearea == 'instruction') {
if ($itemid != $question->id) {
return false;
} else {
return true;
}
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
}
}
// INITIATION - Without this line the question type is not in use.

View file

@ -1,6 +1,6 @@
<?php
$plugin->version = 2009100100;
$plugin->version = 2009100101;
$plugin->requires = 2007101000;

View 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/>.
/**
* The default questiontype class.
*
@ -30,6 +46,7 @@ require_once($CFG->libdir . '/questionlib.php');
* @subpackage questiontypes
*/
class default_questiontype {
public static $fileoptions = array('subdirs'=>false, 'maxfiles'=>-1, 'maxbytes'=>0);
/**
* Name of the question type
@ -294,31 +311,35 @@ class default_questiontype {
*/
function save_question($question, $form, $course) {
global $USER, $DB, $OUTPUT;
list($question->category) = explode(',', $form->category);
$context = $this->get_context_by_category_id($question->category);
// This default implementation is suitable for most
// question types.
// First, save the basic question itself
$question->name = trim($form->name);
$question->questiontext = trim($form->questiontext);
$question->questiontextformat = $form->questiontextformat;
$question->parent = isset($form->parent) ? $form->parent : 0;
$question->length = $this->actual_number_of_questions($question);
$question->penalty = isset($form->penalty) ? $form->penalty : 0;
if (empty($form->image)) {
$question->image = '';
if (empty($form->questiontext['text'])) {
$question->questiontext = '';
} else {
$question->image = $form->image;
$question->questiontext = trim($form->questiontext['text']);;
}
$question->questiontextformat = !empty($form->questiontext['format'])?$form->questiontext['format']:0;
if (empty($form->generalfeedback)) {
if (empty($form->generalfeedback['text'])) {
$question->generalfeedback = '';
} else {
$question->generalfeedback = trim($form->generalfeedback);
$question->generalfeedback = trim($form->generalfeedback['text']);
}
$question->generalfeedbackformat = !empty($form->generalfeedback['format'])?$form->generalfeedback['format']:0;
if (empty($question->name)) {
$question->name = shorten_text(strip_tags($question->questiontext), 15);
$question->name = shorten_text(strip_tags($form->questiontext['text']), 15);
if (empty($question->name)) {
$question->name = '-';
}
@ -332,14 +353,16 @@ class default_questiontype {
$question->defaultgrade = $form->defaultgrade;
}
list($question->category) = explode(',', $form->category);
if (!empty($question->id)) {
/// Question already exists, update.
$question->modifiedby = $USER->id;
$question->timemodified = time();
$DB->update_record('question', $question);
// process queston text
$question->questiontext = file_save_draft_area_files($form->questiontext['itemid'], $context->id, 'question', 'questiontext', (int)$question->id, self::$fileoptions, $question->questiontext);
// process feedback text
$question->generalfeedback = file_save_draft_area_files($form->generalfeedback['itemid'], $context->id, 'question', 'generalfeedback', (int)$question->id, self::$fileoptions, $question->generalfeedback);
$DB->update_record('question', $question);
} else {
/// New question.
// Set the unique code
@ -349,6 +372,12 @@ class default_questiontype {
$question->timecreated = time();
$question->timemodified = time();
$question->id = $DB->insert_record('question', $question);
// process queston text
$question->questiontext = file_save_draft_area_files($form->questiontext['itemid'], $context->id, 'question', 'questiontext', (int)$question->id, self::$fileoptions, $question->questiontext);
// process feedback text
$question->generalfeedback = file_save_draft_area_files($form->generalfeedback['itemid'], $context->id, 'question', 'generalfeedback', (int)$question->id, self::$fileoptions, $question->generalfeedback);
$DB->update_record('question', $question);
}
// Now to save all the answers and type-specific options
@ -356,11 +385,14 @@ class default_questiontype {
$form->qtype = $question->qtype;
$form->category = $question->category;
$form->questiontext = $question->questiontext;
$form->questiontextformat = $question->questiontextformat;
// current context
$form->context = $context;
$result = $this->save_question_options($form);
if (!empty($result->error)) {
print_error('questionsaveerror', 'question', '', $result->error);
print_error($result->error);
}
if (!empty($result->notice)) {
@ -888,12 +920,18 @@ class default_questiontype {
* @param object $cmoptions
* @param object $options An object describing the rendering options.
*/
function print_question(&$question, &$state, $number, $cmoptions, $options) {
function print_question(&$question, &$state, $number, $cmoptions, $options, $context=null) {
/* The default implementation should work for most question types
provided the member functions it calls are overridden where required.
The layout is determined by the template question.html */
global $CFG, $OUTPUT;
$context = $this->get_context_by_category_id($question->category);
$question->questiontext = quiz_rewrite_question_urls($question->questiontext, 'pluginfile.php', $context->id, 'question', 'questiontext', array($state->attempt, $state->question), $question->id);
$question->generalfeedback = quiz_rewrite_question_urls($question->generalfeedback, 'pluginfile.php', $context->id, 'question', 'generalfeedback', array($state->attempt, $state->question), $question->id);
$isgraded = question_state_is_graded($state->last_graded);
if (isset($question->randomquestionid)) {
@ -908,7 +946,7 @@ class default_questiontype {
$generalfeedback = '';
if ($isgraded && $options->generalfeedback) {
$generalfeedback = $this->format_text($question->generalfeedback,
$question->questiontextformat, $cmoptions);
$question->generalfeedbackformat, $cmoptions);
}
$grade = '';
@ -1246,6 +1284,22 @@ class default_questiontype {
.' been implemented for question type '.$this->name());
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
if ($component == 'question' && $filearea == 'questiontext') {
// Question text always visible.
return true;
} else if ($component == 'question' && $filearea = 'generalfeedback') {
return $options->generalfeedback && question_state_is_graded($state->last_graded);
} else {
// Unrecognised component or filearea.
return false;
}
}
/**
* Prints the submit button(s) for the question in the given state
*
@ -1494,29 +1548,6 @@ class default_questiontype {
*/
function find_file_links($question, $courseid){
$urls = array();
/// Question image
if ($question->image != ''){
if (substr(strtolower($question->image), 0, 7) == 'http://') {
$matches = array();
//support for older questions where we have a complete url in image field
if (preg_match('!^'.question_file_links_base_url($courseid).'(.*)!i', $question->image, $matches)){
if ($cleanedurl = question_url_check($urls[$matches[2]])){
$urls[$cleanedurl] = null;
}
}
} else {
if ($question->image != ''){
if ($cleanedurl = question_url_check($question->image)){
$urls[$cleanedurl] = null;//will be set later
}
}
}
}
/// Questiontext and general feedback.
$urls += question_find_file_links_from_html($question->questiontext, $courseid);
$urls += question_find_file_links_from_html($question->generalfeedback, $courseid);
@ -1557,21 +1588,6 @@ class default_questiontype {
function replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination){
global $CFG, $DB;
$updateqrec = false;
/// Question image
if (!empty($question->image)){
//support for older questions where we have a complete url in image field
if (substr(strtolower($question->image), 0, 7) == 'http://') {
$questionimage = preg_replace('!^'.question_file_links_base_url($fromcourseid).preg_quote($url, '!').'$!i', $destination, $question->image, 1);
} else {
$questionimage = preg_replace('!^'.preg_quote($url, '!').'$!i', $destination, $question->image, 1);
}
if ($questionimage != $question->image){
$question->image = $questionimage;
$updateqrec = true;
}
}
/// Questiontext and general feedback.
$question->questiontext = question_replace_file_links_in_html($question->questiontext, $fromcourseid, $tocourseid, $url, $destination, $updateqrec);
$question->generalfeedback = question_replace_file_links_in_html($question->generalfeedback, $fromcourseid, $tocourseid, $url, $destination, $updateqrec);
@ -1802,5 +1818,45 @@ class default_questiontype {
$question->qtype = $this->qtype;
return array($form, $question);
}
/**
* Get question context by category id
* @param int $category
* @return object $context
*/
function get_context_by_category_id($category) {
global $DB;
$contextid = $DB->get_field('question_categories', 'contextid', array('id'=>$category));
$context = get_context_instance_by_id($contextid);
return $context;
}
/**
* 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;
$fs = get_file_storage();
$component = 'question';
// process general question files
// Currently we have questiontext and generalfeedback areas
foreach (array('questiontext', 'generalfeedback') as $filearea) {
$files = $fs->get_area_files($question->contextid, $component, $filearea, $question->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
if ($newcategory->contextid == $question->contextid) {
continue;
}
$newfile = new object();
// only contextid changed
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
// delete old files
$storedfile->delete();
}
}
}
}
}

View file

@ -79,11 +79,13 @@ class question_edit_random_form extends question_edit_form {
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
$mform->closeHeaderBefore('buttonar');
}
function validation($fromform, $files) {
//validation of category
//is not relevant for this question type
return array();
}
function qtype() {
return 'random';
}

View file

@ -88,6 +88,7 @@ class random_qtype extends default_questiontype {
}
function display_question_editing_page(&$mform, $question, $wizardnow){
global $OUTPUT;
$heading = $this->get_heading(empty($question->id));
echo $OUTPUT->heading_with_help($heading, $this->name(), $this->plugin_name());
$mform->display();
@ -424,5 +425,3 @@ class random_qtype extends default_questiontype {
//// INITIATION - Without this line the question type is not in use... ///
//////////////////////////////////////////////////////////////////////////
question_register_questiontype(new random_qtype());

View file

@ -19,8 +19,6 @@ class question_edit_randomsamatch_form extends question_edit_form {
* @param MoodleQuickForm $mform the form being built.
*/
function definition_inner(&$mform) {
$mform->removeElement('image');
$questionstoselect = array();
for ($i=2; $i<=QUESTION_NUMANS; $i++){
$questionstoselect[$i] = $i;
@ -33,7 +31,7 @@ class question_edit_randomsamatch_form extends question_edit_form {
$mform->setType('fraction', PARAM_RAW);
}
function set_data($question) {
function data_preprocessing($question) {
if (empty($question->name)) {
$question->name = get_string("randomsamatch", "quiz");
}
@ -41,7 +39,7 @@ class question_edit_randomsamatch_form extends question_edit_form {
if (empty($question->questiontext)) {
$question->questiontext = get_string("randomsamatchintro", "quiz");
}
parent::set_data($question);
return $question;
}
function qtype() {
@ -70,7 +68,5 @@ class question_edit_randomsamatch_form extends question_edit_form {
$errors['choose'] = get_string('notenoughsaincategory', 'qtype_randomsamatch', $a);
}
return $errors;
}
}

View file

@ -2,10 +2,6 @@
<?php echo $questiontext; ?>
</div>
<?php if ($image) { ?>
<img class="qimage" src="<?php echo $image; ?>" alt="" />
<?php } ?>
<div class="ablock clearfix">
<div class="prompt">
<?php echo get_string("answer", "quiz").': '; ?>

View file

@ -1,4 +1,22 @@
<?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/>.
defined('MOODLE_INTERNAL') || die();
/**
* Defines the editing form for the shortanswer question type.
*
@ -30,22 +48,40 @@ class question_edit_shortanswer_form extends question_edit_form {
$creategrades->gradeoptions);
}
function set_data($question) {
function data_preprocessing($question) {
if (isset($question->options)){
$answers = $question->options->answers;
$answers_ids = array();
if (count($answers)) {
$key = 0;
foreach ($answers as $answer){
$answers_ids[] = $answer->id;
$default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction;
$default_values['feedback['.$key.']'] = $answer->feedback;
$default_values['feedback['.$key.']'] = array();
// prepare feedback editor to display files in draft area
$draftid_editor = file_get_submitted_draft_itemid('feedback['.$key.']');
$default_values['feedback['.$key.']']['text'] = file_prepare_draft_area(
$draftid_editor, // 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.']']['itemid'] = $draftid_editor;
// prepare files code block ends
$default_values['feedback['.$key.']']['format'] = $answer->feedbackformat;
$key++;
}
}
$default_values['usecase'] = $question->options->usecase;
$question = (object)((array)$question + $default_values);
}
parent::set_data($question);
return $question;
}
function validation($data, $files) {
$errors = parent::validation($data, $files);
@ -59,7 +95,7 @@ class question_edit_shortanswer_form extends question_edit_form {
if ($data['fraction'][$key] == 1) {
$maxgrade = true;
}
} else if ($data['fraction'][$key] != 0 || !html_is_blank($data['feedback'][$key])) {
} else if ($data['fraction'][$key] != 0 || !html_is_blank($data['feedback'][$key]['text'])) {
$errors["answer[$key]"] = get_string('answermustbegiven', 'qtype_shortanswer');
$answercount++;
}

View file

@ -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_shortanswer_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $DB, $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_shortanswer', $filearea, $args, $forcedownload);
}

View file

@ -1,5 +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/>.
///////////////////
/// SHORTANSWER ///
///////////////////
@ -34,10 +49,41 @@ class question_shortanswer_qtype extends default_questiontype {
return '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();
}
}
}
}
function save_question_options($question) {
global $DB;
$result = new stdClass;
$context = $question->context;
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
}
@ -49,23 +95,36 @@ class question_shortanswer_qtype extends default_questiontype {
foreach ($question->answer as $key => $dataanswer) {
// Check for, and ingore, completely blank answer from the form.
if (trim($dataanswer) == '' && $question->fraction[$key] == 0 &&
html_is_blank($question->feedback[$key])) {
html_is_blank($question->feedback[$key]['text'])) {
continue;
}
$feedbackformat = $question->feedback[$key]['format'];
if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
$answer = $oldanswer;
$answer->answer = trim($dataanswer);
$answer->fraction = $question->fraction[$key];
$answer->feedback = $question->feedback[$key];
// save draft file and rewrite text in feedback
$answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $oldanswer->id, self::$fileoptions, $question->feedback[$key]['text']);
$answer->feedbackformat = $feedbackformat;
$DB->update_record("question_answers", $answer);
} else { // This is a completely new answer
$answer = new stdClass;
$answer->answer = trim($dataanswer);
$answer->question = $question->id;
$answer->fraction = $question->fraction[$key];
$answer->feedback = $question->feedback[$key];
// feedback content needs to be rewriten
$answer->feedback = '';
$answer->feedbackformat = $feedbackformat;
$answer->id = $DB->insert_record("question_answers", $answer);
// save draft file and rewrite text in feedback
$answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $question->feedback[$key]['text']);
// update feedback content
$DB->set_field('question_answers', 'feedback', $answer->feedback, array('id'=>$answer->id));
}
$answers[] = $answer->id;
if ($question->fraction[$key] > $maxfraction) {
@ -97,6 +156,8 @@ class question_shortanswer_qtype extends default_questiontype {
}
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG;
$context = $this->get_context_by_category_id($question->category);
/// This implementation is also used by question type 'numerical'
$readonly = empty($options->readonly) ? '' : 'readonly="readonly"';
$formatoptions = new stdClass;
@ -109,7 +170,6 @@ class question_shortanswer_qtype extends default_questiontype {
$questiontext = format_text($question->questiontext,
$question->questiontextformat,
$formatoptions, $cmoptions->course);
$image = get_question_image($question);
/// Print input controls
@ -135,6 +195,7 @@ class question_shortanswer_qtype extends default_questiontype {
$class = question_get_feedback_class($answer->fraction);
$feedbackimg = question_get_feedback_image($answer->fraction);
if ($answer->feedback) {
$answer->feedback = quiz_rewrite_question_urls($answer->feedback, 'pluginfile.php', $context->id, 'question', 'answerfeedback', array($state->attempt, $state->question), $answer->id);
$feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course);
}
break;
@ -390,6 +451,33 @@ class question_shortanswer_qtype extends default_questiontype {
return $this->save_question($question, $form, $course);
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
if ($component == 'question' && $filearea == 'answerfeedback') {
$answers = &$question->options->answers;
if (isset($state->responses[''])) {
$response = $state->responses[''];
} else {
$response = '';
}
$answerid = reset($args); // itemid is answer id.
if (empty($options->feedback)) {
return false;
}
foreach($answers as $answer) {
if ($this->test_response($question, $state, $answer)) {
return true;
}
}
return false;
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
}
}
//// END OF CLASS ////

View file

@ -2,10 +2,6 @@
<?php echo $questiontext; ?>
</div>
<?php if ($image) { ?>
<img class="qimage" src="<?php echo $image; ?>" alt="" />
<?php } ?>
<div class="ablock clearfix">
<div class="prompt">
<?php print_string('answer', 'quiz') ?>:

View file

@ -1,9 +1,26 @@
<?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/>.
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
require_once($CFG->dirroot.'/question/type/edit_question_form.php');
/**
* Defines the editing form for the thruefalse question type.
*
@ -12,7 +29,7 @@ require_once($CFG->dirroot.'/question/type/edit_question_form.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank
* @subpackage questiontypes
*//** */
*/
/**
* truefalse editing form definition.
@ -27,12 +44,10 @@ class question_edit_truefalse_form extends question_edit_form {
$mform->addElement('select', 'correctanswer', get_string('correctanswer', 'qtype_truefalse'),
array(0 => get_string('false', 'qtype_truefalse'), 1 => get_string('true', 'qtype_truefalse')));
$mform->addElement('htmleditor', 'feedbacktrue', get_string('feedbacktrue', 'qtype_truefalse'),
array('course' => $this->coursefilesid));;
$mform->addElement('editor', 'feedbacktrue', get_string('feedbacktrue', 'qtype_truefalse'), null, $this->editoroptions);;
$mform->setType('feedbacktrue', PARAM_RAW);
$mform->addElement('htmleditor', 'feedbackfalse', get_string('feedbackfalse', 'qtype_truefalse'),
array('course' => $this->coursefilesid));
$mform->addElement('editor', 'feedbackfalse', get_string('feedbackfalse', 'qtype_truefalse'), null, $this->editoroptions);
$mform->setType('feedbackfalse', PARAM_RAW);
// Fix penalty factor at 1.
@ -43,9 +58,45 @@ class question_edit_truefalse_form extends question_edit_form {
function set_data($question) {
if (!empty($question->options->trueanswer)) {
$trueanswer = $question->options->answers[$question->options->trueanswer];
$draftid = file_get_submitted_draft_itemid('trueanswer');
$answerid = $question->options->trueanswer;
$text = $trueanswer->feedback;
$question->correctanswer = ($trueanswer->fraction != 0);
$question->feedbacktrue = $trueanswer->feedback;
$question->feedbackfalse = $question->options->answers[$question->options->falseanswer]->feedback;
$question->feedbacktrue = array();
$question->feedbacktrue['text'] = $trueanswer->feedback;
$question->feedbacktrue['format'] = $trueanswer->feedbackformat;
$question->feedbacktrue['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'question', // component
'answerfeedback', // filarea
!empty($answerid)?(int)$answerid:null, // itemid
$this->fileoptions, // options
$text // text
);
$question->feedbacktrue['itemid'] = $draftid;
}
if (!empty($question->options->falseanswer)) {
$falseanswer = $question->options->answers[$question->options->falseanswer];
$draftid = file_get_submitted_draft_itemid('falseanswer');
$answerid = $question->options->falseanswer;
$text = $falseanswer->feedback;
$question->correctanswer = ($falseanswer->fraction != 0);
$question->feedbackfalse = array();
$question->feedbackfalse['text'] = $falseanswer->feedback;
$question->feedbackfalse['format'] = $falseanswer->feedbackformat;
$question->feedbackfalse['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'question', // component
'answerfeedback', // filarea
!empty($answerid)?(int)$answerid:null, // itemid
$this->fileoptions, // options
$text // text
);
$question->feedbackfalse['itemid'] = $draftid;
}
parent::set_data($question);
}

View file

@ -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 qtype
* @subpackage qtype_truefalse
* @copyright The Open Unviersity
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function qtype_truefalse_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_truefalse', $filearea, $args, $forcedownload);
}

View file

@ -1,5 +1,22 @@
<?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/>.
defined('MOODLE_INTERNAL') || die();
/////////////////
/// TRUEFALSE ///
/////////////////
@ -24,34 +41,52 @@ class question_truefalse_qtype extends default_questiontype {
$oldanswers = array();
}
$feedbacktext = $question->feedbacktrue['text'];
$feedbackitemid = $question->feedbacktrue['itemid'];
$feedbackformat = $question->feedbacktrue['format'];
// Save answer 'True'
if ($true = array_shift($oldanswers)) { // Existing answer, so reuse it
$true->answer = get_string("true", "quiz");
$true->fraction = $question->correctanswer;
$true->feedback = $question->feedbacktrue;
$true->feedback = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $true->id, self::$fileoptions, $feedbacktext);
$true->feedbackformat = $feedbackformat;
$DB->update_record("question_answers", $true);
} else {
unset($true);
$true = new stdclass;
$true->answer = get_string("true", "quiz");
$true->question = $question->id;
$true->fraction = $question->correctanswer;
$true->feedback = $question->feedbacktrue;
$true->feedback = '';
$true->feedbackformat = $feedbackformat;
$true->id = $DB->insert_record("question_answers", $true);
$feedbacktext = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $true->id, self::$fileoptions, $feedbacktext);
$DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$true->id));
}
$feedbacktext = $question->feedbackfalse['text'];
$feedbackitemid = $question->feedbackfalse['itemid'];
$feedbackformat = $question->feedbackfalse['format'];
// Save answer 'False'
if ($false = array_shift($oldanswers)) { // Existing answer, so reuse it
$false->answer = get_string("false", "quiz");
$false->fraction = 1 - (int)$question->correctanswer;
$false->feedback = $question->feedbackfalse;
$false->feedback = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $false->id, self::$fileoptions, $feedbacktext);
$false->feedbackformat = $feedbackformat;
$DB->update_record("question_answers", $false);
} else {
unset($false);
$false = new stdclass;
$false->answer = get_string("false", "quiz");
$false->question = $question->id;
$false->fraction = 1 - (int)$question->correctanswer;
$false->feedback = $question->feedbackfalse;
$false->feedback = '';
$false->feedbackformat = $feedbackformat;
$false->id = $DB->insert_record("question_answers", $false);
$feedbacktext = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $false->id, self::$fileoptions, $feedbacktext);
$DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$false->id));
}
// delete any leftover old answer records (there couldn't really be any, but who knows)
@ -134,9 +169,9 @@ class question_truefalse_qtype extends default_questiontype {
/**
* Prints the main content of the question including any interactions
*/
function print_question_formulation_and_controls(&$question, &$state,
$cmoptions, $options) {
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG;
$context = $this->get_context_by_category_id($question->category);
$readonly = $options->readonly ? ' disabled="disabled"' : '';
@ -148,7 +183,6 @@ class question_truefalse_qtype extends default_questiontype {
$questiontext = format_text($question->questiontext,
$question->questiontextformat,
$formatoptions, $cmoptions->course);
$image = get_question_image($question);
$answers = &$question->options->answers;
$trueanswer = &$answers[$question->options->trueanswer];
@ -198,12 +232,33 @@ class question_truefalse_qtype extends default_questiontype {
$feedback = '';
if ($options->feedback and isset($answers[$response])) {
$chosenanswer = $answers[$response];
$chosenanswer->feedback = quiz_rewrite_question_urls($chosenanswer->feedback, 'pluginfile.php', $context->id, 'question', 'answerfeedback', array($state->attempt, $state->question), $chosenanswer->id);
$feedback = format_text($chosenanswer->feedback, true, $formatoptions, $cmoptions->course);
}
include("$CFG->dirroot/question/type/truefalse/display.html");
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
if ($component == 'question' && $filearea == 'answerfeedback') {
$answerid = reset($args); // itemid is answer id.
$answers = &$question->options->answers;
if (isset($state->responses[''])) {
$response = $state->responses[''];
} else {
$response = '';
}
return $options->feedback && isset($answers[$response]) && $answerid == $response;
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
}
function grade_responses(&$question, &$state, $cmoptions) {
if (isset($state->responses['']) && isset($question->options->answers[$state->responses['']])) {
$state->raw_grade = $question->options->answers[$state->responses['']]->fraction * $question->maxgrade;
@ -363,6 +418,34 @@ class question_truefalse_qtype extends default_questiontype {
return $this->save_question($question, $form, $course);
}
/**
* 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();
}
}
}
}
}
//// END OF CLASS ////
@ -370,4 +453,3 @@ class question_truefalse_qtype extends default_questiontype {
//// INITIATION - Without this line the question type is not in use... ///
//////////////////////////////////////////////////////////////////////////
question_register_questiontype(new question_truefalse_qtype());

View file

@ -6,7 +6,7 @@
// This is compared against the values stored in the database to determine
// whether upgrades should be performed (see lib/db/*.php)
$version = 2010080307; // YYYYMMDD = date of the last version bump
$version = 2010080901; // YYYYMMDD = date of the last version bump
// XX = daily increments
$release = '2.0 Preview 4+ (Build: 20100810)'; // Human-friendly version name