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" ?> <?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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd" 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="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="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="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="info" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="infoformat"/>
<FIELD NAME="stamp" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="info" NEXT="parent"/> <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="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"/> <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="999" SEQUENCE="false" PREVIOUS="parent"/>
</FIELDS> </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="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="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="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="questiontextformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="questiontext" NEXT="generalfeedback"/>
<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="questiontextformat" NEXT="generalfeedbackformat"/>
<FIELD NAME="generalfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="to store the question feedback" PREVIOUS="image" NEXT="defaultgrade"/> <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="generalfeedback" NEXT="penalty"/> <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="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="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"/> <FIELD NAME="length" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="qtype" NEXT="stamp"/>
@ -1293,9 +1294,11 @@
<FIELDS> <FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="question"/> <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="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="answer" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="question" NEXT="answerformat"/>
<FIELD NAME="fraction" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="7" PREVIOUS="answer" NEXT="feedback"/> <FIELD NAME="answerformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="answer" NEXT="fraction"/>
<FIELD NAME="feedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="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> </FIELDS>
<KEYS> <KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/> <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="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="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="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="manualcomment" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="sumpenalty" NEXT="manualcommentformat"/>
<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="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> </FIELDS>
<KEYS> <KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="attemptid"/> <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"/> <KEY NAME="handlerid" TYPE="foreign" FIELDS="handlerid" REFTABLE="events_handlers" REFFIELDS="id" PREVIOUS="queuedeventid"/>
</KEYS> </KEYS>
</TABLE> </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> <FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" COMMENT="id of the table" NEXT="courseid"/> <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"/> <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="Mostly these are defined site wide ie NULL" PREVIOUS="id" NEXT="shortname"/>
@ -2709,4 +2713,4 @@
</KEYS> </KEYS>
</TABLE> </TABLE>
</TABLES> </TABLES>
</XMLDB> </XMLDB>

View file

@ -4911,7 +4911,170 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
upgrade_main_savepoint(true, 2010080305); 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; return true;
} }

View file

@ -851,18 +851,31 @@ function question_delete_activity($cm, $feedback=true) {
* *
* @global object * @global object
* @param string $questionids a comma-separated list of question ids. * @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) { function question_move_questions_to_category($questionids, $newcategoryid) {
global $DB; global $DB, $QTYPES;
$result = true; $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. // 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. // 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. // TODO Deal with datasets.
@ -1080,6 +1093,7 @@ function question_load_states(&$questions, &$states, $cmoptions, $attempt, $last
$states[$qid]->last_graded = clone($states[$qid]); $states[$qid]->last_graded = clone($states[$qid]);
} }
} else { } else {
if ($lastattemptid) { if ($lastattemptid) {
// If the new attempt is to be based on this previous attempt. // 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 // Find the responses from the previous attempt and save them to the new session
@ -2091,7 +2105,7 @@ function question_init_qengine_js() {
$module = array( $module = array(
'name' => 'core_question_flags', 'name' => 'core_question_flags',
'fullpath' => '/question/flags.js', 'fullpath' => '/question/flags.js',
'requires' => array('base', 'dom', 'event-delegate', 'io-base'), 'requires' => array('base', 'dom', 'event-delegate', 'io-base'),
); );
$actionurl = $CFG->wwwroot . '/question/toggleflag.php'; $actionurl = $CFG->wwwroot . '/question/toggleflag.php';
$flagattributes = array( $flagattributes = array(
@ -2162,9 +2176,9 @@ function question_get_editing_head_contributions($question) {
* @param object $cmoptions The options specified by the course module * @param object $cmoptions The options specified by the course module
* @param object $options An object specifying the rendering options. * @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; 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 * Saves question options
@ -3191,4 +3205,124 @@ class question_edit_contexts {
print_error('nopermissions', '', '', 'access question edit tab '.$tabname); print_error('nopermissions', '', '', 'access question edit tab '.$tabname);
} }
} }
} }
/**
* Rewrite question url, file_rewrite_pluginfile_urls always build url by
* $file/$contextid/$component/$filearea/$itemid/$pathname_in_text, so we cannot add
* extra questionid and attempted in url by it, so we create quiz_rewrite_question_urls
* to build url here
*
* @param string $text text being processed
* @param string $file the php script used to serve files
* @param int $contextid
* @param string $component component
* @param string $filearea filearea
* @param array $ids other IDs will be used to check file permission
* @param int $itemid
* @param array $options
* @return string
*/
function quiz_rewrite_question_urls($text, $file, $contextid, $component, $filearea, array $ids, $itemid, array $options=null) {
global $CFG;
$options = (array)$options;
if (!isset($options['forcehttps'])) {
$options['forcehttps'] = false;
}
if (!$CFG->slasharguments) {
$file = $file . '?file=';
}
$baseurl = "$CFG->wwwroot/$file/$contextid/$component/$filearea/";
if (!empty($ids)) {
$baseurl .= (implode('/', $ids) . '/');
}
if ($itemid !== null) {
$baseurl .= "$itemid/";
}
if ($options['forcehttps']) {
$baseurl = str_replace('http://', 'https://', $baseurl);
}
return str_replace('@@PLUGINFILE@@/', $baseurl, $text);
}
/**
* Called by pluginfile.php to serve files related to the 'question' core
* component and for files belonging to qtypes.
*
* For files that relate to questions in a question_attempt, then we delegate to
* a function in the component that owns the attempt (for example in the quiz,
* or in core question preview) to get necessary inforation.
*
* (Note that, at the moment, all question file areas relate to questions in
* attempts, so the If at the start of the last paragraph is always true.)
*
* Does not return, either calls send_file_not_found(); or serves the file.
*
* @param object $course course settings object
* @param object $context context object
* @param string $component the name of the component we are serving files for.
* @param string $filearea the name of the file area.
* @param array $args the remaining bits of the file path.
* @param bool $forcedownload whether the user must be forced to download the file.
*/
function question_pluginfile($course, $context, $component, $filearea, $args, $forcedownload) {
global $DB, $CFG;
$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; 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. * 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 $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() { public function is_preview() {
return $this->attempt->preview; return $this->attempt->preview;
} }
@ -629,10 +633,12 @@ class quiz_attempt extends quiz {
/** /**
* Wrapper that calls get_render_options with the appropriate arguments. * 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. * @return object the render options for this user on this attempt.
*/ */
public function get_render_options($state) { public function get_render_options($questionid) {
return quiz_get_renderoptions($this->quiz, $this->attempt, $this->context, $state); 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_EVENTCLOSEANDGRADE:
case QUESTION_EVENTCLOSE: case QUESTION_EVENTCLOSE:
case QUESTION_EVENTMANUALGRADE: 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) { if ($options->scores && $this->questions[$questionid]->maxgrade > 0) {
return question_get_feedback_class($state->last_graded->raw_grade / return question_get_feedback_class($state->last_graded->raw_grade /
$this->questions[$questionid]->maxgrade); $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. * @return string the formatted grade, to the number of decimal places specified by the quiz.
*/ */
public function get_question_score($questionid) { public function get_question_score($questionid) {
$options = $this->get_render_options($this->states[$questionid]); $options = $this->get_render_options($questionid);
if ($options->scores) { if ($options->scores) {
return quiz_format_question_grade($this->quiz, $this->states[$questionid]->last_graded->grade); return quiz_format_question_grade($this->quiz, $this->states[$questionid]->last_graded->grade);
} else { } else {
@ -805,7 +811,7 @@ class quiz_attempt extends quiz {
if ($reviewing) { if ($reviewing) {
$options = $this->get_review_options(); $options = $this->get_review_options();
} else { } else {
$options = $this->get_render_options($this->states[$id]); $options = $this->get_render_options($id);
} }
if ($thispageurl) { if ($thispageurl) {
$this->quiz->thispageurl = $thispageurl; $this->quiz->thispageurl = $thispageurl;
@ -816,6 +822,20 @@ class quiz_attempt extends quiz {
$this->quiz, $options); $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. * 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" ?> <?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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd" xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
> >
@ -100,8 +100,9 @@
<FIELDS> <FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="quizid"/> <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="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="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="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="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"/> <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> </FIELDS>
<KEYS> <KEYS>

View file

@ -341,6 +341,22 @@ function xmldb_quiz_upgrade($oldversion) {
upgrade_mod_savepoint(true, 2010051800, 'quiz'); 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; return true;
} }

View file

@ -155,7 +155,7 @@
$grade = get_string('outofshort', 'quiz', $a); $grade = get_string('outofshort', 'quiz', $a);
} }
if ($alloptions->overallfeedback) { 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; $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) { function quiz_add_instance($quiz) {
global $DB; global $DB;
$cmid = $quiz->coursemodule;
// Process the options from the form. // Process the options from the form.
$quiz->created = time(); $quiz->created = time();
@ -982,10 +983,10 @@ function quiz_process_options(&$quiz) {
if (isset($quiz->feedbacktext)) { if (isset($quiz->feedbacktext)) {
// Clean up the boundary text. // Clean up the boundary text.
for ($i = 0; $i < count($quiz->feedbacktext); $i += 1) { for ($i = 0; $i < count($quiz->feedbacktext); $i += 1) {
if (empty($quiz->feedbacktext[$i])) { if (empty($quiz->feedbacktext[$i]['text'])) {
$quiz->feedbacktext[$i] = ''; $quiz->feedbacktext[$i]['text'] = '';
} else { } 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) { 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); return get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1);
} }
} }
@ -1145,17 +1146,25 @@ function quiz_process_options(&$quiz) {
*/ */
function quiz_after_add_or_update($quiz) { function quiz_after_add_or_update($quiz) {
global $DB; 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 // Save the feedback
$DB->delete_records('quiz_feedback', array('quizid' => $quiz->id)); $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 = new stdClass;
$feedback->quizid = $quiz->id; $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->mingrade = $quiz->feedbackboundaries[$i];
$feedback->maxgrade = $quiz->feedbackboundaries[$i - 1]; $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. // Update the events relating to this quiz.
@ -1421,23 +1430,44 @@ function quiz_reset_userdata($data) {
* @param int $questionid int question id * @param int $questionid int question id
* @return boolean to indicate access granted or denied * @return boolean to indicate access granted or denied
*/ */
function quiz_check_file_access($attemptuniqueid, $questionid) { function quiz_check_file_access($attemptuniqueid, $questionid, $context = null) {
global $USER, $DB; 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)); $attempt = $DB->get_record('quiz_attempts', array('uniqueid' => $attemptuniqueid));
$quiz = $DB->get_record('quiz', array('id' => $attempt->quiz)); $attemptobj = quiz_attempt::create($attempt->id);
$context = get_context_instance(CONTEXT_COURSE, $quiz->course);
// access granted if the current user submitted this file // does question exist?
if ($attempt->userid == $USER->id) { if (!$question = $DB->get_record('question', array('id' => $questionid))) {
return true; return false;
// access granted if the current user has permission to grade quizzes in this course
} else if (has_capability('mod/quiz:viewreports', $context) || has_capability('mod/quiz:grade', $context)) {
return true;
} }
// otherwise, this user does not have permission if ($context === null) {
return false; $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(); 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. * @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. * @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; global $DB;
$feedback = $DB->get_field_select('quiz_feedback', 'feedbacktext',
"quizid = ? AND mingrade <= ? AND $grade < maxgrade", array($quizid, $grade));
if (empty($feedback)) { $feedback = $DB->get_record_select('quiz_feedback', "quizid = ? AND mingrade <= ? AND $grade < maxgrade", array($quiz->id, $grade));
$feedback = '';
if (empty($feedback->feedbacktext)) {
$feedback->feedbacktext = '';
} }
// Clean the text, ready for display. // Clean the text, ready for display.
$formatoptions = new stdClass; $formatoptions = new stdClass;
$formatoptions->noclean = true; $formatoptions->noclean = true;
$feedback = format_text($feedback, FORMAT_MOODLE, $formatoptions); $feedbacktext = file_rewrite_pluginfile_urls($feedback->feedbacktext, 'pluginfile.php', $context->id, 'mod_quiz', 'feedback', $feedback->id);
$feedbacktext = format_text($feedbacktext, $feedback->feedbacktextformat, $formatoptions);
return $feedback; return $feedbacktext;
} }
/** /**
@ -1263,4 +1264,4 @@ function quiz_get_js_module() {
array('flagged', 'question'), array('flagged', 'question'),
), ),
); );
} }

View file

@ -296,7 +296,7 @@ class mod_quiz_mod_form extends moodleform_mod {
$mform->addElement('static', 'gradeboundarystatic1', get_string('gradeboundary', 'quiz'), '100%'); $mform->addElement('static', 'gradeboundarystatic1', get_string('gradeboundary', 'quiz'), '100%');
$repeatarray = array(); $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); $mform->setType('feedbacktext', PARAM_RAW);
$repeatarray[] = &MoodleQuickForm::createElement('text', 'feedbackboundaries', get_string('gradeboundary', 'quiz'), array('size' => 10)); $repeatarray[] = &MoodleQuickForm::createElement('text', 'feedbackboundaries', get_string('gradeboundary', 'quiz'), array('size' => 10));
$mform->setType('feedbackboundaries', PARAM_NOTAGS); $mform->setType('feedbackboundaries', PARAM_NOTAGS);
@ -313,7 +313,7 @@ class mod_quiz_mod_form extends moodleform_mod {
get_string('addmoreoverallfeedbacks', 'quiz'), true); get_string('addmoreoverallfeedbacks', 'quiz'), true);
// Put some extra elements in before the button // 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'); $mform->insertElementBefore($insertEl, 'boundary_add_fields');
$insertEl = &MoodleQuickForm::createElement('static', 'gradeboundarystatic2', get_string('gradeboundary', 'quiz'), '0%'); $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)) { if (count($this->_feedbacks)) {
$key = 0; $key = 0;
foreach ($this->_feedbacks as $feedback){ 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) { if ($feedback->mingrade > 0) {
$default_values['feedbackboundaries['.$key.']'] = (100.0 * $feedback->mingrade / $default_values['grade']) . '%'; $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) { 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); $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 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) { if ($options->overallfeedback && $feedback) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('feedback', 'quiz') . $rows[] = '<tr><th scope="row" class="cell">' . get_string('feedback', 'quiz') .
'</th><td class="cell">' . $feedback . '</td></tr>'; '</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 // 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->requires = 2010080300; // Requires this Moodle version
$module->cron = 0; // How often should cron check this module (seconds)? $module->cron = 0; // How often should cron check this module (seconds)?

View file

@ -277,7 +277,7 @@
if ($feedbackcolumn && $attempt->timefinish > 0) { if ($feedbackcolumn && $attempt->timefinish > 0) {
if ($attemptoptions->overallfeedback) { if ($attemptoptions->overallfeedback) {
$row[] = quiz_feedback_for_grade($attemptgrade, $quiz->id); $row[] = quiz_feedback_for_grade($attemptgrade, $quiz, $context, $cm);
} else { } else {
$row[] = ''; $row[] = '';
} }
@ -330,7 +330,7 @@
} }
if ($feedbackcolumn) { if ($feedbackcolumn) {
$resultinfo .= $OUTPUT->heading(get_string('overallfeedback', 'quiz'), 3, 'main'); $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) { if ($resultinfo) {

View file

@ -630,6 +630,12 @@ if ($component === 'blog') {
send_file_not_found(); 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) { } else if (strpos($component, 'mod_') === 0) {
$modname = substr($component, 4); $modname = substr($component, 4);

View file

@ -12,7 +12,7 @@ require_once(dirname(__FILE__) . '/../config.php');
require_once(dirname(__FILE__) . '/editlib.php'); require_once(dirname(__FILE__) . '/editlib.php');
require_once($CFG->dirroot.'/question/contextmoveq_form.php'); require_once($CFG->dirroot.'/question/contextmoveq_form.php');
$ids = required_param('ids',PARAM_SEQUENCE); // question ids $ids = required_param('ids', PARAM_SEQUENCE); // question ids
if (!$cmid = optional_param('cmid', 0, PARAM_INT)){ if (!$cmid = optional_param('cmid', 0, PARAM_INT)){
$courseid = required_param('courseid', PARAM_INT); $courseid = required_param('courseid', PARAM_INT);
@ -165,6 +165,9 @@ if ($contextmoveform->is_cancelled()){
if (!question_move_questions_to_category($ids, $tocat->id)) { if (!question_move_questions_to_category($ids, $tocat->id)) {
print_error('errormovingquestions', 'question', $returnurl, $ids); print_error('errormovingquestions', 'question', $returnurl, $ids);
} }
if ($returnurl) {
$returnurl = new moodle_url('/' . $returnurl);
}
redirect($returnurl); redirect($returnurl);
} }

View file

@ -24,11 +24,11 @@
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
/** /**
* Page to edit the question bank * Page to edit the question bank
* *
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank * @package questionbank
*//** */ */
require_once("../config.php"); require_once("../config.php");
require_once("editlib.php"); require_once("editlib.php");
@ -67,7 +67,7 @@
$context = $contexts->lowest(); $context = $contexts->lowest();
$streditingquestions = get_string('editquestions', "quiz"); $streditingquestions = get_string('editquestions', "quiz");
$PAGE->set_title($streditingquestions); $PAGE->set_title($streditingquestions);
$PAGE->set_heading($COURSE->fullname); $PAGE->set_heading($COURSE->fullname);
echo $OUTPUT->header(); echo $OUTPUT->header();

View file

@ -1402,7 +1402,7 @@ class question_bank_view {
$questionids[] = $key; $questionids[] = $key;
} }
} }
if ($questionids){ if ($questionids) {
list($usql, $params) = $DB->get_in_or_equal($questionids); list($usql, $params) = $DB->get_in_or_equal($questionids);
$sql = "SELECT q.*, c.contextid FROM {question} q, {question_categories} c WHERE q.id $usql AND c.id = q.category"; $sql = "SELECT q.*, c.contextid FROM {question} q, {question_categories} c WHERE q.id $usql AND c.id = q.category";
if (!$questions = $DB->get_records_sql($sql, $params)){ if (!$questions = $DB->get_records_sql($sql, $params)){
@ -1426,8 +1426,9 @@ class question_bank_view {
} else { } else {
$returnurl = str_replace($CFG->wwwroot . '/', '', $returnurl); $returnurl = str_replace($CFG->wwwroot . '/', '', $returnurl);
$movecontexturl = new moodle_url('/question/contextmoveq.php', $movecontexturl = new moodle_url('/question/contextmoveq.php',
array('returnurl' => $returnurl, 'ids' => $questionids, array('returnurl' => $returnurl,
'tocatid' => $tocategoryid)); 'ids' => implode(',', $questionids),
'tocatid' => $tocategoryid));
if (!empty($cm->id)){ if (!empty($cm->id)){
$movecontexturl->param('cmid', $cm->id); $movecontexturl->param('cmid', $cm->id);
} else { } else {

View file

@ -862,6 +862,7 @@ class qformat_default {
* performs the conversion. * performs the conversion.
*/ */
function format_question_text($question) { function format_question_text($question) {
global $DB;
$formatoptions = new stdClass; $formatoptions = new stdClass;
$formatoptions->noclean = true; $formatoptions->noclean = true;
$formatoptions->para = false; $formatoptions->para = false;
@ -870,10 +871,7 @@ class qformat_default {
} else { } else {
$format = $question->questiontextformat; $format = $question->questiontextformat;
} }
return format_text($question->questiontext, $format, $formatoptions); $text = $question->questiontext;
return format_text(html_to_text($text), $format, $formatoptions);
} }
} }

View file

@ -211,7 +211,7 @@
$PAGE->set_title($strpreview); $PAGE->set_title($strpreview);
$PAGE->set_heading($COURSE->fullname); $PAGE->set_heading($COURSE->fullname);
echo $OUTPUT->header(); echo $OUTPUT->header();
if (!empty($quizid)) { if (!empty($quizid)) {
echo '<p class="quemodname">'.get_string('modulename', 'quiz') . ': '; echo '<p class="quemodname">'.get_string('modulename', 'quiz') . ': ';
p(format_string($quiz->name)); p(format_string($quiz->name));
@ -219,7 +219,7 @@
} }
$number = 1; $number = 1;
echo '<form method="post" action="'.$url->out_omit_querystring().'" enctype="multipart/form-data" id="responseform">', "\n"; 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 '<div class="controls">';
echo html_writer::input_hidden_params($url); 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); $thiscontext = get_context_instance(CONTEXT_MODULE, $cmid);
} elseif ($courseid) { } elseif ($courseid) {
require_login($courseid, false); require_login($courseid, false);
$PAGE->set_pagelayout('course');
$thiscontext = get_context_instance(CONTEXT_COURSE, $courseid); $thiscontext = get_context_instance(CONTEXT_COURSE, $courseid);
$module = null; $module = null;
$cm = null; $cm = null;
@ -106,7 +107,7 @@ if ($id) {
} else if ($categoryid) { } else if ($categoryid) {
// Category, but no qtype. They probably came from the addquestion.php // Category, but no qtype. They probably came from the addquestion.php
// script without choosing a question type. Send them back. // script without choosing a question type. Send them back.
$addurl = new moodle_url('/question/addquestion.php', $url->params()); $addurl = new moodle_url('/question/addquestion.php', $url->params());
$addurl->param('validationerror', 1); $addurl->param('validationerror', 1);
redirect($addurl); redirect($addurl);
@ -151,6 +152,8 @@ if ($id) {
} else { // creating a new question } else { // creating a new question
require_capability('moodle/question:add', $categorycontext); require_capability('moodle/question:add', $categorycontext);
$formeditable = true; $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->repeatelements = true;
$question->formoptions->movecontext = false; $question->formoptions->movecontext = false;
} }
@ -189,7 +192,9 @@ if ($cm !== null){
} else { } else {
$toform->courseid = $COURSE->id; $toform->courseid = $COURSE->id;
} }
$toform->inpopup = $inpopup; $toform->inpopup = $inpopup;
$mform->set_data($toform); $mform->set_data($toform);
if ($mform->is_cancelled()){ if ($mform->is_cancelled()){

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?> <?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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd" 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="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="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="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="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any correct response." PREVIOUS="shuffleanswers" NEXT="correctfeedbackformat"/>
<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="correctfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="correctfeedback" NEXT="partiallycorrectfeedback"/>
<FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="partiallycorrectfeedback" NEXT="answernumbering"/> <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="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="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> </FIELDS>
<KEYS> <KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/> <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'); 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; return true;
} }

View file

@ -1,4 +1,20 @@
<?php <?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. * Defines the editing form for the calculated question type.
* *
@ -19,12 +35,12 @@ class question_edit_calculated_form extends question_edit_form {
* @var question_calculated_qtype * @var question_calculated_qtype
*/ */
public $qtypeobj; public $qtypeobj;
public $questiondisplay ; public $questiondisplay;
public $activecategory ; public $activecategory;
public $categorychanged = false ; public $categorychanged = false;
public $initialname = ''; public $initialname = '';
public $reload = false ; public $reload = false;
function question_edit_calculated_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){ function question_edit_calculated_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
global $QTYPES, $SESSION, $CFG, $DB; global $QTYPES, $SESSION, $CFG, $DB;
$this->question = $question; $this->question = $question;
@ -42,20 +58,20 @@ class question_edit_calculated_form extends question_edit_form {
$regs= array(); $regs= array();
if(preg_match('~#\{([^[:space:]]*)#~',$question->name , $regs)){ if(preg_match('~#\{([^[:space:]]*)#~',$question->name , $regs)){
$question->name = str_replace($regs[0], '', $question->name); $question->name = str_replace($regs[0], '', $question->name);
}; };
} }
}else { }else {
} }
parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable); parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
} }
function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) { function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
// $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption); // $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
$repeated = array(); $repeated = array();
$repeated[] =& $mform->createElement('header', 'answerhdr', $label); $repeated[] =& $mform->createElement('header', 'answerhdr', $label);
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50)); $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions); $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'), $repeated[] =& $mform->createElement('editor', 'feedback', get_string('feedback', 'quiz'), null, $this->editoroptions);
array('course' => $this->coursefilesid));
$repeatedoptions['answer']['type'] = PARAM_RAW; $repeatedoptions['answer']['type'] = PARAM_RAW;
$repeatedoptions['fraction']['default'] = 0; $repeatedoptions['fraction']['default'] = 0;
$answersoption = 'answers'; $answersoption = 'answers';
@ -63,8 +79,8 @@ class question_edit_calculated_form extends question_edit_form {
$mform->setType('answer', PARAM_NOTAGS); $mform->setType('answer', PARAM_NOTAGS);
$addrepeated = array(); $addrepeated = array();
$addrepeated[] =& $mform->createElement('text', 'tolerance', get_string('tolerance', 'qtype_calculated')); $addrepeated[] =& $mform->createElement('text', 'tolerance', get_string('tolerance', 'qtype_calculated'));
$addrepeated[] =& $mform->createElement('select', 'tolerancetype', get_string('tolerancetype', 'quiz'), $this->qtypeobj->tolerance_types()); $addrepeated[] =& $mform->createElement('select', 'tolerancetype', get_string('tolerancetype', 'quiz'), $this->qtypeobj->tolerance_types());
$repeatedoptions['tolerance']['type'] = PARAM_NUMBER; $repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
$repeatedoptions['tolerance']['default'] = 0.01; $repeatedoptions['tolerance']['default'] = 0.01;
@ -74,7 +90,7 @@ class question_edit_calculated_form extends question_edit_form {
$answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz')); $answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz'));
$addrepeated[] =& $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats); $addrepeated[] =& $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
array_splice($repeated, 3, 0, $addrepeated); array_splice($repeated, 3, 0, $addrepeated);
$repeated[1]->setLabel(get_string('correctanswerformula', 'quiz').'='); $repeated[1]->setLabel(get_string('correctanswerformula', 'quiz').'=');
return $repeated; return $repeated;
} }
@ -87,8 +103,8 @@ class question_edit_calculated_form extends question_edit_form {
function definition_inner(&$mform) { function definition_inner(&$mform) {
global $QTYPES; global $QTYPES;
$this->qtypeobj =& $QTYPES[$this->qtype()]; $this->qtypeobj =& $QTYPES[$this->qtype()];
// echo code left for testing period // echo code left for testing period
// echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>"; // echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
$label = get_string('sharedwildcards', 'qtype_calculated'); $label = get_string('sharedwildcards', 'qtype_calculated');
$mform->addElement('hidden', 'initialcategory', 1); $mform->addElement('hidden', 'initialcategory', 1);
$mform->addElement('hidden', 'reload', 1); $mform->addElement('hidden', 'reload', 1);
@ -100,43 +116,42 @@ class question_edit_calculated_form extends question_edit_form {
}; };
$addfieldsname='updatecategory'; $addfieldsname='updatecategory';
$addstring=get_string("updatecategory", "qtype_calculated"); $addstring=get_string("updatecategory", "qtype_calculated");
$mform->registerNoSubmitButton($addfieldsname); $mform->registerNoSubmitButton($addfieldsname);
$mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory'); $mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
$mform->registerNoSubmitButton('createoptionbutton'); $mform->registerNoSubmitButton('createoptionbutton');
//editing as regular //editing as regular
$mform->setType('single', PARAM_INT); $mform->setType('single', PARAM_INT);
$mform->addElement('hidden','shuffleanswers', '1'); $mform->addElement('hidden','shuffleanswers', '1');
$mform->setType('shuffleanswers', PARAM_INT); $mform->setType('shuffleanswers', PARAM_INT);
$mform->addElement('hidden','answernumbering', 'abc'); $mform->addElement('hidden','answernumbering', 'abc');
$mform->setType('answernumbering', PARAM_SAFEDIR); $mform->setType('answernumbering', PARAM_SAFEDIR);
$creategrades = get_grade_options(); $creategrades = get_grade_options();
$this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'), $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'), $creategrades->gradeoptions, 1, 1);
$creategrades->gradeoptions, 1, 1);
$repeated = array(); $repeated = array();
$QTYPES['numerical']->add_units_options($mform,$this); $QTYPES['numerical']->add_units_options($mform,$this);
$QTYPES['numerical']->add_units_elements($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) { 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); $mform->setType($feedbackname, PARAM_RAW);
} }
//hidden elements //hidden elements
$mform->addElement('hidden', 'synchronize', ''); $mform->addElement('hidden', 'synchronize', '');
$mform->setType('synchronize', PARAM_INT); $mform->setType('synchronize', PARAM_INT);
$mform->addElement('hidden', 'wizard', 'datasetdefinitions'); $mform->addElement('hidden', 'wizard', 'datasetdefinitions');
$mform->setType('wizard', PARAM_ALPHA); $mform->setType('wizard', PARAM_ALPHA);
} }
function set_data($question) { function data_preprocessing($question) {
global $QTYPES; global $QTYPES;
$default_values = array(); $default_values = array();
@ -145,26 +160,58 @@ class question_edit_calculated_form extends question_edit_form {
if (count($answers)) { if (count($answers)) {
$key = 0; $key = 0;
foreach ($answers as $answer){ foreach ($answers as $answer){
$draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
$default_values['answer['.$key.']'] = $answer->answer; $default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction; $default_values['fraction['.$key.']'] = $answer->fraction;
$default_values['tolerance['.$key.']'] = $answer->tolerance; $default_values['tolerance['.$key.']'] = $answer->tolerance;
$default_values['tolerancetype['.$key.']'] = $answer->tolerancetype; $default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
$default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength; $default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
$default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat; $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++; $key++;
} }
} }
$default_values['synchronize'] = $question->options->synchronize ; $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)){ if (isset($question->options->single)){
$default_values['single'] = $question->options->single; $default_values['single'] = $question->options->single;
$default_values['answernumbering'] = $question->options->answernumbering; $default_values['answernumbering'] = $question->options->answernumbering;
$default_values['shuffleanswers'] = $question->options->shuffleanswers; $default_values['shuffleanswers'] = $question->options->shuffleanswers;
$default_values['correctfeedback'] = $question->options->correctfeedback; //$default_values['correctfeedback'] = $question->options->correctfeedback;
$default_values['partiallycorrectfeedback'] = $question->options->partiallycorrectfeedback; //$default_values['partiallycorrectfeedback'] = $question->options->partiallycorrectfeedback;
$default_values['incorrectfeedback'] = $question->options->incorrectfeedback; //$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['submitbutton'] = get_string('nextpage', 'qtype_calculated');
$default_values['makecopy'] = get_string('makecopynextpage', 'qtype_calculated'); $default_values['makecopy'] = get_string('makecopynextpage', 'qtype_calculated');
@ -173,16 +220,16 @@ class question_edit_calculated_form extends question_edit_form {
update category button. The value can be obtain by update category button. The value can be obtain by
$qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0]; $qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
but is coded using existing functions but is coded using existing functions
*/ */
$qu = new stdClass; $qu = new stdClass;
$el = new stdClass; $el = new stdClass;
/* no need to call elementExists() here */ /* no need to call elementExists() here */
if ($this->_form->elementExists('category')){ if ($this->_form->elementExists('category')){
$el=$this->_form->getElement('category'); $el=$this->_form->getElement('category');
} else { } else {
$el=$this->_form->getElement('categorymoveto'); $el=$this->_form->getElement('categorymoveto');
} }
if($value =$el->getSelected()) { if($value =$el->getSelected()) {
$qu->category =$value[0]; $qu->category =$value[0];
}else { }else {
$qu->category=$question->category;// on load $question->category is set by question.php $qu->category=$question->category;// on load $question->category is set by question.php
@ -191,7 +238,7 @@ class question_edit_calculated_form extends question_edit_form {
$this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ; $this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ;
$question = (object)((array)$question + $default_values); $question = (object)((array)$question + $default_values);
parent::set_data($question); return $question;
} }
function qtype() { function qtype() {
@ -200,21 +247,21 @@ class question_edit_calculated_form extends question_edit_form {
function validation($data, $files) { function validation($data, $files) {
global $QTYPES; global $QTYPES;
// echo code left for testing period // echo code left for testing period
// echo "<p>question <pre>";print_r($this->question);echo "</pre></p>"; // echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
// echo "<p>data <pre>";print_r($data);echo "</pre></p>"; // echo "<p>data <pre>";print_r($data);echo "</pre></p>";
$errors = parent::validation($data, $files); $errors = parent::validation($data, $files);
//verifying for errors in {=...} in question text; //verifying for errors in {=...} in question text;
$qtext = ""; $qtext = "";
$qtextremaining = $data['questiontext'] ; $qtextremaining = $data['questiontext']['text'];
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']); $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
foreach ($possibledatasets as $name => $value) { foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining); $qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
} }
// echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets); // echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2); $qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0]; $qtext =$qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1]; $qtextremaining = $qtextsplits[1];
@ -229,45 +276,45 @@ class question_edit_calculated_form extends question_edit_form {
$answers = $data['answer']; $answers = $data['answer'];
$answercount = 0; $answercount = 0;
$maxgrade = false; $maxgrade = false;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']); $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
$mandatorydatasets = array(); $mandatorydatasets = array();
foreach ($answers as $key => $answer){ foreach ($answers as $key => $answer){
$mandatorydatasets += $this->qtypeobj->find_dataset_names($answer); $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
} }
if ( count($mandatorydatasets )==0){ if ( count($mandatorydatasets )==0){
// $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent'); // $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
foreach ($answers as $key => $answer){ foreach ($answers as $key => $answer){
$errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent'); $errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
} }
} }
// regular calculated // regular calculated
foreach ($answers as $key => $answer){ foreach ($answers as $key => $answer){
//check no of choices //check no of choices
// the * for everykind of answer not actually implemented // the * for everykind of answer not actually implemented
$trimmedanswer = trim($answer); $trimmedanswer = trim($answer);
if (($trimmedanswer!='')||$answercount==0){ if (($trimmedanswer!='')||$answercount==0){
$eqerror = qtype_calculated_find_formula_errors($trimmedanswer); $eqerror = qtype_calculated_find_formula_errors($trimmedanswer);
if (FALSE !== $eqerror){ if (FALSE !== $eqerror){
$errors['answer['.$key.']'] = $eqerror; $errors['answer['.$key.']'] = $eqerror;
}
} }
if ($trimmedanswer!=''){ }
if ('2' == $data['correctanswerformat'][$key] if ($trimmedanswer!=''){
&& '0' == $data['correctanswerlength'][$key]) { if ('2' == $data['correctanswerformat'][$key]
&& '0' == $data['correctanswerlength'][$key]) {
$errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz'); $errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
} }
if (!is_numeric($data['tolerance'][$key])){ if (!is_numeric($data['tolerance'][$key])){
$errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated'); $errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
} }
if ($data['fraction'][$key] == 1) { if ($data['fraction'][$key] == 1) {
$maxgrade = true; $maxgrade = true;
}
$answercount++;
} }
//check grades
//TODO how should grade checking work here?? $answercount++;
}
//check grades
//TODO how should grade checking work here??
/*if ($answer != '') { /*if ($answer != '') {
if ($data['fraction'][$key] > 0) { if ($data['fraction'][$key] > 0) {
$totalfraction += $data['fraction'][$key]; $totalfraction += $data['fraction'][$key];
@ -276,10 +323,10 @@ class question_edit_calculated_form extends question_edit_form {
$maxfraction = $data['fraction'][$key]; $maxfraction = $data['fraction'][$key];
} }
}*/ }*/
} }
//grade checking : //grade checking :
/// Perform sanity checks on fractional grades /// Perform sanity checks on fractional grades
/*if ( ) { /*if ( ) {
if ($maxfraction != 1) { if ($maxfraction != 1) {
$maxfraction = $maxfraction * 100; $maxfraction = $maxfraction * 100;
@ -292,7 +339,7 @@ class question_edit_calculated_form extends question_edit_form {
$errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction); $errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
} }
} }
$units = $data['unit']; $units = $data['unit'];
if (count($units)) { if (count($units)) {
foreach ($units as $key => $unit){ foreach ($units as $key => $unit){
if (is_numeric($unit)){ if (is_numeric($unit)){
@ -309,18 +356,17 @@ class question_edit_calculated_form extends question_edit_form {
} }
} }
} }
}*/ }*/
$QTYPES['numerical']->validate_numerical_options($data, $errors) ; $QTYPES['numerical']->validate_numerical_options($data, $errors) ;
if ($answercount==0){ if ($answercount==0){
$errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated'); $errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
} }
if ($maxgrade == false) { if ($maxgrade == false) {
$errors['fraction[0]'] = get_string('fractionsnomax', 'question'); $errors['fraction[0]'] = get_string('fractionsnomax', 'question');
} }
return $errors; 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['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['correctanswershows'] = 'Correct answer shows';
$string['correctanswershowsformat'] = 'Format'; $string['correctanswershowsformat'] = 'Format';
$string['correctfeedback'] = 'For any correct response';
$string['dataitemdefined']='with {$a} numerical values already defined is available'; $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['datasetrole']= ' The wild cards <strong>{x..}</strong> will be substituted by a numerical value from their dataset';
$string['deleteitem'] = 'Delete Item'; $string['deleteitem'] = 'Delete Item';
@ -61,6 +62,7 @@ $string['forceregenerationall'] = 'forceregeneration of all wildcards';
$string['forceregenerationshared'] = 'forceregeneration of only non-shared wildcards'; $string['forceregenerationshared'] = 'forceregeneration of only non-shared wildcards';
$string['getnextnow'] = 'Get New \'Item to Add\' Now'; $string['getnextnow'] = 'Get New \'Item to Add\' Now';
$string['hexanotallowed'] = 'Dataset <strong>{$a->name}</strong> hexadecimal format value $a->value is not allowed' ; $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['item(s)'] = 'item(s)';
$string['itemno'] = 'Item {$a}'; $string['itemno'] = 'Item {$a}';
$string['itemscount']='Items<br />Count'; $string['itemscount']='Items<br />Count';
@ -91,8 +93,9 @@ $string['nocommaallowed'] = 'The , cannot be used, use . as in 0.013 or 1.3e-2'
$string['nodataset'] = 'nothing - it is not a wild card'; $string['nodataset'] = 'nothing - it is not a wild card';
$string['nosharedwildcard'] = 'No shared wild card in this category'; $string['nosharedwildcard'] = 'No shared wild card in this category';
$string['notvalidnumber'] = 'Wild card value is not a valid number ' ; $string['notvalidnumber'] = 'Wild card value is not a valid number ' ;
$string['oneanswertrueansweroutsidelimits'] = 'At least one correct answer outside the true value limits.<br />Modify the answers tolerance settings available as Advanced parameters'; $string['oneanswertrueansweroutsidelimits'] = 'At least one correct answer outside the true value limits.<br />Modify the answers tolerance settings available as Advanced parameters';
$string['param'] = 'Param {<strong>{$a}</strong>}'; $string['param'] = 'Param {<strong>{$a}</strong>}';
$string['partiallycorrectfeedback'] = 'For any partially correct response';
$string['possiblehdr'] = 'Possible wild cards present only in the question text'; $string['possiblehdr'] = 'Possible wild cards present only in the question text';
$string['questiondatasets'] = 'Question datasets'; $string['questiondatasets'] = 'Question datasets';
$string['questiondatasets_help'] = 'Question datasets of wild cards that will be used in each individual question'; $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);
}

File diff suppressed because it is too large Load diff

View file

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

View file

@ -18,11 +18,11 @@ class question_edit_calculatedmulti_form extends question_edit_form {
* *
* @var question_calculatedmulti_qtype * @var question_calculatedmulti_qtype
*/ */
var $qtypeobj; public $qtypeobj;
public $questiondisplay ; public $questiondisplay ;
public $initialname = ''; public $initialname = '';
public $reload = false ; public $reload = false ;
function question_edit_calculatedmulti_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){ function question_edit_calculatedmulti_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true) {
global $QTYPES, $SESSION, $CFG, $DB; global $QTYPES, $SESSION, $CFG, $DB;
$this->question = $question; $this->question = $question;
$this->qtypeobj =& $QTYPES[$this->question->qtype]; $this->qtypeobj =& $QTYPES[$this->question->qtype];
@ -38,22 +38,21 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$regs= array(); $regs= array();
if(preg_match('~#\{([^[:space:]]*)#~',$question->name , $regs)){ if(preg_match('~#\{([^[:space:]]*)#~',$question->name , $regs)){
$question->name = str_replace($regs[0], '', $question->name); $question->name = str_replace($regs[0], '', $question->name);
}; };
} }
}else { }else {
} }
parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable); parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
} }
function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) { function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
// $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption); // $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
$repeated = array(); $repeated = array();
$repeated[] =& $mform->createElement('header', 'answerhdr', $label); $repeated[] =& $mform->createElement('header', 'answerhdr', $label);
// if ($this->editasmultichoice == 1){ // if ($this->editasmultichoice == 1){
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50)); $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions); $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'), $repeated[] =& $mform->createElement('editor', 'feedback', get_string('feedback', 'quiz'), null, $this->editoroptions);
array('course' => $this->coursefilesid));
$repeatedoptions['answer']['type'] = PARAM_RAW; $repeatedoptions['answer']['type'] = PARAM_RAW;
$repeatedoptions['fraction']['default'] = 0; $repeatedoptions['fraction']['default'] = 0;
$answersoption = 'answers'; $answersoption = 'answers';
@ -61,8 +60,8 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$mform->setType('answer', PARAM_NOTAGS); $mform->setType('answer', PARAM_NOTAGS);
$addrepeated = array(); $addrepeated = array();
$addrepeated[] =& $mform->createElement('hidden', 'tolerance'); $addrepeated[] =& $mform->createElement('hidden', 'tolerance');
$addrepeated[] =& $mform->createElement('hidden', 'tolerancetype',1); $addrepeated[] =& $mform->createElement('hidden', 'tolerancetype',1);
$repeatedoptions['tolerance']['type'] = PARAM_NUMBER; $repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
$repeatedoptions['tolerance']['default'] = 0.01; $repeatedoptions['tolerance']['default'] = 0.01;
@ -72,7 +71,7 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz')); $answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz'));
$addrepeated[] =& $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats); $addrepeated[] =& $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
array_splice($repeated, 3, 0, $addrepeated); array_splice($repeated, 3, 0, $addrepeated);
$repeated[1]->setLabel('...<strong>{={x}+..}</strong>...'); $repeated[1]->setLabel('...<strong>{={x}+..}</strong>...');
return $repeated; return $repeated;
} }
@ -85,79 +84,78 @@ class question_edit_calculatedmulti_form extends question_edit_form {
function definition_inner(&$mform) { function definition_inner(&$mform) {
global $QTYPES; global $QTYPES;
$this->qtypeobj =& $QTYPES[$this->qtype()]; $this->qtypeobj =& $QTYPES[$this->qtype()];
// echo code left for testing period // echo code left for testing period
// echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>"; // echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
$label = get_string("sharedwildcards", "qtype_calculated"); $label = get_string("sharedwildcards", "qtype_calculated");
$mform->addElement('hidden', 'initialcategory', 1); $mform->addElement('hidden', 'initialcategory', 1);
$mform->addElement('hidden', 'reload', 1); $mform->addElement('hidden', 'reload', 1);
$mform->setType('initialcategory', PARAM_INT); $mform->setType('initialcategory', PARAM_INT);
// $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question); // $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question);
$html2 =""; $html2 ="";
$mform->insertElementBefore($mform->createElement('static','listcategory',$label,$html2),'name'); $mform->insertElementBefore($mform->createElement('static','listcategory',$label,$html2),'name');
if(isset($this->question->id )){ if(isset($this->question->id )){
$mform->insertElementBefore($mform->createElement('static','initialname',get_string('questionstoredname','qtype_calculated'),$this->initialname),'name'); $mform->insertElementBefore($mform->createElement('static','initialname',get_string('questionstoredname','qtype_calculated'),$this->initialname),'name');
}; };
$addfieldsname='updatecategory'; $addfieldsname='updatecategory';
$addstring=get_string("updatecategory", "qtype_calculated"); $addstring=get_string("updatecategory", "qtype_calculated");
$mform->registerNoSubmitButton($addfieldsname); $mform->registerNoSubmitButton($addfieldsname);
$this->editasmultichoice = 1 ; $this->editasmultichoice = 1 ;
$mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory'); $mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
$mform->registerNoSubmitButton('createoptionbutton'); $mform->registerNoSubmitButton('createoptionbutton');
$mform->addElement('hidden', 'multichoice',$this->editasmultichoice); $mform->addElement('hidden', 'multichoice',$this->editasmultichoice);
$mform->setType('multichoice', PARAM_INT); $mform->setType('multichoice', PARAM_INT);
// $mform->addElement('header', 'choicehdr',get_string('multichoicecalculatedquestion', 'qtype_calculated'));
$menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice')); // $mform->addElement('header', 'choicehdr',get_string('multichoicecalculatedquestion', 'qtype_calculated'));
$mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu); $menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice'));
$mform->setDefault('single', 1); $mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu);
$mform->setDefault('single', 1);
$mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0,1));
$mform->addHelpButton('shuffleanswers', 'shuffleanswers', 'qtype_multichoice'); $mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0,1));
$mform->setDefault('shuffleanswers', 1); $mform->setHelpButton('shuffleanswers', array('multichoiceshuffle', get_string('shuffleanswers','qtype_multichoice'), 'qtype_multichoice'));
$mform->setDefault('shuffleanswers', 1);
$numberingoptions = $QTYPES['multichoice']->get_numbering_styles();
$menu = array(); $numberingoptions = $QTYPES['multichoice']->get_numbering_styles();
foreach ($numberingoptions as $numberingoption) { $menu = array();
$menu[$numberingoption] = get_string('answernumbering' . $numberingoption, 'qtype_multichoice'); foreach ($numberingoptions as $numberingoption) {
} $menu[$numberingoption] = get_string('answernumbering' . $numberingoption, 'qtype_multichoice');
$mform->addElement('select', 'answernumbering', get_string('answernumbering', 'qtype_multichoice'), $menu); }
$mform->setDefault('answernumbering', 'abc'); $mform->addElement('select', 'answernumbering', get_string('answernumbering', 'qtype_multichoice'), $menu);
$mform->setDefault('answernumbering', 'abc');
$creategrades = get_grade_options(); $creategrades = get_grade_options();
$this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'), $this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'),
$creategrades->gradeoptionsfull, max(5, QUESTION_NUMANS_START)); $creategrades->gradeoptionsfull, max(5, QUESTION_NUMANS_START));
$repeated = array(); $repeated = array();
// if ($this->editasmultichoice == 1){ // if ($this->editasmultichoice == 1){
$nounits = optional_param('nounits', 1, PARAM_INT); $nounits = optional_param('nounits', 1, PARAM_INT);
$mform->addElement('hidden', 'nounits', $nounits); $mform->addElement('hidden', 'nounits', $nounits);
$mform->setType('nounits', PARAM_INT); $mform->setType('nounits', PARAM_INT);
$mform->setConstants(array('nounits'=>$nounits)); $mform->setConstants(array('nounits'=>$nounits));
for ($i=0; $i< $nounits; $i++) { for ($i=0; $i< $nounits; $i++) {
$mform->addElement('hidden','unit'."[$i]", optional_param('unit'."[$i]", '', PARAM_NOTAGS)); $mform->addElement('hidden','unit'."[$i]", optional_param('unit'."[$i]", '', PARAM_NOTAGS));
$mform->setType('unit'."[$i]", PARAM_NOTAGS); $mform->setType('unit'."[$i]", PARAM_NOTAGS);
$mform->addElement('hidden', 'multiplier'."[$i]", optional_param('multiplier'."[$i]", '', PARAM_NUMBER)); $mform->addElement('hidden', 'multiplier'."[$i]", optional_param('multiplier'."[$i]", '', PARAM_NUMBER));
$mform->setType('multiplier'."[$i]", PARAM_NUMBER); $mform->setType('multiplier'."[$i]", PARAM_NUMBER);
} }
$mform->addElement('hidden','unitgradingtype',optional_param('unitgradingtype', '', PARAM_INT)) ; $mform->addElement('hidden','unitgradingtype',optional_param('unitgradingtype', '', PARAM_INT)) ;
$mform->addElement('hidden','unitpenalty',optional_param('unitpenalty', '', PARAM_NUMBER)) ; $mform->addElement('hidden','unitpenalty',optional_param('unitpenalty', '', PARAM_NUMBER)) ;
$mform->addElement('hidden','showunits',optional_param('showunits', '', PARAM_INT)) ; $mform->addElement('hidden','showunits',optional_param('showunits', '', PARAM_INT)) ;
$mform->addElement('hidden','unitsleft',optional_param('unitsleft', '', PARAM_INT)) ; $mform->addElement('hidden','unitsleft',optional_param('unitsleft', '', PARAM_INT)) ;
$mform->addElement('hidden','instructions',optional_param('instructions', '', PARAM_RAW)) ; $mform->addElement('hidden','instructions',optional_param('instructions', '', PARAM_RAW)) ;
$mform->setType('addunits','hidden'); $mform->setType('addunits','hidden');
$mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice')); $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) { foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
$mform->addElement('htmleditor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'), $mform->addElement('editor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'), null, $this->editoroptions);
array('course' => $this->coursefilesid)); $mform->setType($feedbackname, PARAM_RAW);
$mform->setType($feedbackname, PARAM_RAW); }
}
//hidden elements //hidden elements
$mform->addElement('hidden', 'synchronize', ''); $mform->addElement('hidden', 'synchronize', '');
$mform->setType('synchronize', PARAM_INT); $mform->setType('synchronize', PARAM_INT);
@ -168,40 +166,43 @@ class question_edit_calculatedmulti_form extends question_edit_form {
} }
$mform->addElement('hidden', 'wizard', 'datasetdefinitions'); $mform->addElement('hidden', 'wizard', 'datasetdefinitions');
$mform->setType('wizard', PARAM_ALPHA); $mform->setType('wizard', PARAM_ALPHA);
} }
function set_data($question) { function data_preprocessing($question) {
$default_values['multichoice']= $this->editasmultichoice ; //$this->editasmultichoice ; $default_values['multichoice']= $this->editasmultichoice ; //$this->editasmultichoice ;
if (isset($question->options)){ if (isset($question->options)){
$answers = $question->options->answers; $answers = $question->options->answers;
if (count($answers)) { if (count($answers)) {
$key = 0; $key = 0;
foreach ($answers as $answer){ foreach ($answers as $answer){
$draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
$default_values['answer['.$key.']'] = $answer->answer; $default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction; $default_values['fraction['.$key.']'] = $answer->fraction;
$default_values['tolerance['.$key.']'] = $answer->tolerance; $default_values['tolerance['.$key.']'] = $answer->tolerance;
$default_values['tolerancetype['.$key.']'] = $answer->tolerancetype; $default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
$default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength; $default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
$default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat; $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++; $key++;
} }
} }
// $default_values['unitgradingtype'] = $question->options->unitgradingtype ; // $default_values['unitgradingtype'] = $question->options->unitgradingtype ;
// $default_values['unitpenalty'] = $question->options->unitpenalty ; // $default_values['unitpenalty'] = $question->options->unitpenalty ;
// $default_values['showunits'] = $question->options->showunits ; // $default_values['showunits'] = $question->options->showunits ;
// $default_values['unitsleft'] = $question->options->unitsleft ; // $default_values['unitsleft'] = $question->options->unitsleft ;
// $default_values['instructions'] = $question->options->instructions ; // $default_values['instructions'] = $question->options->instructions ;
$default_values['synchronize'] = $question->options->synchronize ; $default_values['synchronize'] = $question->options->synchronize ;
if (isset($question->options->units)){ if (isset($question->options->units)){
$units = array_values($question->options->units); $units = array_values($question->options->units);
// make sure the default unit is at index 0 // make sure the default unit is at index 0
usort($units, create_function('$a, $b', usort($units, create_function('$a, $b',
'if (1.0 === (float)$a->multiplier) { return -1; } else '. 'if (1.0 === (float)$a->multiplier) { return -1; } else '.
'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }')); 'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
if (count($units)) { if (count($units)) {
$key = 0; $key = 0;
foreach ($units as $unit){ foreach ($units as $unit){
@ -213,30 +214,51 @@ class question_edit_calculatedmulti_form extends question_edit_form {
} }
} }
if (isset($question->options->single)){ if (isset($question->options->single)){
$default_values['single'] = $question->options->single; $default_values['single'] = $question->options->single;
$default_values['answernumbering'] = $question->options->answernumbering; $default_values['answernumbering'] = $question->options->answernumbering;
$default_values['shuffleanswers'] = $question->options->shuffleanswers; $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['submitbutton'] = get_string('nextpage', 'qtype_calculated');
$default_values['makecopy'] = get_string('makecopynextpage', '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 // prepare draft files
update category button. The value can be obtain by foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
$qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0]; if (!isset($question->options->$feedbackname)) {
but is coded using existing functions continue;
*/ }
$qu = new stdClass; $text = $question->options->$feedbackname;
$el = new stdClass; $draftid = file_get_submitted_draft_itemid($feedbackname);
/* no need to call elementExists() here */ $feedbackformat = $feedbackname . 'format';
if ($this->_form->elementExists('category')){ $format = $question->options->$feedbackformat;
$default_values[$feedbackname] = array();
$default_values[$feedbackname]['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'qtype_calculatedmulti', // component
$feedbackname, // filarea
!empty($question->id)?(int)$question->id:null, // itemid
$this->fileoptions, // options
$text // text
);
$default_values[$feedbackname]['format'] = $format;
$default_values[$feedbackname]['itemid'] = $draftid;
}
/**
* set the wild cards category display given that on loading the category element is
* unselected when processing this function but have a valid value when processing the
* update category button. The value can be obtain by
* $qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
* but is coded using existing functions
*/
$qu = new stdClass;
$el = new stdClass;
/* no need to call elementExists() here */
if ($this->_form->elementExists('category')){
$el=$this->_form->getElement('category'); $el=$this->_form->getElement('category');
} else { } else {
$el=$this->_form->getElement('categorymoveto'); $el=$this->_form->getElement('categorymoveto');
} }
if($value =$el->getSelected()) { if($value =$el->getSelected()) {
$qu->category =$value[0]; $qu->category =$value[0];
}else { }else {
$qu->category=$question->category;// on load $question->category is set by question.php $qu->category=$question->category;// on load $question->category is set by question.php
@ -244,8 +266,7 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$html2 = $this->qtypeobj->print_dataset_definitions_category($qu); $html2 = $this->qtypeobj->print_dataset_definitions_category($qu);
$this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ; $this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ;
$question = (object)((array)$question + $default_values); $question = (object)((array)$question + $default_values);
return $question;
parent::set_data($question);
} }
function qtype() { function qtype() {
@ -253,21 +274,20 @@ class question_edit_calculatedmulti_form extends question_edit_form {
} }
function validation($data, $files) { function validation($data, $files) {
// echo code left for testing period // echo code left for testing period
// echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
// echo "<p>question <pre>";print_r($this->question);echo "</pre></p>"; // echo "<p>data <pre>";print_r($data);echo "</pre></p>";
// echo "<p>data <pre>";print_r($data);echo "</pre></p>";
$errors = parent::validation($data, $files); $errors = parent::validation($data, $files);
//verifying for errors in {=...} in question text; //verifying for errors in {=...} in question text;
$qtext = ""; $qtext = "";
$qtextremaining = $data['questiontext'] ; $qtextremaining = $data['questiontext']['text'];
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']); $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
foreach ($possibledatasets as $name => $value) { foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining); $qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
} }
// echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets); // echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2); $qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0]; $qtext =$qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1]; $qtextremaining = $qtextsplits[1];
@ -282,13 +302,13 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$answers = $data['answer']; $answers = $data['answer'];
$answercount = 0; $answercount = 0;
$maxgrade = false; $maxgrade = false;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']); $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
$mandatorydatasets = array(); $mandatorydatasets = array();
foreach ($answers as $key => $answer){ foreach ($answers as $key => $answer){
$mandatorydatasets += $this->qtypeobj->find_dataset_names($answer); $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
} }
if ( count($mandatorydatasets )==0){ if ( count($mandatorydatasets )==0){
// $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent'); // $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
foreach ($answers as $key => $answer){ foreach ($answers as $key => $answer){
$errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent'); $errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
} }
@ -296,15 +316,15 @@ class question_edit_calculatedmulti_form extends question_edit_form {
if ($data['multichoice']== 1 ){ if ($data['multichoice']== 1 ){
foreach ($answers as $key => $answer){ foreach ($answers as $key => $answer){
$trimmedanswer = trim($answer); $trimmedanswer = trim($answer);
if (($trimmedanswer!='')||$answercount==0){ if (($trimmedanswer!='')||$answercount==0){
//verifying for errors in {=...} in answer text; //verifying for errors in {=...} in answer text;
$qanswer = ""; $qanswer = "";
$qanswerremaining = $trimmedanswer ; $qanswerremaining = $trimmedanswer ;
$possibledatasets = $this->qtypeobj->find_dataset_names($trimmedanswer); $possibledatasets = $this->qtypeobj->find_dataset_names($trimmedanswer);
foreach ($possibledatasets as $name => $value) { foreach ($possibledatasets as $name => $value) {
$qanswerremaining = str_replace('{'.$name.'}', '1', $qanswerremaining); $qanswerremaining = str_replace('{'.$name.'}', '1', $qanswerremaining);
} }
// echo "numericalquestion qanswerremaining <pre>";print_r($possibledatasets); // echo "numericalquestion qanswerremaining <pre>";print_r($possibledatasets);
while (preg_match('~\{=([^[:space:]}]*)}~', $qanswerremaining, $regs1)) { while (preg_match('~\{=([^[:space:]}]*)}~', $qanswerremaining, $regs1)) {
$qanswersplits = explode($regs1[0], $qanswerremaining, 2); $qanswersplits = explode($regs1[0], $qanswerremaining, 2);
$qanswer =$qanswer.$qanswersplits[0]; $qanswer =$qanswer.$qanswersplits[0];
@ -320,16 +340,16 @@ class question_edit_calculatedmulti_form extends question_edit_form {
} }
if ($trimmedanswer!=''){ if ($trimmedanswer!=''){
if ('2' == $data['correctanswerformat'][$key] if ('2' == $data['correctanswerformat'][$key]
&& '0' == $data['correctanswerlength'][$key]) { && '0' == $data['correctanswerlength'][$key]) {
$errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz'); $errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
} }
if (!is_numeric($data['tolerance'][$key])){ if (!is_numeric($data['tolerance'][$key])){
$errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated'); $errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
} }
if ($data['fraction'][$key] == 1) { if ($data['fraction'][$key] == 1) {
$maxgrade = true; $maxgrade = true;
} }
$answercount++; $answercount++;
} }
//check grades //check grades
@ -342,14 +362,14 @@ class question_edit_calculatedmulti_form extends question_edit_form {
if ($data['fraction'][$key] > $maxfraction) { if ($data['fraction'][$key] > $maxfraction) {
$maxfraction = $data['fraction'][$key]; $maxfraction = $data['fraction'][$key];
} }
} }
} }
if ($answercount==0){ if ($answercount==0){
$errors['answer[0]'] = get_string('notenoughanswers', 'qtype_multichoice', 2); $errors['answer[0]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
$errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2); $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
} elseif ($answercount==1){ } elseif ($answercount==1){
$errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2); $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
} }
/// Perform sanity checks on fractional grades /// Perform sanity checks on fractional grades
@ -365,17 +385,15 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction); $errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
} }
} }
if ($answercount==0){ if ($answercount==0){
$errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated'); $errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
} }
if ($maxgrade == false) { if ($maxgrade == false) {
$errors['fraction[0]'] = get_string('fractionsnomax', 'question'); $errors['fraction[0]'] = get_string('fractionsnomax', 'question');
} }
} }
return $errors; 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 <?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 /// // CALCULATED ///
///////////////// /////////////////
/// QUESTION TYPE CLASS ////////////////// /// QUESTION TYPE CLASS //////////////////
class question_calculatedmulti_qtype extends question_calculated_qtype { class question_calculatedmulti_qtype extends question_calculated_qtype {
// Used by the function custom_generator_tools: // Used by the function custom_generator_tools:
var $calcgenerateidhasbeenadded = false; public $calcgenerateidhasbeenadded = false;
public $virtualqtype = false; public $virtualqtype = false;
function name() { function name() {
@ -28,14 +41,13 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
function save_question_options($question) { function save_question_options($question) {
//$options = $question->subtypeoptions;
// Get old answers:
global $CFG, $DB, $QTYPES ; global $CFG, $DB, $QTYPES ;
$context = $question->context;
if (isset($question->answer) && !isset($question->answers)) { if (isset($question->answer) && !isset($question->answers)) {
$question->answers = $question->answer; $question->answers = $question->answer;
} }
// calculated options // calculated options
$update = true ; $update = true ;
$options = $DB->get_record("question_calculated_options", array("question" => $question->id)); $options = $DB->get_record("question_calculated_options", array("question" => $question->id));
if (!$options) { if (!$options) {
$update = false; $update = false;
@ -46,19 +58,20 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$options->single = $question->single; $options->single = $question->single;
$options->answernumbering = $question->answernumbering; $options->answernumbering = $question->answernumbering;
$options->shuffleanswers = $question->shuffleanswers; $options->shuffleanswers = $question->shuffleanswers;
$options->correctfeedback = trim($question->correctfeedback);
$options->partiallycorrectfeedback = trim($question->partiallycorrectfeedback); // save question feedback files
$options->incorrectfeedback = trim($question->incorrectfeedback); 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 ($update) {
if (!$DB->update_record("question_calculated_options", $options)) { $DB->update_record("question_calculated_options", $options);
$result->error = "Could not update calculated question options! (id=$options->id)";
return $result;
}
} else { } else {
if (!$DB->insert_record("question_calculated_options", $options)) { $DB->insert_record("question_calculated_options", $options);
$result->error = "Could not insert calculated question options!";
return $result;
}
} }
// Get old versions of the objects // Get old versions of the objects
@ -71,8 +84,9 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
} }
// Save the units. // Save the units.
$virtualqtype = $this->get_virtual_qtype( $question); $virtualqtype = $this->get_virtual_qtype($question);
// $result = $virtualqtype->save_numerical_units($question); // TODO: What is this?
// $result = $virtualqtype->save_numerical_units($question);
if (isset($result->error)) { if (isset($result->error)) {
return $result; return $result;
} else { } else {
@ -80,21 +94,25 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
} }
// Insert all the new answers // Insert all the new answers
if (isset($question->answer) && !isset($question->answers)) { if (isset($question->answer) && !isset($question->answers)) {
$question->answers=$question->answer; $question->answers = $question->answer;
} }
foreach ($question->answers as $key => $dataanswer) { foreach ($question->answers as $key => $dataanswer) {
if ( trim($dataanswer) != '' ) { if ( trim($dataanswer) != '' ) {
$answer = new stdClass; $answer = new stdClass;
$answer->question = $question->id; $answer->question = $question->id;
$answer->answer = trim($dataanswer); $answer->answer = trim($dataanswer);
$answer->fraction = $question->fraction[$key]; $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 if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
$answer->id = $oldanswer->id; $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); $DB->update_record("question_answers", $answer);
} else { // This is a completely new answer } else { // This is a completely new answer
$answer->id = $DB->insert_record("question_answers", $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 // Set up the options object
@ -129,15 +147,15 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$DB->delete_records('question_calculated', array('id' => $oo->id)); $DB->delete_records('question_calculated', array('id' => $oo->id));
} }
} }
// $result = $QTYPES['numerical']->save_numerical_options($question); // $result = $QTYPES['numerical']->save_numerical_options($question);
// if (isset($result->error)) { // if (isset($result->error)) {
// return $result; // return $result;
// } // }
if( isset($question->import_process)&&$question->import_process){ if( isset($question->import_process)&&$question->import_process){
$this->import_datasets($question); $this->import_datasets($question);
} }
// Report any problems. // Report any problems.
if (!empty($result->notice)) { if (!empty($result->notice)) {
return $result; return $result;
@ -148,30 +166,25 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) { function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
// Find out how many datasets are available // Find out how many datasets are available
global $CFG, $DB, $QTYPES, $OUTPUT ; global $CFG, $DB, $QTYPES, $OUTPUT ;
if(!$maxnumber = (int)$DB->get_field_sql( $maxnumber = (int)$DB->get_field_sql(
"SELECT MIN(a.itemcount) "SELECT MIN(a.itemcount)
FROM {question_dataset_definitions} a, FROM {question_dataset_definitions} a, {question_datasets} b
{question_datasets} b WHERE b.question = ? AND a.id = b.datasetdefinition", array($question->id));
WHERE b.question = ? if (!$maxnumber) {
AND a.id = b.datasetdefinition", array($question->id))) {
print_error('cannotgetdsforquestion', 'question', '', $question->id); print_error('cannotgetdsforquestion', 'question', '', $question->id);
} }
$sql = "SELECT i.* $sql = "SELECT i.*
FROM {question_datasets} d, FROM {question_datasets} d, {question_dataset_definitions} i
{question_dataset_definitions} i WHERE d.question = ? AND d.datasetdefinition = i.id AND i.category != 0";
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))) { if (!$question->options->synchronize || !$records = $DB->get_records_sql($sql, array($question->id))) {
$synchronize_calculated = false ; $synchronize_calculated = false ;
}else { } else {
// i.e records is true so test coherence // i.e records is true so test coherence
$coherence = true ; $coherence = true ;
$a = new stdClass ; $a = new stdClass ;
$a->qid = $question->id ; $a->qid = $question->id ;
$a->qcat = $question->category ; $a->qcat = $question->category ;
foreach($records as $def ){ foreach($records as $def ){
if ($def->category != $question->category){ if ($def->category != $question->category){
$a->name = $def->name; $a->name = $def->name;
$a->sharedcat = $def->category ; $a->sharedcat = $def->category ;
@ -180,11 +193,11 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
} }
} }
if(!$coherence){ if(!$coherence){
echo $OUTPUT->notification(get_string('nocoherencequestionsdatyasetcategory','qtype_calculated',$a)); echo $OUTPUT->notification(get_string('nocoherencequestionsdatyasetcategory','qtype_calculated',$a));
} }
$synchronize_calculated = true ; $synchronize_calculated = true ;
} }
// Choose a random dataset // Choose a random dataset
// maxnumber sould not be breater than 100 // maxnumber sould not be breater than 100
@ -194,41 +207,40 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
if ( $synchronize_calculated === false ) { if ( $synchronize_calculated === false ) {
$state->options->datasetitem = rand(1, $maxnumber); $state->options->datasetitem = rand(1, $maxnumber);
}else{ }else{
$state->options->datasetitem = intval( $maxnumber * substr($attempt->timestart,-2) /100 ) ; $state->options->datasetitem = intval( $maxnumber * substr($attempt->timestart,-2) /100 ) ;
if ($state->options->datasetitem < 1) { if ($state->options->datasetitem < 1) {
$state->options->datasetitem =1 ; $state->options->datasetitem =1 ;
} else if ($state->options->datasetitem > $maxnumber){ } else if ($state->options->datasetitem > $maxnumber){
$state->options->datasetitem = $maxnumber ; $state->options->datasetitem = $maxnumber ;
} }
}; };
$state->options->dataset = $state->options->dataset =
$this->pick_question_dataset($question,$state->options->datasetitem); $this->pick_question_dataset($question,$state->options->datasetitem);
// create an array of answerids ??? why so complicated ??? // create an array of answerids ??? why so complicated ???
$answerids = array_values(array_map(create_function('$val', $answerids = array_values(array_map(create_function('$val',
'return $val->id;'), $question->options->answers)); 'return $val->id;'), $question->options->answers));
// Shuffle the answers if required // Shuffle the answers if required
if (!empty($cmoptions->shuffleanswers) and !empty($question->options->shuffleanswers)) { if (!empty($cmoptions->shuffleanswers) and !empty($question->options->shuffleanswers)) {
$answerids = swapshuffle($answerids); $answerids = swapshuffle($answerids);
} }
$state->options->order = $answerids; $state->options->order = $answerids;
// Create empty responses // Create empty responses
if ($question->options->single) { if ($question->options->single) {
$state->responses = array('' => ''); $state->responses = array('' => '');
} else { } else {
$state->responses = array(); $state->responses = array();
} }
return true; return true;
} }
function save_session_and_responses(&$question, &$state) { function save_session_and_responses(&$question, &$state) {
global $DB; global $DB;
$responses = 'dataset'.$state->options->datasetitem.'-' ; $responses = 'dataset'.$state->options->datasetitem.'-' ;
$responses .= implode(',', $state->options->order) . ':'; $responses .= implode(',', $state->options->order) . ':';
$responses .= implode(',', $state->responses); $responses .= implode(',', $state->responses);
// Set the legacy answer field // Set the legacy answer field
if (!$DB->set_field('question_states', 'answer', $responses, array('id'=> $state->id))) { if (!$DB->set_field('question_states', 'answer', $responses, array('id'=> $state->id))) {
return false; return false;
} }
@ -241,7 +253,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
foreach ($form->answers as $key => $answer) { foreach ($form->answers as $key => $answer) {
$a->answer = trim($form->answer[$key]); $a->answer = trim($form->answer[$key]);
$a->fraction = $form->fraction[$key];//new $a->fraction = $form->fraction[$key];//new
$a->tolerance = $form->tolerance[$key]; $a->tolerance = $form->tolerance[$key];
$a->tolerancetype = $form->tolerancetype[$key]; $a->tolerancetype = $form->tolerancetype[$key];
$a->correctanswerlength = $form->correctanswerlength[$key]; $a->correctanswerlength = $form->correctanswerlength[$key];
$a->correctanswerformat = $form->correctanswerformat[$key]; $a->correctanswerformat = $form->correctanswerformat[$key];
@ -251,57 +263,53 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
return $question; return $question;
} }
function convert_answers (&$question, &$state){ function convert_answers (&$question, &$state){
foreach ($question->options->answers as $key => $answer) { foreach ($question->options->answers as $key => $answer) {
$answer->answer = $this->substitute_variables($answer->answer, $state->options->dataset); $answer->answer = $this->substitute_variables($answer->answer, $state->options->dataset);
//evaluate the equations i.e {=5+4) //evaluate the equations i.e {=5+4)
$qtext = ""; $qtext = "";
$qtextremaining = $answer->answer ; $qtextremaining = $answer->answer ;
// while (preg_match('~\{(=)|%[[:digit]]\.=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { // while (preg_match('~\{(=)|%[[:digit]]\.=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2); $qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0]; $qtext = $qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1]; $qtextremaining = $qtextsplits[1];
if (empty($regs1[1])) { if (empty($regs1[1])) {
$str = ''; $str = '';
} else { } else {
if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){ if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
$str=$formulaerrors ; $str=$formulaerrors ;
}else { }else {
eval('$str = '.$regs1[1].';'); eval('$str = '.$regs1[1].';');
$texteval= qtype_calculated_calculate_answer( $texteval= qtype_calculated_calculate_answer(
$str, $state->options->dataset, $answer->tolerance, $str, $state->options->dataset, $answer->tolerance,
$answer->tolerancetype, $answer->correctanswerlength, $answer->tolerancetype, $answer->correctanswerlength,
$answer->correctanswerformat, ''); $answer->correctanswerformat, '');
$str = $texteval->answer; $str = $texteval->answer;
} }
}
$qtext = $qtext.$str ;
} }
$answer->answer = $qtext.$qtextremaining ; ; $qtext = $qtext.$str ;
} }
$answer->answer = $qtext.$qtextremaining ; ;
} }
}
function get_default_numerical_unit($question,$virtualqtype){ function get_default_numerical_unit($question, $virtualqtype){
$unit = ''; $unit = '';
return $unit ; return $unit ;
} }
function grade_responses(&$question, &$state, $cmoptions) { function grade_responses(&$question, &$state, $cmoptions) {
// Forward the grading to the virtual qtype // Forward the grading to the virtual qtype
// We modify the question to look like a multichoice question // We modify the question to look like a multichoice question
// for grading nothing to do // for grading nothing to do
/* $numericalquestion = fullclone($question); /* $numericalquestion = fullclone($question);
foreach ($numericalquestion->options->answers as $key => $answer) { foreach ($numericalquestion->options->answers as $key => $answer) {
$answer = $numericalquestion->options->answers[$key]->answer; // for PHP 4.x $answer = $numericalquestion->options->answers[$key]->answer; // for PHP 4.x
$numericalquestion->options->answers[$key]->answer = $this->substitute_variables_and_eval($answer, $numericalquestion->options->answers[$key]->answer = $this->substitute_variables_and_eval($answer,
$state->options->dataset); $state->options->dataset);
}*/ }*/
$virtualqtype = $this->get_virtual_qtype( $question); $virtualqtype = $this->get_virtual_qtype( $question);
return $virtualqtype->grade_responses($question, $state, $cmoptions) ; return $virtualqtype->grade_responses($question, $state, $cmoptions) ;
} }
@ -319,7 +327,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$this->convert_answers ($numericalquestion, $state); $this->convert_answers ($numericalquestion, $state);
$this->convert_questiontext ($numericalquestion, $state); $this->convert_questiontext ($numericalquestion, $state);
/* $numericalquestion->questiontext = $this->substitute_variables_and_eval( /* $numericalquestion->questiontext = $this->substitute_variables_and_eval(
$numericalquestion->questiontext, $state->options->dataset);*/ $numericalquestion->questiontext, $state->options->dataset);*/
$responses = $virtualqtype->get_all_responses($numericalquestion, $state); $responses = $virtualqtype->get_all_responses($numericalquestion, $state);
$response = reset($responses->responses); $response = reset($responses->responses);
$correct = $response->answer.' : '; $correct = $response->answer.' : ';
@ -335,8 +343,8 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
function create_virtual_qtype() { function create_virtual_qtype() {
global $CFG; global $CFG;
require_once("$CFG->dirroot/question/type/multichoice/questiontype.php"); require_once("$CFG->dirroot/question/type/multichoice/questiontype.php");
return new question_multichoice_qtype(); return new question_multichoice_qtype();
} }
@ -353,7 +361,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
} else { } else {
$strheader .= $delimiter.$answer->answer; $strheader .= $delimiter.$answer->answer;
} }
$delimiter = '<br/>'; $delimiter = '<br/>';
} }
return $strheader; return $strheader;
} }
@ -369,7 +377,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$unit = $unit->unit; $unit = $unit->unit;
} else { } else {
$unit = ''; $unit = '';
}*/ }*/
$answers = fullclone($answers); $answers = fullclone($answers);
$strmin = get_string('min', 'quiz'); $strmin = get_string('min', 'quiz');
@ -377,29 +385,29 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$errors = ''; $errors = '';
$delimiter = ': '; $delimiter = ': ';
foreach ($answers as $key => $answer) { foreach ($answers as $key => $answer) {
$answer->answer = $this->substitute_variables($answer->answer, $data); $answer->answer = $this->substitute_variables($answer->answer, $data);
//evaluate the equations i.e {=5+4) //evaluate the equations i.e {=5+4)
$qtext = ""; $qtext = "";
$qtextremaining = $answer->answer ; $qtextremaining = $answer->answer ;
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2); $qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0]; $qtext =$qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1]; $qtextremaining = $qtextsplits[1];
if (empty($regs1[1])) { if (empty($regs1[1])) {
$str = ''; $str = '';
} else { } else {
if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){ if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
$str=$formulaerrors ; $str=$formulaerrors ;
}else { }else {
eval('$str = '.$regs1[1].';'); eval('$str = '.$regs1[1].';');
} }
}
$qtext = $qtext.$str ;
} }
$answer->answer = $qtext.$qtextremaining ; ; $qtext = $qtext.$str ;
$comment->stranswers[$key]= $answer->answer ; }
$answer->answer = $qtext.$qtextremaining;
$comment->stranswers[$key] = $answer->answer;
/* $formula = $this->substitute_variables($answer->answer,$data); /* $formula = $this->substitute_variables($answer->answer,$data);
$formattedanswer = qtype_calculated_calculate_answer( $formattedanswer = qtype_calculated_calculate_answer(
$answer->answer, $data, $answer->tolerance, $answer->answer, $data, $answer->tolerance,
@ -411,7 +419,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
}else { }else {
eval('$answer->answer = '.$formula.';') ; eval('$answer->answer = '.$formula.';') ;
$virtualqtype->get_tolerance_interval($answer); $virtualqtype->get_tolerance_interval($answer);
} }
if ($answer->min === '') { if ($answer->min === '') {
// This should mean that something is wrong // This should mean that something is wrong
$comment->stranswers[$key] = " $formattedanswer->answer".'<br/><br/>'; $comment->stranswers[$key] = " $formattedanswer->answer".'<br/><br/>';
@ -432,15 +440,11 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$comment->stranswers[$key] .=get_string('trueanswerinsidelimits','qtype_calculated',$correcttrue);//' True answer :'.$calculated->trueanswer.' inside limits'; $comment->stranswers[$key] .=get_string('trueanswerinsidelimits','qtype_calculated',$correcttrue);//' True answer :'.$calculated->trueanswer.' inside limits';
} }
$comment->stranswers[$key] .=''; $comment->stranswers[$key] .='';
}*/ }*/
} }
return fullclone($comment); return fullclone($comment);
} }
function get_correct_responses1(&$question, &$state) { function get_correct_responses1(&$question, &$state) {
$virtualqtype = $this->get_virtual_qtype( $question); $virtualqtype = $this->get_virtual_qtype( $question);
/* if ($question->options->multichoice != 1 ) { /* if ($question->options->multichoice != 1 ) {
@ -464,71 +468,148 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
return $correct; return $correct;
} }
} }
}else{**/ }else{**/
return $virtualqtype->get_correct_responses($question, $state) ; return $virtualqtype->get_correct_responses($question, $state) ;
// } // }
return null; return null;
} }
function get_virtual_qtype() { function get_virtual_qtype() {
global $QTYPES; global $QTYPES;
// if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){ // if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){
$this->virtualqtype =& $QTYPES['multichoice']; $this->virtualqtype =& $QTYPES['multichoice'];
// }else { // }else {
// $this->virtualqtype =& $QTYPES['numerical']; // $this->virtualqtype =& $QTYPES['numerical'];
// } // }
return $this->virtualqtype; return $this->virtualqtype;
} }
/** /**
* Runs all the code required to set up and save an essay question for testing purposes. * Runs all the code required to set up and save an essay question for testing purposes.
* Alternate DB table prefix may be used to facilitate data deletion. * Alternate DB table prefix may be used to facilitate data deletion.
*/ */
function generate_test($name, $courseid = null) { function generate_test($name, $courseid = null) {
global $DB; global $DB;
list($form, $question) = parent::generate_test($name, $courseid); list($form, $question) = parent::generate_test($name, $courseid);
$form->feedback = 1; $form->feedback = 1;
$form->multiplier = array(1, 1); $form->multiplier = array(1, 1);
$form->shuffleanswers = 1; $form->shuffleanswers = 1;
$form->noanswers = 1; $form->noanswers = 1;
$form->qtype ='calculatedmulti'; $form->qtype ='calculatedmulti';
$question->qtype ='calculatedmulti'; $question->qtype ='calculatedmulti';
$form->answers = array('{a} + {b}'); $form->answers = array('{a} + {b}');
$form->fraction = array(1); $form->fraction = array(1);
$form->tolerance = array(0.01); $form->tolerance = array(0.01);
$form->tolerancetype = array(1); $form->tolerancetype = array(1);
$form->correctanswerlength = array(2); $form->correctanswerlength = array(2);
$form->correctanswerformat = array(1); $form->correctanswerformat = array(1);
$form->questiontext = "What is {a} + {b}?"; $form->questiontext = "What is {a} + {b}?";
if ($courseid) { if ($courseid) {
$course = $DB->get_record('course', array('id'=> $courseid)); $course = $DB->get_record('course', array('id'=> $courseid));
} }
$new_question = $this->save_question($question, $form, $course); $new_question = $this->save_question($question, $form, $course);
$dataset_form = new stdClass(); $dataset_form = new stdClass();
$dataset_form->nextpageparam["forceregeneration"]= 1; $dataset_form->nextpageparam["forceregeneration"]= 1;
$dataset_form->calcmin = array(1 => 1.0, 2 => 1.0); $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
$dataset_form->calcmax = array(1 => 10.0, 2 => 10.0); $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
$dataset_form->calclength = array(1 => 1, 2 => 1); $dataset_form->calclength = array(1 => 1, 2 => 1);
$dataset_form->number = array(1 => 5.4 , 2 => 4.9); $dataset_form->number = array(1 => 5.4 , 2 => 4.9);
$dataset_form->itemid = array(1 => '' , 2 => ''); $dataset_form->itemid = array(1 => '' , 2 => '');
$dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform'); $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
$dataset_form->definition = array(1 => "1-0-a", $dataset_form->definition = array(1 => "1-0-a",
2 => "1-0-b"); 2 => "1-0-b");
$dataset_form->nextpageparam = array('forceregeneration' => false); $dataset_form->nextpageparam = array('forceregeneration' => false);
$dataset_form->addbutton = 1; $dataset_form->addbutton = 1;
$dataset_form->selectadd = 1; $dataset_form->selectadd = 1;
$dataset_form->courseid = $courseid; $dataset_form->courseid = $courseid;
$dataset_form->cmid = 0; $dataset_form->cmid = 0;
$dataset_form->id = $new_question->id; $dataset_form->id = $new_question->id;
$this->save_dataset_items($new_question, $dataset_form); $this->save_dataset_items($new_question, $dataset_form);
return $new_question; 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 //// //// END OF CLASS ////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View file

@ -1,4 +1,20 @@
<?php <?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. * Defines the editing form for the calculated simplequestion type.
* *
@ -8,21 +24,17 @@
* @package questionbank * @package questionbank
* @subpackage questiontypes * @subpackage questiontypes
*/ */
/**
* calculatedsimple editing form definition.
*/
class question_edit_calculatedsimple_form extends question_edit_form { class question_edit_calculatedsimple_form extends question_edit_form {
/** /**
* Handle to the question type for this question. * Handle to the question type for this question.
* *
* @var question_calculatedsimple_qtype * @var question_calculatedsimple_qtype
*/ */
var $qtypeobj; public $qtypeobj;
var $wildcarddisplay ; public $wildcarddisplay ;
var $questiondisplay ; public $questiondisplay ;
public $datasetdefs; public $datasetdefs;
@ -46,8 +58,6 @@ class question_edit_calculatedsimple_form extends question_edit_form {
public $formdata = array(); public $formdata = array();
function question_edit_calculatedsimple_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){ function question_edit_calculatedsimple_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
global $QTYPES, $SESSION, $CFG, $DB; global $QTYPES, $SESSION, $CFG, $DB;
$this->regenerate = true; $this->regenerate = true;
@ -60,17 +70,17 @@ class question_edit_calculatedsimple_form extends question_edit_form {
//so this should be removed from here //so this should be removed from here
// get priority to paramdatasets // get priority to paramdatasets
if ( "1" == optional_param('reload','', PARAM_INT )) { if ("1" == optional_param('reload','', PARAM_INT )) {
$this->reload = true ; $this->reload = true;
}else { }else {
$this->reload = false ; $this->reload = false;
} }
if(!$this->reload ){ // use database data as this is first pass if (!$this->reload) { // use database data as this is first pass
// question->id == 0 so no stored datasets // question->id == 0 so no stored datasets
// else get datasets // else get datasets
// echo "<p>question <pre>";print_r($question);echo "</pre></p>"; // echo "<p>question <pre>";print_r($question);echo "</pre></p>";
if ( !empty($question->id)) { if (!empty($question->id)) {
/* if (empty($question->options)) { /* if (empty($question->options)) {
$this->get_question_options($question); $this->get_question_options($question);
}*/ }*/
@ -127,7 +137,7 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$mandatorydatasets = array(); $mandatorydatasets = array();
// should not test on adding a new answer // should not test on adding a new answer
// should test if there are already olddatasets or if the 'analyzequestion' submit button has been clicked // should test if there are already olddatasets or if the 'analyzequestion' submit button has been clicked
if ('' != optional_param('datasetdef', '', PARAM_RAW) || '' != optional_param('analyzequestion', '', PARAM_RAW)){ if ('' != optional_param('datasetdef', '', PARAM_RAW) || '' != optional_param('analyzequestion', '', PARAM_RAW)){
if ( $dummyform->answer = optional_param('answer', '', PARAM_NOTAGS)) { // there is always at least one answer... if ( $dummyform->answer = optional_param('answer', '', PARAM_NOTAGS)) { // there is always at least one answer...
$fraction = optional_param('fraction', '', PARAM_NUMBER); $fraction = optional_param('fraction', '', PARAM_NUMBER);
@ -297,12 +307,12 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$addfieldsname='updatequestion value'; $addfieldsname='updatequestion value';
$addstring=get_string("updatecategory", "qtype_calculated"); $addstring=get_string("updatecategory", "qtype_calculated");
$mform->registerNoSubmitButton($addfieldsname); $mform->registerNoSubmitButton($addfieldsname);
//put a submit button to stop supplementary answers on update answers parameters // put a submit button to stop supplementary answers on update answers parameters
// $mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory'); // $mform->insertElementBefore($mform->createElement('submit', $addfieldsname, $addstring), 'listcategory');
$creategrades = get_grade_options(); $creategrades = get_grade_options();
$this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'), $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'),
$creategrades->gradeoptions, 1, 1); $creategrades->gradeoptions, 1, 1);
$QTYPES['numerical']->add_units_options($mform,$this); $QTYPES['numerical']->add_units_options($mform,$this);
@ -327,7 +337,7 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$this->noofitems = 0; $this->noofitems = 0;
} }
if(!empty($this->datasetdefs)){//So there are some datadefs if(!empty($this->datasetdefs)){//So there are some datadefs
// we put them on the page // we put them on the page
$key = 0; $key = 0;
$mform->addElement('header', 'additemhdr', get_string('wildcardparam', 'qtype_calculatedsimple')); $mform->addElement('header', 'additemhdr', get_string('wildcardparam', 'qtype_calculatedsimple'));
$idx = 1; $idx = 1;
@ -347,180 +357,183 @@ class question_edit_calculatedsimple_form extends question_edit_form {
} }
//this should be done before the elements are created and stored as $this->formdata ; //this should be done before the elements are created and stored as $this->formdata ;
//fill out all data sets and also the fields for the next item to add. //fill out all data sets and also the fields for the next item to add.
/*Here we do already the values error analysis so that /*Here we do already the values error analysis so that
* we could force all wild cards values display if there is an error in values. * we could force all wild cards values display if there is an error in values.
* as using a , in a number */ * as using a , in a number */
$this->numbererrors = array(); $this->numbererrors = array();
if(!empty($this->datasetdefs)){ if(!empty($this->datasetdefs)){
$j = $this->noofitems * count($this->datasetdefs); $j = $this->noofitems * count($this->datasetdefs);
for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){ for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){
$data = array(); $data = array();
$numbererrors = array() ; $numbererrors = array() ;
$comment = new stdClass; $comment = new stdClass;
$comment->stranswers = array(); $comment->stranswers = array();
$comment->outsidelimit = false ; $comment->outsidelimit = false ;
$comment->answers = array(); $comment->answers = array();
foreach ($this->datasetdefs as $defid => $datasetdef){ foreach ($this->datasetdefs as $defid => $datasetdef){
if (isset($datasetdef->items[$itemnumber])){ if (isset($datasetdef->items[$itemnumber])){
$this->formdata["definition[$j]"] = $defid; $this->formdata["definition[$j]"] = $defid;
$this->formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id; $this->formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id;
$data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value; $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
$this->formdata["number[$j]"] = $number = $datasetdef->items[$itemnumber]->value; $this->formdata["number[$j]"] = $number = $datasetdef->items[$itemnumber]->value;
if(! is_numeric($number)){ if(! is_numeric($number)){
$a = new stdClass; $a = new stdClass;
$a->name = '{'.$datasetdef->name.'}' ; $a->name = '{'.$datasetdef->name.'}' ;
$a->value = $datasetdef->items[$itemnumber]->value ; $a->value = $datasetdef->items[$itemnumber]->value ;
if (stristr($number,',')){ if (stristr($number,',')){
$this->numbererrors["number[$j]"]=get_string('nocommaallowed', 'qtype_calculated'); $this->numbererrors["number[$j]"]=get_string('nocommaallowed', 'qtype_calculated');
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />"; $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
}else { }else {
$this->numbererrors["number[$j]"]= get_string('notvalidnumber','qtype_calculated',$a); $this->numbererrors["number[$j]"]= get_string('notvalidnumber','qtype_calculated',$a);
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />"; $numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
//$comment->outsidelimit = false ; //$comment->outsidelimit = false ;
}
}else if( stristr($number,'x')){ // hexa will pass the test
$a = new stdClass;
$a->name = '{'.$datasetdef->name.'}' ;
$a->value = $datasetdef->items[$itemnumber]->value ;
$this->numbererrors['number['.$j.']']= get_string('hexanotallowed','qtype_calculated',$a);
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
} else if( is_nan($number)){
$a = new stdClass;
$a->name = '{'.$datasetdef->name.'}' ;
$a->value = $datasetdef->items[$itemnumber]->value ;
$this->numbererrors["number[$j]"]= get_string('notvalidnumber','qtype_calculated',$a);
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
// $val = 1.0 ;
}
}
$j--;
}
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 ($comment->outsidelimit) {
$this->outsidelimit=$comment->outsidelimit ;
}
$totalcomment='';
foreach ($this->nonemptyanswer as $key => $answer) {
$totalcomment .= $comment->stranswers[$key].'<br/>';
}
$this->formdata['answercomment['.$itemnumber.']'] = $totalcomment ;
} }
}else if( stristr($number,'x')){ // hexa will pass the test
$a = new stdClass;
$a->name = '{'.$datasetdef->name.'}' ;
$a->value = $datasetdef->items[$itemnumber]->value ;
$this->numbererrors['number['.$j.']']= get_string('hexanotallowed','qtype_calculated',$a);
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
} else if( is_nan($number)){
$a = new stdClass;
$a->name = '{'.$datasetdef->name.'}' ;
$a->value = $datasetdef->items[$itemnumber]->value ;
$this->numbererrors["number[$j]"]= get_string('notvalidnumber','qtype_calculated',$a);
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
// $val = 1.0 ;
} }
}
$j--;
} }
$this->formdata['selectdelete'] = '1'; if($this->noofitems != 0 ) {
$this->formdata['selectadd'] = '1'; if (empty($numbererrors)) {
$j = $this->noofitems * count($this->datasetdefs)+1; if (!isset($this->question->id)) {
$data = array(); // data for comment_on_datasetitems later $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 ;
}
$totalcomment='';
foreach ($this->nonemptyanswer as $key => $answer) {
$totalcomment .= $comment->stranswers[$key].'<br/>';
}
$this->formdata['answercomment['.$itemnumber.']'] = $totalcomment ;
}
}
}
$this->formdata['selectdelete'] = '1';
$this->formdata['selectadd'] = '1';
$j = $this->noofitems * count($this->datasetdefs)+1;
$data = array(); // data for comment_on_datasetitems later
$idx =1 ; $idx =1 ;
foreach ($this->datasetdefs as $defid => $datasetdef){ foreach ($this->datasetdefs as $defid => $datasetdef){
$this->formdata["datasetdef[$idx]"] = $defid; $this->formdata["datasetdef[$idx]"] = $defid;
$idx++; $idx++;
} }
$this->formdata = $this->qtypeobj->custom_generator_set_data($this->datasetdefs, $this->formdata); $this->formdata = $this->qtypeobj->custom_generator_set_data($this->datasetdefs, $this->formdata);
}
$addoptions = Array();
$addoptions['1']='1';
for ($i=10; $i<=100 ; $i+=10){
$addoptions["$i"]="$i";
}
$showoptions = Array();
$showoptions['1']='1';
$showoptions['2']='2';
$showoptions['5']='5';
for ($i=10; $i<=100 ; $i+=10){
$showoptions["$i"]="$i";
}
$mform->closeHeaderBefore('additemhdr');
$addgrp = array();
$addgrp[] =& $mform->createElement('submit', 'addbutton', get_string('generatenewitemsset', 'qtype_calculatedsimple'));
$addgrp[] =& $mform->createElement('select', "selectadd", '', $addoptions);
$addgrp[] = & $mform->createElement('static',"stat",'',get_string('newsetwildcardvalues', 'qtype_calculatedsimple'));
$mform->addGroup($addgrp, 'addgrp', '', ' ', false);
$mform->registerNoSubmitButton('addbutton');
$mform->closeHeaderBefore('addgrp');
$addgrp1 = array();
$addgrp1[] =& $mform->createElement('submit', 'showbutton', get_string('showitems', 'qtype_calculatedsimple'));
$addgrp1[] =& $mform->createElement('select', "selectshow",'' , $showoptions);
$addgrp1[] = & $mform->createElement('static',"stat",'',get_string('setwildcardvalues', 'qtype_calculatedsimple'));
$mform->addGroup($addgrp1, 'addgrp1', '', ' ', false);
$mform->registerNoSubmitButton('showbutton');
$mform->closeHeaderBefore('addgrp1');
$mform->addElement('static', "divideradd", '', '');
if ($this->noofitems == 0) {
$mform->addElement('static','warningnoitems','','<span class="error">'.get_string('youmustaddatleastonevalue', 'qtype_calculatedsimple').'</span>');
$mform->closeHeaderBefore('warningnoitems');
}else {
$mform->addElement('header', 'additemhdr1', get_string('wildcardvalues', 'qtype_calculatedsimple'));
$mform->closeHeaderBefore('additemhdr1');
// $mform->addElement('header', '', get_string('itemno', 'qtype_calculated', ""));
if( !empty($this->numbererrors) || $this->outsidelimit) {
$mform->addElement('static', "alert", '', '<span class="error">'.get_string('useadvance', 'qtype_calculatedsimple').'</span>');
} }
$mform->addElement('submit', 'updatedatasets', get_string('updatewildcardvalues', 'qtype_calculatedsimple'));
$mform->registerNoSubmitButton('updatedatasets');
$mform->setAdvanced("updatedatasets",true);
$addoptions = Array(); //------------------------------------------------------------------------------------------------------------------------------
$addoptions['1']='1'; $j = $this->noofitems * count($this->datasetdefs);
for ($i=10; $i<=100 ; $i+=10){ $k = 1 ;
$addoptions["$i"]="$i"; if ("" != optional_param('selectshow')){
} $k = optional_param('selectshow', '', PARAM_INT);
$showoptions = Array(); }
$showoptions['1']='1';
$showoptions['2']='2'; for ($i = $this->noofitems; $i >= 1 ; $i--){
$showoptions['5']='5'; foreach ($this->datasetdefs as $defkey => $datasetdef){
for ($i=10; $i<=100 ; $i+=10){ if($k > 0 || $this->outsidelimit || !empty($this->numbererrors ) ){
$showoptions["$i"]="$i"; $mform->addElement('text',"number[$j]" , get_string('wildcard', 'qtype_calculatedsimple', $datasetdef->name));
} $mform->setAdvanced("number[$j]",true);
$mform->closeHeaderBefore('additemhdr'); if(!empty($this->numbererrors['number['.$j.']']) ){
$addgrp = array(); $mform->addElement('static', "numbercomment[$j]",'','<span class="error">'.$this->numbererrors['number['.$j.']'].'</span>');
$addgrp[] =& $mform->createElement('submit', 'addbutton', get_string('generatenewitemsset', 'qtype_calculatedsimple')); $mform->setAdvanced("numbercomment[$j]",true);
$addgrp[] =& $mform->createElement('select', "selectadd", '', $addoptions); }
$addgrp[] = & $mform->createElement('static',"stat",'',get_string('newsetwildcardvalues', 'qtype_calculatedsimple')); }else {
$mform->addGroup($addgrp, 'addgrp', '', ' ', false); $mform->addElement('hidden',"number[$j]" , get_string('wildcard', 'qtype_calculatedsimple', $datasetdef->name));
$mform->registerNoSubmitButton('addbutton'); }
$mform->closeHeaderBefore('addgrp'); $mform->setType("number[$j]", PARAM_NUMBER);
$addgrp1 = array();
$addgrp1[] =& $mform->createElement('submit', 'showbutton', get_string('showitems', 'qtype_calculatedsimple')); $mform->addElement('hidden', "itemid[$j]");
$addgrp1[] =& $mform->createElement('select', "selectshow",'' , $showoptions); $mform->setType("itemid[$j]", PARAM_INT);
$addgrp1[] = & $mform->createElement('static',"stat",'',get_string('setwildcardvalues', 'qtype_calculatedsimple'));
$mform->addGroup($addgrp1, 'addgrp1', '', ' ', false); $mform->addElement('hidden', "definition[$j]");
$mform->registerNoSubmitButton('showbutton'); $mform->setType("definition[$j]", PARAM_NOTAGS);
$mform->closeHeaderBefore('addgrp1');
$mform->addElement('static', "divideradd", '', ''); $j--;
if ($this->noofitems == 0) { }
$mform->addElement('static','warningnoitems','','<span class="error">'.get_string('youmustaddatleastonevalue', 'qtype_calculatedsimple').'</span>'); if (!empty( $strquestionlabel) && ($k > 0 || $this->outsidelimit || !empty($this->numbererrors ) ) ){
$mform->closeHeaderBefore('warningnoitems'); // $repeated[] =& $mform->addElement('static', "answercomment[$i]", $strquestionlabel);
$mform->addElement('static', "answercomment[$i]", "<b>".get_string('setno', 'qtype_calculatedsimple', $i)."</b>&nbsp;&nbsp;".$strquestionlabel);
}
if($k > 0 || $this->outsidelimit || !empty($this->numbererrors )){
$mform->addElement('static', "divider1[$j]", '', '<hr />');
}
$k-- ;
}
}
// if ($this->outsidelimit){
// $mform->addElement('static','outsidelimit','','');
// }
}else { }else {
$mform->addElement('header', 'additemhdr1', get_string('wildcardvalues', 'qtype_calculatedsimple')); $mform->addElement('static','warningnowildcards','','<span class="error">'.get_string('atleastonewildcard', 'qtype_calculatedsimple').'</span>');
$mform->closeHeaderBefore('additemhdr1'); $mform->closeHeaderBefore('warningnowildcards');
// $mform->addElement('header', '', get_string('itemno', 'qtype_calculated', ""));
if( !empty($this->numbererrors) || $this->outsidelimit) {
$mform->addElement('static', "alert", '', '<span class="error">'.get_string('useadvance', 'qtype_calculatedsimple').'</span>');
} }
$mform->addElement('submit', 'updatedatasets', get_string('updatewildcardvalues', 'qtype_calculatedsimple'));
$mform->registerNoSubmitButton('updatedatasets');
$mform->setAdvanced("updatedatasets",true);
//------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------
$j = $this->noofitems * count($this->datasetdefs);
$k = 1 ;
if ("" != optional_param('selectshow')){
$k = optional_param('selectshow', '', PARAM_INT);
}
for ($i = $this->noofitems; $i >= 1 ; $i--){
foreach ($this->datasetdefs as $defkey => $datasetdef){
if($k > 0 || $this->outsidelimit || !empty($this->numbererrors ) ){
$mform->addElement('text',"number[$j]" , get_string('wildcard', 'qtype_calculatedsimple', $datasetdef->name));
$mform->setAdvanced("number[$j]",true);
if(!empty($this->numbererrors['number['.$j.']']) ){
$mform->addElement('static', "numbercomment[$j]",'','<span class="error">'.$this->numbererrors['number['.$j.']'].'</span>');
$mform->setAdvanced("numbercomment[$j]",true);
}
}else {
$mform->addElement('hidden',"number[$j]" , get_string('wildcard', 'qtype_calculatedsimple', $datasetdef->name));
}
$mform->setType("number[$j]", PARAM_NUMBER);
$mform->addElement('hidden', "itemid[$j]");
$mform->setType("itemid[$j]", PARAM_INT);
$mform->addElement('hidden', "definition[$j]");
$mform->setType("definition[$j]", PARAM_NOTAGS);
$j--;
}
if (!empty( $strquestionlabel) && ($k > 0 || $this->outsidelimit || !empty($this->numbererrors ) ) ){
// $repeated[] =& $mform->addElement('static', "answercomment[$i]", $strquestionlabel);
$mform->addElement('static', "answercomment[$i]", "<b>".get_string('setno', 'qtype_calculatedsimple', $i)."</b>&nbsp;&nbsp;".$strquestionlabel);
}
if($k > 0 || $this->outsidelimit || !empty($this->numbererrors )){
$mform->addElement('static', "divider1[$j]", '', '<hr />');
}
$k-- ;
}
}
// if ($this->outsidelimit){
// $mform->addElement('static','outsidelimit','','');
// }
}else {
$mform->addElement('static','warningnowildcards','','<span class="error">'.get_string('atleastonewildcard', 'qtype_calculatedsimple').'</span>');
$mform->closeHeaderBefore('warningnowildcards');
}
//------------------------------------------------------------------------------------------------------------------------------
//non standard name for button element needed so not using add_action_buttons //non standard name for button element needed so not using add_action_buttons
//hidden elements //hidden elements
@ -536,50 +549,64 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$mform->setDefault('cmid', 0); $mform->setDefault('cmid', 0);
if (!empty($this->question->id)){ if (!empty($this->question->id)){
if ($this->question->formoptions->cansaveasnew){ if ($this->question->formoptions->cansaveasnew){
$mform->addElement('header', 'additemhdr', get_string('converttocalculated', 'qtype_calculatedsimple')); $mform->addElement('header', 'additemhdr', get_string('converttocalculated', 'qtype_calculatedsimple'));
$mform->closeHeaderBefore('additemhdr'); $mform->closeHeaderBefore('additemhdr');
$mform->addElement('checkbox', 'convert','' ,get_string('willconverttocalculated', 'qtype_calculatedsimple')); $mform->addElement('checkbox', 'convert','' ,get_string('willconverttocalculated', 'qtype_calculatedsimple'));
$mform->setDefault('convert', 0); $mform->setDefault('convert', 0);
}
} }
}
} }
function set_data($question) { function data_preprocessing($question) {
global $QTYPES; global $QTYPES;
$answer = $this->answer;
$default_values = array();
if (count($answer)) {
$key = 0;
foreach ($answer as $answer){
$default_values['answer['.$key.']'] = $answer->answer; // is necessary ? to-do test it
$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;
$answer = $this->answer; // prepare draft files
$default_values = array(); $draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
if (count($answer)) { $default_values['feedback['.$key.']']['text'] = file_prepare_draft_area(
$key = 0; $draftid, // draftid
foreach ($answer as $answer){ $this->context->id, // context
$default_values['answer['.$key.']'] = $answer->answer; // is necessary ? to-do test it 'question', // component
$default_values['fraction['.$key.']'] = $answer->fraction; 'answerfeedback', // filarea
$default_values['tolerance['.$key.']'] = $answer->tolerance; !empty($answer->id)?(int)$answer->id:null, // itemid
$default_values['tolerancetype['.$key.']'] = $answer->tolerancetype; $this->fileoptions, // options
$default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength; !empty($answer->feedback)?$answer->feedback:'' // text
$default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat; );
$key++; $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); $default_values['synchronize'] = 0 ;
/* if (isset($question->options)){ $QTYPES['numerical']->set_numerical_unit_data($this, $question, $default_values);
/* if (isset($question->options)){
$default_values['unitgradingtype'] = $question->options->unitgradingtype ; $default_values['unitgradingtype'] = $question->options->unitgradingtype ;
$default_values['unitpenalty'] = $question->options->unitpenalty ; $default_values['unitpenalty'] = $question->options->unitpenalty ;
switch ($question->options->showunits){ switch ($question->options->showunits){
case 'O' : case 'O' :
case '1' : case '1' :
$default_values['showunits0'] = $question->options->showunits ; $default_values['showunits0'] = $question->options->showunits ;
$default_values['unitrole'] = 0 ; $default_values['unitrole'] = 0 ;
break; break;
case '2' : case '2' :
case '3' : case '3' :
$default_values['showunits1'] = $question->options->showunits ; $default_values['showunits1'] = $question->options->showunits ;
$default_values['unitrole'] = 1 ; $default_values['unitrole'] = 1 ;
break; break;
} }
$default_values['unitsleft'] = $question->options->unitsleft ; $default_values['unitsleft'] = $question->options->unitsleft ;
$default_values['instructions'] = $question->options->instructions ; $default_values['instructions'] = $question->options->instructions ;
@ -593,16 +620,16 @@ class question_edit_calculatedsimple_form extends question_edit_form {
} }
} }
} }
*/ */
$key = 0 ; $key = 0 ;
$formdata = array(); $formdata = array();
$fromform = new stdClass(); $fromform = new stdClass();
//this should be done before the elements are created and stored as $this->formdata ; //this should be done before the elements are created and stored as $this->formdata ;
//fill out all data sets and also the fields for the next item to add. //fill out all data sets and also the fields for the next item to add.
/* if(!empty($this->datasetdefs)){ /* if(!empty($this->datasetdefs)){
$j = $this->noofitems * count($this->datasetdefs); $j = $this->noofitems * count($this->datasetdefs);
for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){ for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){
$data = array(); $data = array();
foreach ($this->datasetdefs as $defid => $datasetdef){ foreach ($this->datasetdefs as $defid => $datasetdef){
if (isset($datasetdef->items[$itemnumber])){ if (isset($datasetdef->items[$itemnumber])){
@ -613,27 +640,27 @@ class question_edit_calculatedsimple_form extends question_edit_form {
} }
$j--; $j--;
} }
// echo "<p>answers avant comment <pre>";print_r($answer);echo"</pre></p>"; // echo "<p>answers avant comment <pre>";print_r($answer);echo"</pre></p>";
// echo "<p>data avant comment <pre>";print_r($data);echo"</pre></p>"; // echo "<p>data avant comment <pre>";print_r($data);echo"</pre></p>";
if($this->noofitems != 0 ) { if($this->noofitems != 0 ) {
if(!isset($question->id)) $question->id = 0 ; if(!isset($question->id)) $question->id = 0 ;
$comment = $this->qtypeobj->comment_on_datasetitems($question->id,$this->nonemptyanswer, $data, $itemnumber);//$this-> $comment = $this->qtypeobj->comment_on_datasetitems($question->id,$this->nonemptyanswer, $data, $itemnumber);//$this->
if ($comment->outsidelimit) { if ($comment->outsidelimit) {
$this->outsidelimit=$comment->outsidelimit ; $this->outsidelimit=$comment->outsidelimit ;
} }
$totalcomment=''; $totalcomment='';
// echo "<p> comment <pre>";print_r($comment);echo"</pre></p>"; // echo "<p> comment <pre>";print_r($comment);echo"</pre></p>";
foreach ($this->nonemptyanswer as $key => $answer) { foreach ($this->nonemptyanswer as $key => $answer) {
$totalcomment .= $comment->stranswers[$key].'<br/>'; $totalcomment .= $comment->stranswers[$key].'<br/>';
} }
$formdata['answercomment['.$itemnumber.']'] = $totalcomment ; $formdata['answercomment['.$itemnumber.']'] = $totalcomment ;
}
} }
} // $formdata['reload'] = '1';
// $formdata['reload'] = '1'; // $formdata['nextpageparam[forceregeneration]'] = $this->regenerate;
// $formdata['nextpageparam[forceregeneration]'] = $this->regenerate;
$formdata['selectdelete'] = '1'; $formdata['selectdelete'] = '1';
$formdata['selectadd'] = '1'; $formdata['selectadd'] = '1';
$j = $this->noofitems * count($this->datasetdefs)+1; $j = $this->noofitems * count($this->datasetdefs)+1;
@ -644,10 +671,10 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$idx++; $idx++;
} }
$formdata = $this->qtypeobj->custom_generator_set_data($this->datasetdefs, $formdata); $formdata = $this->qtypeobj->custom_generator_set_data($this->datasetdefs, $formdata);
}*/ }*/
$question = (object)((array)$question + $default_values+$this->formdata ); $question = (object)((array)$question + $default_values+$this->formdata );
parent::set_data($question); return $question;
} }
function qtype() { function qtype() {
@ -659,8 +686,8 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$errors = parent::validation($data, $files); $errors = parent::validation($data, $files);
//verifying for errors in {=...} in question text; //verifying for errors in {=...} in question text;
$qtext = ""; $qtext = "";
$qtextremaining = $data['questiontext'] ; $qtextremaining = $data['questiontext']['text'];
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']); $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
foreach ($possibledatasets as $name => $value) { foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining); $qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
} }
@ -679,7 +706,7 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$answers = $data['answer']; $answers = $data['answer'];
$answercount = 0; $answercount = 0;
$maxgrade = false; $maxgrade = false;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']); $possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
$mandatorydatasets = array(); $mandatorydatasets = array();
foreach ($answers as $key => $answer){ foreach ($answers as $key => $answer){
$mandatorydatasets += $this->qtypeobj->find_dataset_names($answer); $mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
@ -797,4 +824,3 @@ class question_edit_calculatedsimple_form extends question_edit_form {
return $errors; 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 <?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 /// // CALCULATED ///
///////////////// /////////////////
/// QUESTION TYPE CLASS ////////////////// /// QUESTION TYPE CLASS //////////////////
class question_calculatedsimple_qtype extends question_calculated_qtype { class question_calculatedsimple_qtype extends question_calculated_qtype {
// Used by the function custom_generator_tools: // Used by the function custom_generator_tools:
var $calcgenerateidhasbeenadded = false; public $calcgenerateidhasbeenadded = false;
public $virtualqtype = false; public $virtualqtype = false;
function name() { function name() {
return 'calculatedsimple'; return 'calculatedsimple';
} }
function save_question_options($question) { function save_question_options($question) {
global $CFG, $DB , $QTYPES;
$context = $question->context;
//$options = $question->subtypeoptions; //$options = $question->subtypeoptions;
// Get old answers: // Get old answers:
global $CFG, $DB , $QTYPES;
if (isset($question->answer) && !isset($question->answers)) { if (isset($question->answer) && !isset($question->answers)) {
$question->answers = $question->answer; $question->answers = $question->answer;
@ -52,18 +63,22 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
$question->answers=$question->answer; $question->answers=$question->answer;
} }
foreach ($question->answers as $key => $dataanswer) { foreach ($question->answers as $key => $dataanswer) {
if ( trim($dataanswer) != '' ) { if ( trim($dataanswer) != '' ) {
$answer = new stdClass; $answer = new stdClass;
$answer->question = $question->id; $answer->question = $question->id;
$answer->answer = trim($dataanswer); $answer->answer = trim($dataanswer);
$answer->fraction = $question->fraction[$key]; $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 if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
$answer->id = $oldanswer->id; $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); $DB->update_record("question_answers", $answer);
} else { // This is a completely new answer } else { // This is a completely new answer
$answer->feedback = '';
$answer->id = $DB->insert_record("question_answers", $answer); $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 // Set up the options object
@ -99,82 +114,82 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
} }
} }
if(isset($question->import_process)&&$question->import_process) {
if( isset($question->import_process)&&$question->import_process){
$this->import_datasets($question); $this->import_datasets($question);
}else { } else {
//save datasets and datatitems from form i.e in question //save datasets and datatitems from form i.e in question
// $datasetdefs = $this->get_dataset_definitions($question->id, array()); // $datasetdefs = $this->get_dataset_definitions($question->id, array());
$question->dataset = $question->datasetdef ; $question->dataset = $question->datasetdef ;
// $this->save_dataset_definitions($question); // $this->save_dataset_definitions($question);
// Save datasets // Save datasets
$datasetdefinitions = $this->get_dataset_definitions($question->id, $question->dataset); $datasetdefinitions = $this->get_dataset_definitions($question->id, $question->dataset);
$tmpdatasets = array_flip($question->dataset); $tmpdatasets = array_flip($question->dataset);
$defids = array_keys($datasetdefinitions); $defids = array_keys($datasetdefinitions);
$datasetdefs = array(); $datasetdefs = array();
foreach ($defids as $defid) { foreach ($defids as $defid) {
$datasetdef = &$datasetdefinitions[$defid]; $datasetdef = &$datasetdefinitions[$defid];
if (isset($datasetdef->id)) { if (isset($datasetdef->id)) {
if (!isset($tmpdatasets[$defid])) { if (!isset($tmpdatasets[$defid])) {
// This dataset is not used any more, delete it // This dataset is not used any more, delete it
$DB->delete_records('question_datasets', array('question' => $question->id, 'datasetdefinition' => $datasetdef->id)); $DB->delete_records('question_datasets', array('question' => $question->id, 'datasetdefinition' => $datasetdef->id));
// if ($datasetdef->category == 0) { // Question local dataset // if ($datasetdef->category == 0) { // Question local dataset
$DB->delete_records('question_dataset_definitions', array('id' => $datasetdef->id)); $DB->delete_records('question_dataset_definitions', array('id' => $datasetdef->id));
$DB->delete_records('question_dataset_items', array('definition' => $datasetdef->id)); $DB->delete_records('question_dataset_items', array('definition' => $datasetdef->id));
// } // }
} }
// This has already been saved or just got deleted // This has already been saved or just got deleted
unset($datasetdefinitions[$defid]);
continue;
}
$datasetdef->id = $DB->insert_record('question_dataset_definitions', $datasetdef);
$datasetdefs[]= clone($datasetdef);
$questiondataset = new stdClass;
$questiondataset->question = $question->id;
$questiondataset->datasetdefinition = $datasetdef->id;
$DB->insert_record('question_datasets', $questiondataset);
unset($datasetdefinitions[$defid]); unset($datasetdefinitions[$defid]);
continue;
} }
$datasetdef->id = $DB->insert_record('question_dataset_definitions', $datasetdef); // Remove local obsolete datasets as well as relations
$datasetdefs[]= clone($datasetdef); // to datasets in other categories:
$questiondataset = new stdClass; if (!empty($datasetdefinitions)) {
$questiondataset->question = $question->id; foreach ($datasetdefinitions as $def) {
$questiondataset->datasetdefinition = $datasetdef->id; $DB->delete_records('question_datasets', array('question' => $question->id, 'datasetdefinition' => $def->id));
$DB->insert_record('question_datasets', $questiondataset); if ($def->category == 0) { // Question local dataset
unset($datasetdefinitions[$defid]); $DB->delete_records('question_dataset_definitions', array('id' => $def->id));
} $DB->delete_records('question_dataset_items', array('definition' => $def->id));
// Remove local obsolete datasets as well as relations }
// to datasets in other categories: }
if (!empty($datasetdefinitions)) { }
foreach ($datasetdefinitions as $def) { $datasetdefs = $this->get_dataset_definitions($question->id, $question->dataset);
$DB->delete_records('question_datasets', array('question' => $question->id, 'datasetdefinition' => $def->id)); // Handle adding and removing of dataset items
if ($def->category == 0) { // Question local dataset $i = 1;
$DB->delete_records('question_dataset_definitions', array('id' => $def->id)); ksort($question->definition);
$DB->delete_records('question_dataset_items', array('definition' => $def->id)); foreach ($question->definition as $key => $defid) {
$addeditem = new stdClass();
$addeditem->definition = $datasetdefs[$defid]->id;
$addeditem->value = $question->number[$i];
$addeditem->itemnumber = ceil($i / count($datasetdefs));
if (empty($question->makecopy) && $question->itemid[$i]) {
// Reuse any previously used record
$addeditem->id = $question->itemid[$i];
$DB->update_record('question_dataset_items', $addeditem);
} else {
$DB->insert_record('question_dataset_items', $addeditem);
}
$i++;
}
$maxnumber = -1;
if (isset($addeditem->itemnumber) && $maxnumber < $addeditem->itemnumber){
$maxnumber = $addeditem->itemnumber;
foreach ($datasetdefs as $key => $newdef) {
if (isset($newdef->id) && $newdef->itemcount <= $maxnumber) {
$newdef->itemcount = $maxnumber;
// Save the new value for options
$DB->update_record('question_dataset_definitions', $newdef);
}
} }
} }
} }
$datasetdefs = $this->get_dataset_definitions($question->id, $question->dataset);
// Handle adding and removing of dataset items
$i = 1;
ksort($question->definition);
foreach ($question->definition as $key => $defid) {
$addeditem = new stdClass();
$addeditem->definition = $datasetdefs[$defid]->id;
$addeditem->value = $question->number[$i];
$addeditem->itemnumber = ceil($i / count($datasetdefs));
if (empty($question->makecopy) && $question->itemid[$i]) {
// Reuse any previously used record
$addeditem->id = $question->itemid[$i];
$DB->update_record('question_dataset_items', $addeditem);
} else {
$DB->insert_record('question_dataset_items', $addeditem);
}
$i++;
}
if (isset($addeditem->itemnumber) && $maxnumber < $addeditem->itemnumber){
$maxnumber = $addeditem->itemnumber;
foreach ($datasetdefs as $key => $newdef) {
if (isset($newdef->id) && $newdef->itemcount <= $maxnumber) {
$newdef->itemcount = $maxnumber;
// Save the new value for options
$DB->update_record('question_dataset_definitions', $newdef);
}
}
}
}
// Report any problems. // Report any problems.
//convert to calculated //convert to calculated
if(!empty($question->makecopy) && !empty($question->convert)) { if(!empty($question->makecopy) && !empty($question->convert)) {
@ -190,39 +205,34 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
} }
return true; return true;
} }
function finished_edit_wizard(&$form) { function finished_edit_wizard(&$form) {
return true ; //isset($form->backtoquiz); return true ; //isset($form->backtoquiz);
} }
/** /**
* this version save the available data at the different steps of the question editing process * this version save the available data at the different steps of the question editing process
* without using global $SESSION as storage between steps * without using global $SESSION as storage between steps
* at the first step $wizardnow = 'question' * at the first step $wizardnow = 'question'
* when creating a new question * when creating a new question
* when modifying a question * when modifying a question
* when copying as a new question * when copying as a new question
* the general parameters and answers are saved using parent::save_question * the general parameters and answers are saved using parent::save_question
* then the datasets are prepared and saved * then the datasets are prepared and saved
* at the second step $wizardnow = 'datasetdefinitions' * at the second step $wizardnow = 'datasetdefinitions'
* the datadefs final type are defined as private, category or not a datadef * the datadefs final type are defined as private, category or not a datadef
* at the third step $wizardnow = 'datasetitems' * at the third step $wizardnow = 'datasetitems'
* the datadefs parameters and the data items are created or defined * the datadefs parameters and the data items are created or defined
* *
* @param object question * @param object question
* @param object $form * @param object $form
* @param int $course * @param int $course
* @param PARAM_ALPHA $wizardnow should be added as we are coming from question2.php * @param PARAM_ALPHA $wizardnow should be added as we are coming from question2.php
*/ */
function save_question($question, $form, $course) { function save_question($question, $form, $course) {
$question = default_questiontype::save_question($question, $form, $course); $question = default_questiontype::save_question($question, $form, $course);
return $question; return $question;
} }
function custom_generator_tools_part(&$mform, $idx, $j){ function custom_generator_tools_part(&$mform, $idx, $j){
$minmaxgrp = array(); $minmaxgrp = array();
@ -247,14 +257,14 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
$strheader = ""; $strheader = "";
$delimiter = ''; $delimiter = '';
// $answers = $question->options->answers; // $answers = $question->options->answers;
foreach ($answers as $key => $answer) { foreach ($answers as $key => $answer) {
/* if (is_string($answer)) { /* if (is_string($answer)) {
$strheader .= $delimiter.$answer; $strheader .= $delimiter.$answer;
} else {*/ } else {*/
$strheader .= $delimiter.$answer->answer; $strheader .= $delimiter.$answer->answer;
// } // }
$delimiter = '<br/><br/><br/>'; $delimiter = '<br/><br/><br/>';
} }
return $strheader; return $strheader;
@ -262,18 +272,18 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
function tolerance_types() { function tolerance_types() {
return array('1' => get_string('relative', 'quiz'), return array('1' => get_string('relative', 'quiz'),
'2' => get_string('nominal', 'quiz'), '2' => get_string('nominal', 'quiz'),
// '3' => get_string('geometric', 'quiz') // '3' => get_string('geometric', 'quiz')
); );
} }
function dataset_options($form, $name, $mandatory=true,$renameabledatasets=false) { function dataset_options($form, $name, $mandatory=true,$renameabledatasets=false) {
// Takes datasets from the parent implementation but // Takes datasets from the parent implementation but
// filters options that are currently not accepted by calculated // filters options that are currently not accepted by calculated
// It also determines a default selection... // It also determines a default selection...
//$renameabledatasets not implemented anmywhere //$renameabledatasets not implemented anmywhere
list($options, $selected) = $this->dataset_options_from_database($form, $name,'','qtype_calculated'); list($options, $selected) = $this->dataset_options_from_database($form, $name,'','qtype_calculated');
// list($options, $selected) = $this->dataset_optionsa($form, $name); // list($options, $selected) = $this->dataset_optionsa($form, $name);
foreach ($options as $key => $whatever) { foreach ($options as $key => $whatever) {
if (!preg_match('~^1-~', $key) && $key != '0') { if (!preg_match('~^1-~', $key) && $key != '0') {
@ -282,7 +292,7 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
} }
if (!$selected) { if (!$selected) {
if ($mandatory){ if ($mandatory){
$selected = "1-0-$name"; // Default $selected = "1-0-$name"; // Default
}else { }else {
$selected = "0"; // Default $selected = "0"; // Default
} }
@ -291,53 +301,123 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
} }
/** /**
* Runs all the code required to set up and save an essay question for testing purposes. * Runs all the code required to set up and save an essay question for testing purposes.
* Alternate DB table prefix may be used to facilitate data deletion. * Alternate DB table prefix may be used to facilitate data deletion.
*/ */
function generate_test($name, $courseid = null) { function generate_test($name, $courseid = null) {
global $DB; global $DB;
list($form, $question) = parent::generate_test($name, $courseid); list($form, $question) = parent::generate_test($name, $courseid);
$form->feedback = 1; $form->feedback = 1;
$form->multiplier = array(1, 1); $form->multiplier = array(1, 1);
$form->shuffleanswers = 1; $form->shuffleanswers = 1;
$form->noanswers = 1; $form->noanswers = 1;
$form->qtype ='calculatedsimple'; $form->qtype ='calculatedsimple';
$question->qtype ='calculatedsimple'; $question->qtype ='calculatedsimple';
$form->answers = array('{a} + {b}'); $form->answers = array('{a} + {b}');
$form->fraction = array(1); $form->fraction = array(1);
$form->tolerance = array(0.01); $form->tolerance = array(0.01);
$form->tolerancetype = array(1); $form->tolerancetype = array(1);
$form->correctanswerlength = array(2); $form->correctanswerlength = array(2);
$form->correctanswerformat = array(1); $form->correctanswerformat = array(1);
$form->questiontext = "What is {a} + {b}?"; $form->questiontext = "What is {a} + {b}?";
if ($courseid) { if ($courseid) {
$course = $DB->get_record('course', array('id'=> $courseid)); $course = $DB->get_record('course', array('id'=> $courseid));
} }
$new_question = $this->save_question($question, $form, $course); $new_question = $this->save_question($question, $form, $course);
$dataset_form = new stdClass(); $dataset_form = new stdClass();
$dataset_form->nextpageparam["forceregeneration"]= 1; $dataset_form->nextpageparam["forceregeneration"]= 1;
$dataset_form->calcmin = array(1 => 1.0, 2 => 1.0); $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
$dataset_form->calcmax = array(1 => 10.0, 2 => 10.0); $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
$dataset_form->calclength = array(1 => 1, 2 => 1); $dataset_form->calclength = array(1 => 1, 2 => 1);
$dataset_form->number = array(1 => 5.4 , 2 => 4.9); $dataset_form->number = array(1 => 5.4 , 2 => 4.9);
$dataset_form->itemid = array(1 => '' , 2 => ''); $dataset_form->itemid = array(1 => '' , 2 => '');
$dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform'); $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
$dataset_form->definition = array(1 => "1-0-a", $dataset_form->definition = array(1 => "1-0-a",
2 => "1-0-b"); 2 => "1-0-b");
$dataset_form->nextpageparam = array('forceregeneration' => false); $dataset_form->nextpageparam = array('forceregeneration' => false);
$dataset_form->addbutton = 1; $dataset_form->addbutton = 1;
$dataset_form->selectadd = 1; $dataset_form->selectadd = 1;
$dataset_form->courseid = $courseid; $dataset_form->courseid = $courseid;
$dataset_form->cmid = 0; $dataset_form->cmid = 0;
$dataset_form->id = $new_question->id; $dataset_form->id = $new_question->id;
$this->save_dataset_items($new_question, $dataset_form); $this->save_dataset_items($new_question, $dataset_form);
return $new_question; 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 //// //// END OF CLASS ////
@ -349,5 +429,3 @@ question_register_questiontype(new question_calculatedsimple_qtype());
if ( ! defined ("CALCULATEDSIMPLE")) { if ( ! defined ("CALCULATEDSIMPLE")) {
define("CALCULATEDSIMPLE", "calculatedsimple"); define("CALCULATEDSIMPLE", "calculatedsimple");
} }

View file

@ -1,4 +1,20 @@
<?php <?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. * Defines the editing form for the description question type.
* *

View file

@ -6,10 +6,6 @@
<div class="qtext"> <div class="qtext">
<?php echo $questiontext; ?> <?php echo $questiontext; ?>
</div> </div>
<?php if ($image) { ?>
<img class="qimage" src="<?php echo $image; ?>" alt="" />
<?php } ?>
</div> </div>
<?php if ($generalfeedback) { ?> <?php if ($generalfeedback) { ?>
<div class="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); $editlink = $this->get_question_edit_link($question, $cmoptions, $options);
$questiontext = $this->format_text($question->questiontext, $question->questiontextformat, $cmoptions); $questiontext = $this->format_text($question->questiontext, $question->questiontextformat, $cmoptions);
$image = get_question_image($question);
$generalfeedback = ''; $generalfeedback = '';
if ($isfinished && $options->generalfeedback) { if ($isfinished && $options->generalfeedback) {
@ -91,4 +90,3 @@ class description_qtype extends default_questiontype {
} }
// Register this question type with questionlib.php. // Register this question type with questionlib.php.
question_register_questiontype(new description_qtype()); question_register_questiontype(new description_qtype());

View file

@ -1,4 +1,20 @@
<?php <?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. * A base class for question editing forms.
* *
@ -7,7 +23,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank * @package questionbank
* @subpackage questiontypes * @subpackage questiontypes
*//** */ */
/** /**
* Form definition base class. This defines the common fields that * Form definition base class. This defines the common fields that
@ -27,24 +43,44 @@ class question_edit_form extends moodleform {
* @var object * @var object
*/ */
public $question; public $question;
public $contexts; public $contexts;
public $category; public $category;
public $categorycontext; public $categorycontext;
public $coursefilesid; 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){ function question_edit_form($submiturl, $question, $category, $contexts, $formeditable = true){
global $DB;
$this->question = $question; $this->question = $question;
$this->contexts = $contexts; $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->category = $category;
$this->categorycontext = get_context_instance_by_id($category->contextid); $this->categorycontext = get_context_instance_by_id($category->contextid);
//** *
//course id or site id depending on question cat context //course id or site id depending on question cat context
$this->coursefilesid = get_filesdir_from_context(get_context_instance_by_id($category->contextid)); $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); parent::moodleform($submiturl, null, 'post', '', null, $formeditable);
} }
@ -106,26 +142,9 @@ class question_edit_form extends moodleform {
$mform->setType('name', PARAM_TEXT); $mform->setType('name', PARAM_TEXT);
$mform->addRule('name', null, 'required', null, 'client'); $mform->addRule('name', null, 'required', null, 'client');
//TODO: MDL-16094 convert to new editor element $mform->addElement('editor', 'questiontext', get_string('questiontext', 'quiz'),
$mform->addElement('htmleditor', 'questiontext', get_string('questiontext', 'quiz'), array('rows' => 15, 'course' => $this->coursefilesid), $this->editoroptions);
array('rows' => 15, 'course' => $this->coursefilesid));
$mform->setType('questiontext', PARAM_RAW); $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'), $mform->addElement('text', 'defaultgrade', get_string('defaultgrade', 'quiz'),
array('size' => 3)); array('size' => 3));
@ -140,8 +159,8 @@ class question_edit_form extends moodleform {
$mform->addHelpButton('penalty', 'penaltyfactor', 'question'); $mform->addHelpButton('penalty', 'penaltyfactor', 'question');
$mform->setDefault('penalty', 0.1); $mform->setDefault('penalty', 0.1);
$mform->addElement('htmleditor', 'generalfeedback', get_string('generalfeedback', 'quiz'), $mform->addElement('editor', 'generalfeedback', get_string('generalfeedback', 'quiz'),
array('rows' => 10, 'course' => $this->coursefilesid)); array('rows' => 10, 'course' => $this->coursefilesid), $this->editoroptions);
$mform->setType('generalfeedback', PARAM_RAW); $mform->setType('generalfeedback', PARAM_RAW);
$mform->addHelpButton('generalfeedback', 'generalfeedback', 'quiz'); $mform->addHelpButton('generalfeedback', 'generalfeedback', 'quiz');
@ -235,7 +254,7 @@ class question_edit_form extends moodleform {
$errors= parent::validation($fromform, $files); $errors= parent::validation($fromform, $files);
if (empty($fromform->makecopy) && isset($this->question->id) if (empty($fromform->makecopy) && isset($this->question->id)
&& ($this->question->formoptions->canedit || $this->question->formoptions->cansaveasnew) && ($this->question->formoptions->canedit || $this->question->formoptions->cansaveasnew)
&& empty($fromform->usecurrentcat) && !$this->question->formoptions->canmove){ && empty($fromform->usecurrentcat) && !$this->question->formoptions->canmove) {
$errors['currentgrp'] = get_string('nopermissionmove', 'question'); $errors['currentgrp'] = get_string('nopermissionmove', 'question');
} }
return $errors; return $errors;
@ -264,8 +283,8 @@ class question_edit_form extends moodleform {
$repeated[] =& $mform->createElement('header', 'answerhdr', $label); $repeated[] =& $mform->createElement('header', 'answerhdr', $label);
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50)); $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions); $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'), $repeated[] =& $mform->createElement('editor', 'feedback', get_string('feedback', 'quiz'),
array('course' => $this->coursefilesid)); array('course' => $this->coursefilesid), $this->editoroptions);
$repeatedoptions['answer']['type'] = PARAM_RAW; $repeatedoptions['answer']['type'] = PARAM_RAW;
$repeatedoptions['fraction']['default'] = 0; $repeatedoptions['fraction']['default'] = 0;
$answersoption = 'answers'; $answersoption = 'answers';
@ -302,9 +321,33 @@ class question_edit_form extends moodleform {
function set_data($question) { function set_data($question) {
global $QTYPES; global $QTYPES;
if (empty($question->image)){ // prepare question text
unset($question->image); $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. // Remove unnecessary trailing 0s form grade fields.
if (isset($question->defaultgrade)) { 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); 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. * 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. * @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 ''; return '';
} }
} }

View file

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

View file

@ -1,4 +1,20 @@
<?php <?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. * 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. * @param MoodleQuickForm $mform the form being built.
*/ */
function definition_inner(&$mform) { function definition_inner(&$mform) {
$mform->addElement('htmleditor', 'feedback', get_string("feedback", "quiz"), $mform->addElement('editor', 'feedback', get_string('feedback', 'quiz'), null, $this->editoroptions);
array('course' => $this->coursefilesid));
$mform->setType('feedback', PARAM_RAW); $mform->setType('feedback', PARAM_RAW);
$mform->addElement('hidden', 'fraction', 0); $mform->addElement('hidden', 'fraction', 0);
@ -32,17 +47,29 @@ class question_edit_essay_form extends question_edit_form {
$mform->setType('penalty', PARAM_RAW); $mform->setType('penalty', PARAM_RAW);
} }
function set_data($question) { function data_preprocessing($question) {
if (!empty($question->options) && !empty($question->options->answers)) { if (!empty($question->options) && !empty($question->options->answers)) {
$answer = reset($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; $question->penalty = 0;
parent::set_data($question); return $question;
} }
function qtype() { function qtype() {
return 'essay'; 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 <?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 /// /// ESSAY ///
///////////////// /////////////////
@ -21,6 +36,7 @@ class question_essay_qtype extends default_questiontype {
function save_question_options($question) { function save_question_options($question) {
global $DB; global $DB;
$context = $question->context;
$result = true; $result = true;
$update = true; $update = true;
$answer = $DB->get_record("question_answers", array("question" => $question->id)); $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; $answer->question = $question->id;
$update = false; $update = false;
} }
$answer->answer = $question->feedback; $answer->feedbackformat = $question->feedback['format'];
$answer->feedback = $question->feedback; $answer->answerformat = $question->feedback['format'];
$answer->fraction = $question->fraction; $answer->fraction = $question->fraction;
if ($update) { 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); $DB->update_record("question_answers", $answer);
} else { } 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; return $result;
} }
@ -43,12 +66,14 @@ class question_essay_qtype extends default_questiontype {
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) { function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG; global $CFG;
$answers = &$question->options->answers; $context = $this->get_context_by_category_id($question->category);
$readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
$answers = &$question->options->answers;
$readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
// Only use the rich text editor for the first essay question on a page. // Only use the rich text editor for the first essay question on a page.
$formatoptions = new stdClass; $formatoptions = new stdClass;
$formatoptions->noclean = true; $formatoptions->noclean = true;
$formatoptions->para = false; $formatoptions->para = false;
@ -60,13 +85,12 @@ class question_essay_qtype extends default_questiontype {
$question->questiontextformat, $question->questiontextformat,
$formatoptions, $cmoptions->course); $formatoptions, $cmoptions->course);
$image = get_question_image($question);
// feedback handling // feedback handling
$feedback = ''; $feedback = '';
if ($options->feedback && !empty($answers)) { if ($options->feedback && !empty($answers)) {
foreach ($answers as $answer) { 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); 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. // Restore method not needed.
} }
//// END OF CLASS //// //// 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... /// //// INITIATION - Without this line the question type is not in use... ///
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
question_register_questiontype(new question_essay_qtype()); question_register_questiontype(new question_essay_qtype());

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-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> <TABLES>
<TABLE NAME="question_match" COMMENT="Defines fixed matching questions" NEXT="question_match_sub"> <TABLE NAME="question_match" COMMENT="Defines fixed matching questions" NEXT="question_match_sub">
<FIELDS> <FIELDS>
@ -18,8 +21,9 @@
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="code"/> <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="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="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="questiontext" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="question" NEXT="questiontextformat"/>
<FIELD NAME="answertext" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="questiontext"/> <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> </FIELDS>
<KEYS> <KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/> <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/>
@ -27,4 +31,4 @@
</KEYS> </KEYS>
</TABLE> </TABLE>
</TABLES> </TABLES>
</XMLDB> </XMLDB>

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; ?> <?php echo $questiontext; ?>
</div> </div>
<?php if ($image) { ?>
<img class="qimage" src="<?php echo $image; ?>" alt="" />
<?php } ?>
<div class="ablock clearfix"> <div class="ablock clearfix">
<table class="answer"> <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) { function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
$repeated = array(); $repeated = array();
$repeated[] =& $mform->createElement('header', 'answerhdr', $label); $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)); $repeated[] =& $mform->createElement('text', 'subanswers', get_string('answer', 'quiz'), array('size'=>50));
$repeatedoptions['subquestions']['type'] = PARAM_RAW; $repeatedoptions['subquestions']['type'] = PARAM_RAW;
$repeatedoptions['subanswers']['type'] = PARAM_TEXT; $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); $this->add_per_answer_fields($mform, get_string('questionno', 'quiz', '{no}'), 0);
} }
function set_data($question) { function data_preprocessing($question) {
if (isset($question->options)){ if (isset($question->options)) {
$subquestions = $question->options->subquestions; $subquestions = $question->options->subquestions;
if (count($subquestions)) { if (count($subquestions)) {
$key = 0; $key = 0;
foreach ($subquestions as $subquestion){ foreach ($subquestions as $subquestion){
$default_values['subanswers['.$key.']'] = $subquestion->answertext; $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++; $key++;
} }
} }
$default_values['shuffleanswers'] = $question->options->shuffleanswers; $default_values['shuffleanswers'] = $question->options->shuffleanswers;
$question = (object)((array)$question + $default_values); $question = (object)((array)$question + $default_values);
} }
parent::set_data($question); return $question;
} }
function qtype() { function qtype() {
@ -69,7 +83,7 @@ class question_edit_match_form extends question_edit_form {
$questioncount = 0; $questioncount = 0;
$answercount = 0; $answercount = 0;
foreach ($questions as $key => $question){ foreach ($questions as $key => $question){
$trimmedquestion = trim($question); $trimmedquestion = trim($question['text']);
$trimmedanswer = trim($answers[$key]); $trimmedanswer = trim($answers[$key]);
if ($trimmedquestion != ''){ if ($trimmedquestion != ''){
$questioncount++; $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 <?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 /// /// MATCH ///
///////////// /////////////
@ -24,6 +39,7 @@ class question_match_qtype extends default_questiontype {
function save_question_options($question) { function save_question_options($question) {
global $DB; global $DB;
$context = $question->context;
$result = new stdClass; $result = new stdClass;
if (!$oldsubquestions = $DB->get_records("question_match_sub", array("question" => $question->id), "id ASC")) { if (!$oldsubquestions = $DB->get_records("question_match_sub", array("question" => $question->id), "id ASC")) {
@ -35,24 +51,30 @@ class question_match_qtype extends default_questiontype {
// Insert all the new question+answer pairs // Insert all the new question+answer pairs
foreach ($question->subquestions as $key => $questiontext) { foreach ($question->subquestions as $key => $questiontext) {
$questiontext = trim($questiontext); $itemid = $questiontext['itemid'];
$format = $questiontext['format'];
$questiontext = trim($questiontext['text']);
$answertext = trim($question->subanswers[$key]); $answertext = trim($question->subanswers[$key]);
if ($questiontext != '' || $answertext != '') { if ($questiontext != '' || $answertext != '') {
if ($subquestion = array_shift($oldsubquestions)) { // Existing answer, so reuse it if ($subquestion = array_shift($oldsubquestions)) { // Existing answer, so reuse it
$subquestion->questiontext = $questiontext;
$subquestion->answertext = $answertext; $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); $DB->update_record("question_match_sub", $subquestion);
} else { } else {
$subquestion = new stdClass; $subquestion = new stdClass;
// Determine a unique random code // Determine a unique random code
$subquestion->code = rand(1,999999999); $subquestion->code = rand(1, 999999999);
while ($DB->record_exists('question_match_sub', array('code' => $subquestion->code, 'question' => $question->id))) { while ($DB->record_exists('question_match_sub', array('code' => $subquestion->code, 'question' => $question->id))) {
$subquestion->code = rand(); $subquestion->code = rand();
} }
$subquestion->question = $question->id; $subquestion->question = $question->id;
$subquestion->questiontext = $questiontext; $subquestion->questiontext = $questiontext;
$subquestion->answertext = $answertext; $subquestion->questiontextformat = $format;
$subquestion->answertext = $answertext;
$subquestion->id = $DB->insert_record("question_match_sub", $subquestion); $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; $subquestions[] = $subquestion->id;
} }
@ -228,6 +250,7 @@ class question_match_qtype extends default_questiontype {
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) { function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG, $OUTPUT; global $CFG, $OUTPUT;
$context = $this->get_context_by_category_id($question->category);
$subquestions = $state->options->subquestions; $subquestions = $state->options->subquestions;
$correctanswers = $this->get_correct_responses($question, $state); $correctanswers = $this->get_correct_responses($question, $state);
$nameprefix = $question->name_prefix; $nameprefix = $question->name_prefix;
@ -263,15 +286,14 @@ class question_match_qtype extends default_questiontype {
// Print formulation // Print formulation
$questiontext = $this->format_text($question->questiontext, $questiontext = $this->format_text($question->questiontext,
$question->questiontextformat, $cmoptions); $question->questiontextformat, $cmoptions);
$image = get_question_image($question);
// Print the input controls // Print the input controls
foreach ($subquestions as $key => $subquestion) { foreach ($subquestions as $key => $subquestion) {
if ($subquestion->questiontext !== '' && !is_null($subquestion->questiontext)) { if ($subquestion->questiontext !== '' && !is_null($subquestion->questiontext)) {
// Subquestion text: // Subquestion text:
$a = new stdClass; $a = new stdClass;
$a->text = $this->format_text($subquestion->questiontext, $text = quiz_rewrite_question_urls($subquestion->questiontext, 'pluginfile.php', $context->id, 'qtype_match', 'subquestion', array($state->attempt, $state->question), $subquestion->id);
$question->questiontextformat, $cmoptions); $a->text = $this->format_text($text, $subquestion->questiontextformat, $cmoptions);
// Drop-down list: // Drop-down list:
$menuname = $nameprefix.$subquestion->id; $menuname = $nameprefix.$subquestion->id;
@ -757,6 +779,53 @@ class question_match_qtype extends default_questiontype {
return $this->save_question($question, $form, $course); 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 //// //// 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... /// //// INITIATION - Without this line the question type is not in use... ///
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
question_register_questiontype(new question_match_qtype()); question_register_questiontype(new question_match_qtype());

View file

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

View file

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

View file

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

View file

@ -15,15 +15,15 @@
class question_edit_multianswer_form extends question_edit_form { class question_edit_multianswer_form extends question_edit_form {
// $questiondisplay will contain the qtype_multianswer_extract_question from the questiontext // $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 // $savedquestiondisplay will contain the qtype_multianswer_extract_question from the questiontext in database
var $savedquestion ; public $savedquestion ;
var $savedquestiondisplay ; public $savedquestiondisplay ;
var $used_in_quiz = false ; public $used_in_quiz = false ;
var $qtype_change = false ; public $qtype_change = false ;
var $negative_diff = 0 ; public $negative_diff = 0 ;
var $nb_of_quiz = 0; public $nb_of_quiz = 0;
var $nb_of_attempts = 0; public $nb_of_attempts = 0;
public $confirm = 0 ; public $confirm = 0 ;
public $reload = false ; public $reload = false ;
@ -35,9 +35,9 @@ class question_edit_multianswer_form extends question_edit_form {
}else { }else {
$this->reload = false ; $this->reload = false ;
} }
// $this->question = $question; // $this->question = $question;
$this->used_in_quiz =false; $this->used_in_quiz =false;
// echo "<p> question <pre>";print_r($question);echo "</pre></p>"; // echo "<p> question <pre>";print_r($question);echo "</pre></p>";
if(isset($question->id) && $question->id != 0 ){ if(isset($question->id) && $question->id != 0 ){
$this->savedquestiondisplay =fullclone($question ) ; $this->savedquestiondisplay =fullclone($question ) ;
if ($list = $DB->get_records('quiz_question_instances', array( 'question'=> $question->id))){ if ($list = $DB->get_records('quiz_question_instances', array( 'question'=> $question->id))){
@ -51,15 +51,9 @@ class question_edit_multianswer_form extends question_edit_form {
} }
} }
parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable); parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
} }
function definition_inner(&$mform) { function definition_inner(&$mform) {
$mform->addElement('hidden', 'reload', 1); $mform->addElement('hidden', 'reload', 1);
$mform->setType('reload', PARAM_INT); $mform->setType('reload', PARAM_INT);
@ -69,7 +63,7 @@ class question_edit_multianswer_form extends question_edit_form {
$mform->removeElement('defaultgrade'); $mform->removeElement('defaultgrade');
$this->confirm = optional_param('confirm','0', PARAM_RAW); $this->confirm = optional_param('confirm','0', PARAM_RAW);
// display the questions from questiontext; // display the questions from questiontext;
if ( "" != optional_param('questiontext','', PARAM_RAW)) { if ( "" != optional_param('questiontext','', PARAM_RAW)) {
$this->questiondisplay = fullclone(qtype_multianswer_extract_question(optional_param('questiontext','', PARAM_RAW))) ; $this->questiondisplay = fullclone(qtype_multianswer_extract_question(optional_param('questiontext','', PARAM_RAW))) ;
@ -80,14 +74,14 @@ class question_edit_multianswer_form extends question_edit_form {
// question->id == 0 so no stored datasets // question->id == 0 so no stored datasets
$this->questiondisplay = fullclone($this->savedquestiondisplay); $this->questiondisplay = fullclone($this->savedquestiondisplay);
foreach($this->questiondisplay->options->questions as $subquestion){ foreach($this->questiondisplay->options->questions as $subquestion){
if (!empty($subquestion)){ if (!empty($subquestion)){
$subquestion->answer = array(''); $subquestion->answer = array('');
foreach($subquestion->options->answers as $ans){ foreach($subquestion->options->answers as $ans){
$subquestion->answer[]=$ans->answer ; $subquestion->answer[]=$ans->answer ;
}
// $subquestion->answer = fullclone($subquestion->options->answers);
} }
// $subquestion->answer = fullclone($subquestion->options->answers);
} }
}
}else { }else {
$this->questiondisplay = ""; $this->questiondisplay = "";
} }
@ -97,7 +91,7 @@ class question_edit_multianswer_form extends question_edit_form {
$countsavedsubquestions =0; $countsavedsubquestions =0;
foreach($this->savedquestiondisplay->options->questions as $subquestion){ foreach($this->savedquestiondisplay->options->questions as $subquestion){
if (!empty($subquestion)){ if (!empty($subquestion)){
$countsavedsubquestions++; $countsavedsubquestions++;
} }
} }
} else { } else {
@ -108,24 +102,24 @@ class question_edit_multianswer_form extends question_edit_form {
$countsubquestions =0; $countsubquestions =0;
foreach($this->questiondisplay->options->questions as $subquestion){ foreach($this->questiondisplay->options->questions as $subquestion){
if (!empty($subquestion)){ if (!empty($subquestion)){
$countsubquestions++; $countsubquestions++;
} }
} }
} else { } else {
$countsubquestions =0; $countsubquestions =0;
}
}else{
$countsubquestions =$countsavedsubquestions ;
} }
// echo "<p> saved question $countsavedsubquestions <pre>";print_r($this->savedquestiondisplay);echo "</pre></p>"; }else{
// echo "<p> saved question $countsubquestions <pre>";print_r($this->questiondisplay);echo "</pre></p>"; $countsubquestions =$countsavedsubquestions ;
}
// echo "<p> saved question $countsavedsubquestions <pre>";print_r($this->savedquestiondisplay);echo "</pre></p>";
// echo "<p> saved question $countsubquestions <pre>";print_r($this->questiondisplay);echo "</pre></p>";
$mform->addElement('submit', 'analyzequestion', get_string('decodeverifyquestiontext','qtype_multianswer')); $mform->addElement('submit', 'analyzequestion', get_string('decodeverifyquestiontext','qtype_multianswer'));
$mform->registerNoSubmitButton('analyzequestion'); $mform->registerNoSubmitButton('analyzequestion');
echo '<div class="ablock clearfix">'; echo '<div class="ablock clearfix">';
echo '<div class=" clearfix">'; echo '<div class=" clearfix">';
if ( $this->reload ){ if ( $this->reload ){
for ($sub =1;$sub <=$countsubquestions ;$sub++) { for ($sub =1;$sub <=$countsubquestions ;$sub++) {
$this->editas[$sub] = 'unknown type'; $this->editas[$sub] = 'unknown type';
@ -135,14 +129,14 @@ class question_edit_multianswer_form extends question_edit_form {
$this->editas[$sub] = optional_param('sub_'.$sub."_".'qtype', '', PARAM_RAW); $this->editas[$sub] = optional_param('sub_'.$sub."_".'qtype', '', PARAM_RAW);
} }
$storemess = ''; $storemess = '';
if(isset($this->savedquestiondisplay->options->questions[$sub]->qtype) && if(isset($this->savedquestiondisplay->options->questions[$sub]->qtype) &&
$this->savedquestiondisplay->options->questions[$sub]->qtype != $this->questiondisplay->options->questions[$sub]->qtype ){ $this->savedquestiondisplay->options->questions[$sub]->qtype != $this->questiondisplay->options->questions[$sub]->qtype ){
$this->qtype_change = true ; $this->qtype_change = true ;
$storemess = "<font class=\"error\"> STORED QTYPE ".$question_type_names[$this->savedquestiondisplay->options->questions[$sub]->qtype]."</font >"; $storemess = "<font class=\"error\"> STORED QTYPE ".$question_type_names[$this->savedquestiondisplay->options->questions[$sub]->qtype]."</font >";
} }
$mform->addElement('header', 'subhdr'.$sub, get_string('questionno', 'quiz', $mform->addElement('header', 'subhdr'.$sub, get_string('questionno', 'quiz',
'{#'.$sub.'}').'&nbsp;'.$question_type_names[$this->questiondisplay->options->questions[$sub]->qtype].$storemess); '{#'.$sub.'}').'&nbsp;'.$question_type_names[$this->questiondisplay->options->questions[$sub]->qtype].$storemess);
$mform->addElement('static', 'sub_'.$sub."_".'questiontext', get_string('questiondefinition','qtype_multianswer'),array('cols'=>60, 'rows'=>3)); $mform->addElement('static', 'sub_'.$sub."_".'questiontext', get_string('questiondefinition','qtype_multianswer'),array('cols'=>60, 'rows'=>3));
@ -153,16 +147,16 @@ class question_edit_multianswer_form extends question_edit_form {
$mform->addElement('static', 'sub_'.$sub."_".'defaultgrade', get_string('defaultgrade', 'quiz')); $mform->addElement('static', 'sub_'.$sub."_".'defaultgrade', get_string('defaultgrade', 'quiz'));
$mform->setDefault('sub_'.$sub."_".'defaultgrade',$this->questiondisplay->options->questions[$sub]->defaultgrade); $mform->setDefault('sub_'.$sub."_".'defaultgrade',$this->questiondisplay->options->questions[$sub]->defaultgrade);
if ($this->questiondisplay->options->questions[$sub]->qtype =='shortanswer' ) { if ($this->questiondisplay->options->questions[$sub]->qtype =='shortanswer' ) {
$mform->addElement('static', 'sub_'.$sub."_".'usecase', get_string('casesensitive', 'quiz')); $mform->addElement('static', 'sub_'.$sub."_".'usecase', get_string('casesensitive', 'quiz'));
} }
if ($this->questiondisplay->options->questions[$sub]->qtype =='multichoice' ) { if ($this->questiondisplay->options->questions[$sub]->qtype =='multichoice' ) {
$mform->addElement('static', 'sub_'.$sub."_".'layout', get_string('layout', 'qtype_multianswer'),array('cols'=>60, 'rows'=>1)) ;//, $gradeoptions); $mform->addElement('static', 'sub_'.$sub."_".'layout', get_string('layout', 'qtype_multianswer'),array('cols'=>60, 'rows'=>1)) ;//, $gradeoptions);
} }
foreach ($this->questiondisplay->options->questions[$sub]->answer as $key =>$ans) { foreach ($this->questiondisplay->options->questions[$sub]->answer as $key =>$ans) {
$mform->addElement('static', 'sub_'.$sub."_".'answer['.$key.']', get_string('answer', 'quiz'), array('cols'=>60, 'rows'=>1)); $mform->addElement('static', 'sub_'.$sub."_".'answer['.$key.']', get_string('answer', 'quiz'), array('cols'=>60, 'rows'=>1));
if ($this->questiondisplay->options->questions[$sub]->qtype =='numerical' && $key == 0 ) { if ($this->questiondisplay->options->questions[$sub]->qtype =='numerical' && $key == 0 ) {
$mform->addElement('static', 'sub_'.$sub."_".'tolerance['.$key.']', get_string('acceptederror', 'quiz')) ;//, $gradeoptions); $mform->addElement('static', 'sub_'.$sub."_".'tolerance['.$key.']', get_string('acceptederror', 'quiz')) ;//, $gradeoptions);
@ -177,16 +171,14 @@ class question_edit_multianswer_form extends question_edit_form {
echo '</div>'; echo '</div>';
$this->negative_diff =$countsavedsubquestions - $countsubquestions ; $this->negative_diff =$countsavedsubquestions - $countsubquestions ;
if ( ($this->negative_diff > 0 ) ||$this->qtype_change || ($this->used_in_quiz && $this->negative_diff != 0)){ if ( ($this->negative_diff > 0 ) ||$this->qtype_change || ($this->used_in_quiz && $this->negative_diff != 0)){
$mform->addElement('header', 'additemhdr', get_string('warningquestionmodified','qtype_multianswer')); $mform->addElement('header', 'additemhdr', get_string('warningquestionmodified','qtype_multianswer'));
} }
if($this->negative_diff > 0) { if($this->negative_diff > 0) {
//$this->used_in_quiz //$this->used_in_quiz
$mform->addElement('static', 'alert1', "<strong>".get_string('questiondeleted','qtype_multianswer')."</strong>",get_string('questionsless','qtype_multianswer',$this->negative_diff));
$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'));
$mform->addElement('static', 'alert1', "<strong>".get_string('questiontypechanged','qtype_multianswer')."</strong>",get_string('questiontypechangedcomment','qtype_multianswer'));
} }
echo '</div>'; echo '</div>';
} }
@ -219,11 +211,11 @@ class question_edit_multianswer_form extends question_edit_form {
foreach ($question->options->questions as $key => $wrapped) { foreach ($question->options->questions as $key => $wrapped) {
if(!empty($wrapped)){ if(!empty($wrapped)){
// The old way of restoring the definitions is kept to gradually // The old way of restoring the definitions is kept to gradually
// update all multianswer questions // update all multianswer questions
if (empty($wrapped->questiontext)) { if (empty($wrapped->questiontext)) {
$parsableanswerdef = '{' . $wrapped->defaultgrade . ':'; $parsableanswerdef = '{' . $wrapped->defaultgrade . ':';
switch ($wrapped->qtype) { switch ($wrapped->qtype) {
case 'multichoice': case 'multichoice':
$parsableanswerdef .= 'MULTICHOICE:'; $parsableanswerdef .= 'MULTICHOICE:';
break; break;
@ -235,62 +227,62 @@ class question_edit_multianswer_form extends question_edit_form {
break; break;
default: default:
print_error('unknownquestiontype', 'question', '', $wrapped->qtype); print_error('unknownquestiontype', 'question', '', $wrapped->qtype);
} }
$separator= ''; $separator= '';
foreach ($wrapped->options->answers as $subanswer) { foreach ($wrapped->options->answers as $subanswer) {
$parsableanswerdef .= $separator $parsableanswerdef .= $separator
. '%' . round(100*$subanswer->fraction) . '%'; . '%' . round(100*$subanswer->fraction) . '%';
$parsableanswerdef .= $subanswer->answer; $parsableanswerdef .= $subanswer->answer;
if (!empty($wrapped->options->tolerance)) { if (!empty($wrapped->options->tolerance)) {
// Special for numerical answers: // Special for numerical answers:
$parsableanswerdef .= ":{$wrapped->options->tolerance}"; $parsableanswerdef .= ":{$wrapped->options->tolerance}";
// We only want tolerance for the first alternative, it will // We only want tolerance for the first alternative, it will
// be applied to all of the alternatives. // be applied to all of the alternatives.
unset($wrapped->options->tolerance); unset($wrapped->options->tolerance);
}
if ($subanswer->feedback) {
$parsableanswerdef .= "#$subanswer->feedback";
}
$separator = '~';
} }
if ($subanswer->feedback) { $parsableanswerdef .= '}';
$parsableanswerdef .= "#$subanswer->feedback"; // Fix the questiontext fields of old questions
} $DB->set_field('question', 'questiontext', $parsableanswerdef, array('id' => $wrapped->id));
$separator = '~'; } else {
$parsableanswerdef = str_replace('&#', '&\#', $wrapped->questiontext);
} }
$parsableanswerdef .= '}'; $question->questiontext = str_replace("{#$key}", $parsableanswerdef, $question->questiontext);
// Fix the questiontext fields of old questions
$DB->set_field('question', 'questiontext', $parsableanswerdef, array('id' => $wrapped->id));
} else {
$parsableanswerdef = str_replace('&#', '&\#', $wrapped->questiontext);
} }
$question->questiontext = str_replace("{#$key}", $parsableanswerdef, $question->questiontext);
} }
} }
}
// set default to $questiondisplay questions elements // set default to $questiondisplay questions elements
if ( $this->reload ){ if ( $this->reload ){
if (isset($this->questiondisplay->options->questions)) { if (isset($this->questiondisplay->options->questions)) {
$subquestions = fullclone($this->questiondisplay->options->questions) ; $subquestions = fullclone($this->questiondisplay->options->questions) ;
if (count($subquestions)) { if (count($subquestions)) {
$sub =1; $sub =1;
foreach ($subquestions as $subquestion) { foreach ($subquestions as $subquestion) {
$prefix = 'sub_'.$sub.'_' ; $prefix = 'sub_'.$sub.'_' ;
// validate parameters // validate parameters
$answercount = 0; $answercount = 0;
$maxgrade = false; $maxgrade = false;
$maxfraction = -1; $maxfraction = -1;
if ($subquestion->qtype =='shortanswer' ) { if ($subquestion->qtype =='shortanswer' ) {
switch ($subquestion->usecase) { switch ($subquestion->usecase) {
case '1': case '1':
$default_values[$prefix.'usecase']= get_string('caseyes', 'quiz'); $default_values[$prefix.'usecase']= get_string('caseyes', 'quiz');
break; break;
case '0': case '0':
default : default :
$default_values[$prefix.'usecase']= get_string('caseno', 'quiz'); $default_values[$prefix.'usecase']= get_string('caseno', 'quiz');
}
} }
}
if ($subquestion->qtype == 'multichoice' ) { if ($subquestion->qtype == 'multichoice' ) {
$default_values[$prefix.'layout'] = $subquestion->layout ; $default_values[$prefix.'layout'] = $subquestion->layout ;
switch ($subquestion->layout) { switch ($subquestion->layout) {
case '0': case '0':
$default_values[$prefix.'layout']= get_string('layoutselectinline', 'qtype_multianswer'); $default_values[$prefix.'layout']= get_string('layoutselectinline', 'qtype_multianswer');
break; break;
@ -302,53 +294,53 @@ class question_edit_multianswer_form extends question_edit_form {
break; break;
default: default:
$default_values[$prefix.'layout']= get_string('layoutundefined', 'qtype_multianswer'); $default_values[$prefix.'layout']= get_string('layoutundefined', 'qtype_multianswer');
}
}
foreach ($subquestion->answer as $key=>$answer) {
if ( $subquestion->qtype == 'numerical' && $key == 0 ) {
$default_values[$prefix.'tolerance['.$key.']'] = $subquestion->tolerance[0] ;
}
$trimmedanswer = trim($answer);
if ($trimmedanswer !== '') {
$answercount++;
if ($subquestion->qtype == 'numerical' && !(is_numeric($trimmedanswer) || $trimmedanswer == '*')) {
$this->_form->setElementError($prefix.'answer['.$key.']' , get_string('answermustbenumberorstar', 'qtype_numerical'));
}
if ($subquestion->fraction[$key] == 1) {
$maxgrade = true;
}
if ($subquestion->fraction[$key] > $maxfraction) {
$maxfraction = $subquestion->fraction[$key] ;
} }
} }
foreach ($subquestion->answer as $key=>$answer) {
if ( $subquestion->qtype == 'numerical' && $key == 0 ) {
$default_values[$prefix.'tolerance['.$key.']'] = $subquestion->tolerance[0] ;
}
$trimmedanswer = trim($answer);
if ($trimmedanswer !== '') {
$answercount++;
if ($subquestion->qtype == 'numerical' && !(is_numeric($trimmedanswer) || $trimmedanswer == '*')) {
$this->_form->setElementError($prefix.'answer['.$key.']' , get_string('answermustbenumberorstar', 'qtype_numerical'));
}
if ($subquestion->fraction[$key] == 1) {
$maxgrade = true;
}
if ($subquestion->fraction[$key] > $maxfraction) {
$maxfraction = $subquestion->fraction[$key] ;
}
}
$default_values[$prefix.'answer['.$key.']'] = htmlspecialchars ($answer); $default_values[$prefix.'answer['.$key.']'] = htmlspecialchars ($answer);
}
if ($answercount == 0) {
if ($subquestion->qtype == 'multichoice' ) {
$this->_form->setElementError($prefix.'answer[0]' , get_string('notenoughanswers', 'qtype_multichoice', 2));
} else {
$this->_form->setElementError($prefix.'answer[0]' , get_string('notenoughanswers', 'quiz', 1));
} }
} if ($answercount == 0) {
if ($maxgrade == false) { if ($subquestion->qtype == 'multichoice' ) {
$this->_form->setElementError($prefix.'fraction[0]' ,get_string('fractionsnomax', 'question')); $this->_form->setElementError($prefix.'answer[0]' , get_string('notenoughanswers', 'qtype_multichoice', 2));
} } else {
foreach ($subquestion->feedback as $key=>$answer) { $this->_form->setElementError($prefix.'answer[0]' , get_string('notenoughanswers', 'quiz', 1));
}
}
if ($maxgrade == false) {
$this->_form->setElementError($prefix.'fraction[0]' ,get_string('fractionsnomax', 'question'));
}
foreach ($subquestion->feedback as $key=>$answer) {
$default_values[$prefix.'feedback['.$key.']'] = htmlspecialchars ($answer); $default_values[$prefix.'feedback['.$key.']'] = htmlspecialchars ($answer);
} }
foreach ( $subquestion->fraction as $key=>$answer) { foreach ( $subquestion->fraction as $key=>$answer) {
$default_values[$prefix.'fraction['.$key.']'] = $answer; $default_values[$prefix.'fraction['.$key.']'] = $answer;
} }
$sub++; $sub++;
}
} }
} }
} }
} $default_values['alertas']= "<strong>".get_string('questioninquiz','qtype_multianswer')."</strong>";
$default_values['alertas']= "<strong>".get_string('questioninquiz','qtype_multianswer')."</strong>";
if( $default_values != "") { if( $default_values != "") {
$question = (object)((array)$question + $default_values); $question = (object)((array)$question + $default_values);
@ -358,14 +350,11 @@ class question_edit_multianswer_form extends question_edit_form {
function validation($data, $files) { function validation($data, $files) {
$errors = parent::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)) { if (isset($questiondisplay->options->questions)) {
$subquestions = fullclone($questiondisplay->options->questions) ;
$subquestions = fullclone($questiondisplay->options->questions) ;
if (count($subquestions)) { if (count($subquestions)) {
$sub =1; $sub =1;
foreach ($subquestions as $subquestion) { foreach ($subquestions as $subquestion) {
@ -373,17 +362,17 @@ class question_edit_multianswer_form extends question_edit_form {
$answercount = 0; $answercount = 0;
$maxgrade = false; $maxgrade = false;
$maxfraction = -1; $maxfraction = -1;
if(isset($this->savedquestiondisplay->options->questions[$sub]->qtype) && if(isset($this->savedquestiondisplay->options->questions[$sub]->qtype) &&
$this->savedquestiondisplay->options->questions[$sub]->qtype != $questiondisplay->options->questions[$sub]->qtype ){ $this->savedquestiondisplay->options->questions[$sub]->qtype != $questiondisplay->options->questions[$sub]->qtype ){
$storemess = " STORED QTYPE ".$question_type_names[$this->savedquestiondisplay->options->questions[$sub]->qtype]; $storemess = " STORED QTYPE ".$question_type_names[$this->savedquestiondisplay->options->questions[$sub]->qtype];
} }
foreach ( $subquestion->answer as $key=>$answer) { foreach ( $subquestion->answer as $key=>$answer) {
$trimmedanswer = trim($answer); $trimmedanswer = trim($answer);
if ($trimmedanswer !== '') { if ($trimmedanswer !== '') {
$answercount++; $answercount++;
if ($subquestion->qtype =='numerical' && !(is_numeric($trimmedanswer) || $trimmedanswer == '*')) { if ($subquestion->qtype =='numerical' && !(is_numeric($trimmedanswer) || $trimmedanswer == '*')) {
$errors[$prefix.'answer['.$key.']']= get_string('answermustbenumberorstar', 'qtype_numerical'); $errors[$prefix.'answer['.$key.']']= get_string('answermustbenumberorstar', 'qtype_numerical');
} }
if ($subquestion->fraction[$key] == 1) { if ($subquestion->fraction[$key] == 1) {
$maxgrade = true; $maxgrade = true;
} }
@ -408,18 +397,17 @@ class question_edit_multianswer_form extends question_edit_form {
$errors['questiontext']=get_string('questionsmissing', 'qtype_multianswer'); $errors['questiontext']=get_string('questionsmissing', 'qtype_multianswer');
} }
} }
// $question = qtype_multianswer_extract_question($data['questiontext']); // $question = qtype_multianswer_extract_question($data['questiontext']);
// if (isset $question->options->questions // if (isset $question->options->questions
if (( $this->negative_diff > 0 || $this->used_in_quiz && ($this->negative_diff > 0 ||$this->negative_diff < 0 || $this->qtype_change ))&& $this->confirm == 0 ){ if (( $this->negative_diff > 0 || $this->used_in_quiz && ($this->negative_diff > 0 ||$this->negative_diff < 0 || $this->qtype_change ))&& $this->confirm == 0 ){
$errors['confirm']=get_string('confirmsave', 'qtype_multianswer',$this->negative_diff); $errors['confirm']=get_string('confirmsave', 'qtype_multianswer',$this->negative_diff);
} }
return $errors; return $errors;
} }
function qtype() { function qtype() {
return 'multianswer'; return 'multianswer';
} }
} }

View file

@ -307,10 +307,6 @@ class embedded_cloze_qtype extends default_questiontype {
} }
echo '<div class="ablock clearfix">'; 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, $qtextremaining = format_text($question->questiontext,
$question->questiontextformat, $formatoptions, $cmoptions->course); $question->questiontextformat, $formatoptions, $cmoptions->course);
@ -986,9 +982,7 @@ function qtype_multianswer_extract_question($text) {
$question->options->questions = array(); $question->options->questions = array();
$question->defaultgrade = 0; // Will be increased for each answer norm $question->defaultgrade = 0; // Will be increased for each answer norm
for ($positionkey=1 for ($positionkey=1; preg_match('/'.ANSWER_REGEX.'/', $question->questiontext['text'], $answerregs); ++$positionkey ) {
; preg_match('/'.ANSWER_REGEX.'/', $question->questiontext, $answerregs)
; ++$positionkey ) {
$wrapped = new stdClass; $wrapped = new stdClass;
if (isset($answerregs[ANSWER_REGEX_NORM])&& $answerregs[ANSWER_REGEX_NORM]!== ''){ if (isset($answerregs[ANSWER_REGEX_NORM])&& $answerregs[ANSWER_REGEX_NORM]!== ''){
$wrapped->defaultgrade = $answerregs[ANSWER_REGEX_NORM]; $wrapped->defaultgrade = $answerregs[ANSWER_REGEX_NORM];
@ -1087,4 +1081,3 @@ function qtype_multianswer_extract_question($text) {
$question->questiontext = $question->questiontext; $question->questiontext = $question->questiontext;
return $question; return $question;
} }
?>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?> <?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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd" 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="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="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="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="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any correct response." PREVIOUS="shuffleanswers" NEXT="correctfeedbackformat"/>
<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="correctfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="correctfeedback" NEXT="partiallycorrectfeedback"/>
<FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="partiallycorrectfeedback" NEXT="answernumbering"/> <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="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="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> </FIELDS>
<KEYS> <KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/> <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/>
@ -23,4 +26,4 @@
</KEYS> </KEYS>
</TABLE> </TABLE>
</TABLES> </TABLES>
</XMLDB> </XMLDB>

View file

@ -52,6 +52,61 @@ function xmldb_qtype_multichoice_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2008021800, 'qtype', 'multichoice'); 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; return true;
} }

View file

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

View file

@ -1,4 +1,22 @@
<?php <?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. * 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')); $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) { foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
$mform->addElement('htmleditor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'), $mform->addElement('editor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'),
array('course' => $this->coursefilesid)); array('course' => $this->coursefilesid), $this->editoroptions);
$mform->setType($feedbackname, PARAM_RAW); $mform->setType($feedbackname, PARAM_RAW);
} }
} }
function set_data($question) { function data_preprocessing($question) {
if (isset($question->options)){ if (isset($question->options)){
$answers = $question->options->answers; $answers = $question->options->answers;
if (count($answers)) { if (count($answers)) {
@ -62,19 +80,44 @@ class question_edit_multichoice_form extends question_edit_form {
foreach ($answers as $answer){ foreach ($answers as $answer){
$default_values['answer['.$key.']'] = $answer->answer; $default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction; $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++; $key++;
} }
} }
$default_values['single'] = $question->options->single; $default_values['single'] = $question->options->single;
$default_values['answernumbering'] = $question->options->answernumbering; $default_values['answernumbering'] = $question->options->answernumbering;
$default_values['shuffleanswers'] = $question->options->shuffleanswers; $default_values['shuffleanswers'] = $question->options->shuffleanswers;
$default_values['correctfeedback'] = $question->options->correctfeedback;
$default_values['partiallycorrectfeedback'] = $question->options->partiallycorrectfeedback; // prepare feedback editor to display files in draft area
$default_values['incorrectfeedback'] = $question->options->incorrectfeedback; 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); $question = (object)((array)$question + $default_values);
} }
parent::set_data($question); return $question;
} }
function qtype() { 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

@ -29,8 +29,8 @@ class question_multichoice_qtype extends default_questiontype {
list ($usql, $params) = $DB->get_in_or_equal(explode(',', $question->options->answers)); list ($usql, $params) = $DB->get_in_or_equal(explode(',', $question->options->answers));
if (!$question->options->answers = $DB->get_records_select('question_answers', "id $usql", $params, 'id')) { if (!$question->options->answers = $DB->get_records_select('question_answers', "id $usql", $params, 'id')) {
echo $OUTPUT->notification('Error: Missing question answers for multichoice question'.$question->id.'!'); echo $OUTPUT->notification('Error: Missing question answers for multichoice question'.$question->id.'!');
return false; return false;
} }
return true; return true;
@ -38,6 +38,7 @@ class question_multichoice_qtype extends default_questiontype {
function save_question_options($question) { function save_question_options($question) {
global $DB; global $DB;
$context = $question->context;
$result = new stdClass; $result = new stdClass;
if (!$oldanswers = $DB->get_records("question_answers", array("question" => $question->id), "id ASC")) { if (!$oldanswers = $DB->get_records("question_answers", array("question" => $question->id), "id ASC")) {
$oldanswers = array(); $oldanswers = array();
@ -65,18 +66,23 @@ class question_multichoice_qtype extends default_questiontype {
foreach ($question->answer as $key => $dataanswer) { foreach ($question->answer as $key => $dataanswer) {
if ($dataanswer != "") { if ($dataanswer != "") {
$feedbackformat = $question->feedback[$key]['format'];
if ($answer = array_shift($oldanswers)) { // Existing answer, so reuse it if ($answer = array_shift($oldanswers)) { // Existing answer, so reuse it
$answer->answer = $dataanswer; $answer->answer = $dataanswer;
$answer->fraction = $question->fraction[$key]; $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); $DB->update_record("question_answers", $answer);
} else { } else {
unset($answer); unset($answer);
$answer->answer = $dataanswer; $answer->answer = $dataanswer;
$answer->question = $question->id; $answer->question = $question->id;
$answer->fraction = $question->fraction[$key]; $answer->fraction = $question->fraction[$key];
$answer->feedback = $question->feedback[$key]; $answer->feedback = '';
$answer->feedbackformat = $feedbackformat;
$answer->id = $DB->insert_record("question_answers", $answer); $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; $answers[] = $answer->id;
@ -100,13 +106,19 @@ class question_multichoice_qtype extends default_questiontype {
$options->answers = implode(",",$answers); $options->answers = implode(",",$answers);
$options->single = $question->single; $options->single = $question->single;
if(isset($question->layout)){ if(isset($question->layout)){
$options->layout = $question->layout; $options->layout = $question->layout;
} }
$options->answernumbering = $question->answernumbering; $options->answernumbering = $question->answernumbering;
$options->shuffleanswers = $question->shuffleanswers; $options->shuffleanswers = $question->shuffleanswers;
$options->correctfeedback = trim($question->correctfeedback);
$options->partiallycorrectfeedback = trim($question->partiallycorrectfeedback); foreach (array('correct', 'partiallycorrect', 'incorrect') as $feedbacktype) {
$options->incorrectfeedback = trim($question->incorrectfeedback); $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) { if ($update) {
$DB->update_record("question_multichoice", $options); $DB->update_record("question_multichoice", $options);
} else { } else {
@ -139,11 +151,11 @@ class question_multichoice_qtype extends default_questiontype {
} }
/** /**
* Deletes question from the question-type specific tables * Deletes question from the question-type specific tables
* *
* @return boolean Success/Failure * @return boolean Success/Failure
* @param object $question The question being deleted * @param object $question The question being deleted
*/ */
function delete_question($questionid) { function delete_question($questionid) {
global $DB; global $DB;
$DB->delete_records("question_multichoice", array("question" => $questionid)); $DB->delete_records("question_multichoice", array("question" => $questionid));
@ -153,10 +165,10 @@ class question_multichoice_qtype extends default_questiontype {
function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) { function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
// create an array of answerids ??? why so complicated ??? // create an array of answerids ??? why so complicated ???
$answerids = array_values(array_map(create_function('$val', $answerids = array_values(array_map(create_function('$val',
'return $val->id;'), $question->options->answers)); 'return $val->id;'), $question->options->answers));
// Shuffle the answers if required // Shuffle the answers if required
if (!empty($cmoptions->shuffleanswers) and !empty($question->options->shuffleanswers)) { if (!empty($cmoptions->shuffleanswers) and !empty($question->options->shuffleanswers)) {
$answerids = swapshuffle($answerids); $answerids = swapshuffle($answerids);
} }
$state->options->order = $answerids; $state->options->order = $answerids;
// Create empty responses // Create empty responses
@ -203,7 +215,7 @@ class question_multichoice_qtype extends default_questiontype {
$state->responses = array_flip($state->responses); $state->responses = array_flip($state->responses);
// Set the value of each element to be equal to the index // Set the value of each element to be equal to the index
array_walk($state->responses, create_function('&$a, $b', array_walk($state->responses, create_function('&$a, $b',
'$a = $b;')); '$a = $b;'));
} }
} }
return true; return true;
@ -252,6 +264,10 @@ class question_multichoice_qtype extends default_questiontype {
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) { function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG; global $CFG;
// required by file api
$context = $this->get_context_by_category_id($question->category);
$component = 'qtype_' . $question->qtype;
$answers = &$question->options->answers; $answers = &$question->options->answers;
$correctanswers = $this->get_correct_responses($question, $state); $correctanswers = $this->get_correct_responses($question, $state);
$readonly = empty($options->readonly) ? '' : 'disabled="disabled"'; $readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
@ -261,10 +277,8 @@ class question_multichoice_qtype extends default_questiontype {
$formatoptions->para = false; $formatoptions->para = false;
// Print formulation // Print formulation
$questiontext = format_text($question->questiontext, $questiontext = format_text($question->questiontext, $question->questiontextformat,
$question->questiontextformat, $formatoptions, $cmoptions->course);
$formatoptions, $cmoptions->course);
$image = get_question_image($question);
$answerprompt = ($question->options->single) ? get_string('singleanswer', 'quiz') : $answerprompt = ($question->options->single) ? get_string('singleanswer', 'quiz') :
get_string('multipleanswers', 'quiz'); get_string('multipleanswers', 'quiz');
@ -311,11 +325,13 @@ class question_multichoice_qtype extends default_questiontype {
// Print the answer text // Print the answer text
$a->text = $this->number_in_style($key, $question->options->answernumbering) . $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 // Print feedback if feedback is on
if (($options->feedback || $options->correct_responses) && $checked) { 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 { } else {
$a->feedback = ''; $a->feedback = '';
} }
@ -327,14 +343,18 @@ class question_multichoice_qtype extends default_questiontype {
if ($options->feedback) { if ($options->feedback) {
if ($state->raw_grade >= $question->maxgrade/1.01) { if ($state->raw_grade >= $question->maxgrade/1.01) {
$feedback = $question->options->correctfeedback; $feedback = $question->options->correctfeedback;
$feedbacktype = 'correctfeedback';
} else if ($state->raw_grade > 0) { } else if ($state->raw_grade > 0) {
$feedback = $question->options->partiallycorrectfeedback; $feedback = $question->options->partiallycorrectfeedback;
$feedbacktype = 'partiallycorrectfeedback';
} else { } else {
$feedback = $question->options->incorrectfeedback; $feedback = $question->options->incorrectfeedback;
$feedbacktype = 'incorrectfeedback';
} }
$feedback = format_text($feedback,
$question->questiontextformat, $feedback = quiz_rewrite_question_urls($feedback, 'pluginfile.php', $context->id, $component, $feedbacktype, array($state->attempt, $state->question), $question->id);
$formatoptions, $cmoptions->course); $feedbackformat = $feedbacktype . 'format';
$feedback = format_text($feedback, $question->options->$feedbackformat, $formatoptions, $cmoptions->course);
} }
include("$CFG->dirroot/question/type/multichoice/display.html"); include("$CFG->dirroot/question/type/multichoice/display.html");
@ -357,7 +377,7 @@ class question_multichoice_qtype extends default_questiontype {
// Make sure we don't assign negative or too high marks // Make sure we don't assign negative or too high marks
$state->raw_grade = min(max((float) $state->raw_grade, $state->raw_grade = min(max((float) $state->raw_grade,
0.0), 1.0) * $question->maxgrade; 0.0), 1.0) * $question->maxgrade;
// Apply the penalty for this attempt // Apply the penalty for this attempt
$state->penalty = $question->penalty * $question->maxgrade; $state->penalty = $question->penalty * $question->maxgrade;
@ -401,7 +421,7 @@ class question_multichoice_qtype extends default_questiontype {
} }
return $totalfraction / count($question->options->answers); return $totalfraction / count($question->options->answers);
} }
/// BACKUP FUNCTIONS //////////////////////////// /// BACKUP FUNCTIONS ////////////////////////////
/* /*
* Backup the data in the question * Backup the data in the question
@ -436,7 +456,7 @@ class question_multichoice_qtype extends default_questiontype {
return $status; return $status;
} }
/// RESTORE FUNCTIONS ///////////////// /// RESTORE FUNCTIONS /////////////////
/* /*
* Restores the data in the question * Restores the data in the question
@ -575,31 +595,31 @@ class question_multichoice_qtype extends default_questiontype {
// Decode links in the question_multichoice table. // Decode links in the question_multichoice table.
if ($multichoices = $DB->get_records_list('question_multichoice', 'question', if ($multichoices = $DB->get_records_list('question_multichoice', 'question',
$questionids, '', 'id, correctfeedback, partiallycorrectfeedback, incorrectfeedback')) { $questionids, '', 'id, correctfeedback, partiallycorrectfeedback, incorrectfeedback')) {
foreach ($multichoices as $multichoice) { foreach ($multichoices as $multichoice) {
$correctfeedback = restore_decode_content_links_worker($multichoice->correctfeedback, $restore); $correctfeedback = restore_decode_content_links_worker($multichoice->correctfeedback, $restore);
$partiallycorrectfeedback = restore_decode_content_links_worker($multichoice->partiallycorrectfeedback, $restore); $partiallycorrectfeedback = restore_decode_content_links_worker($multichoice->partiallycorrectfeedback, $restore);
$incorrectfeedback = restore_decode_content_links_worker($multichoice->incorrectfeedback, $restore); $incorrectfeedback = restore_decode_content_links_worker($multichoice->incorrectfeedback, $restore);
if ($correctfeedback != $multichoice->correctfeedback || if ($correctfeedback != $multichoice->correctfeedback ||
$partiallycorrectfeedback != $multichoice->partiallycorrectfeedback || $partiallycorrectfeedback != $multichoice->partiallycorrectfeedback ||
$incorrectfeedback != $multichoice->incorrectfeedback) { $incorrectfeedback != $multichoice->incorrectfeedback) {
$subquestion->correctfeedback = $correctfeedback; $subquestion->correctfeedback = $correctfeedback;
$subquestion->partiallycorrectfeedback = $partiallycorrectfeedback; $subquestion->partiallycorrectfeedback = $partiallycorrectfeedback;
$subquestion->incorrectfeedback = $incorrectfeedback; $subquestion->incorrectfeedback = $incorrectfeedback;
$DB->update_record('question_multichoice', $multichoice); $DB->update_record('question_multichoice', $multichoice);
} }
// Do some output. // Do some output.
if (++$i % 5 == 0 && !defined('RESTORE_SILENTLY')) { if (++$i % 5 == 0 && !defined('RESTORE_SILENTLY')) {
echo "."; echo ".";
if ($i % 100 == 0) { if ($i % 100 == 0) {
echo "<br />"; echo "<br />";
}
backup_flush(300);
} }
backup_flush(300);
} }
} }
}
return $status; return $status;
} }
@ -625,16 +645,16 @@ class question_multichoice_qtype extends default_questiontype {
*/ */
function number_in_style($num, $style) { function number_in_style($num, $style) {
switch($style) { switch($style) {
case 'abc': case 'abc':
return $this->number_html(chr(ord('a') + $num)); return $this->number_html(chr(ord('a') + $num));
case 'ABCD': case 'ABCD':
return $this->number_html(chr(ord('A') + $num)); return $this->number_html(chr(ord('A') + $num));
case '123': case '123':
return $this->number_html(($num + 1)); return $this->number_html(($num + 1));
case 'none': case 'none':
return ''; return '';
default: default:
return 'ERR'; return 'ERR';
} }
} }
@ -708,8 +728,82 @@ class question_multichoice_qtype extends default_questiontype {
return $this->save_question($question, $form, $course); 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. // Register this question type with the question bank.
question_register_questiontype(new question_multichoice_qtype()); question_register_questiontype(new question_multichoice_qtype());

View file

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

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?> <?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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd" xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
> >
@ -23,8 +23,9 @@
<FIELDS> <FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="question"/> <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="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="instructions" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="question" NEXT="instructionsformat"/>
<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="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="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="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"/> <FIELD NAME="unitpenalty" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="true" DEFAULT="0.1" SEQUENCE="false" DECIMALS="7" PREVIOUS="unitgradingtype"/>
@ -50,4 +51,4 @@
</INDEXES> </INDEXES>
</TABLE> </TABLE>
</TABLES> </TABLES>
</XMLDB> </XMLDB>

View file

@ -51,6 +51,35 @@ function xmldb_qtype_numerical_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2009100100, 'qtype', 'numerical'); 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; return true;
} }

View file

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

View file

@ -1,4 +1,20 @@
<?php <?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. * Defines the editing form for the numerical question type.
* *
@ -39,30 +55,36 @@ class question_edit_numerical_form extends question_edit_form {
$creategrades->gradeoptions); $creategrades->gradeoptions);
//------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------
$QTYPES['numerical']->add_units_options($mform,$this); $QTYPES['numerical']->add_units_options($mform,$this);
$QTYPES['numerical']->add_units_elements($mform,$this); $QTYPES['numerical']->add_units_elements($mform,$this);
} }
function set_data($question) { function data_preprocessing($question) {
global $QTYPES ; global $QTYPES ;
if (isset($question->options)){ 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; $answers = $question->options->answers;
if (count($answers)) { if (count($answers)) {
$key = 0; $key = 0;
foreach ($answers as $answer){ foreach ($answers as $answer){
$draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
$default_values['answer['.$key.']'] = $answer->answer; $default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction; $default_values['fraction['.$key.']'] = $answer->fraction;
$default_values['tolerance['.$key.']'] = $answer->tolerance; $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++; $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)){ /* if (isset($question->options->units)){
$units = array_values($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); $question = (object)((array)$question + $default_values);
} }
parent::set_data($question); return $question;
} }
function validation($data, $files) { function validation($data, $files) {
global $QTYPES; global $QTYPES;
$errors = parent::validation($data, $files); $errors = parent::validation($data, $files);
@ -95,7 +118,7 @@ class question_edit_numerical_form extends question_edit_form {
if ($data['fraction'][$key] == 1) { if ($data['fraction'][$key] == 1) {
$maxgrade = true; $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'); $errors["answer[$key]"] = get_string('answermustbenumberorstar', 'qtype_numerical');
$answercount++; $answercount++;
} }
@ -110,6 +133,7 @@ class question_edit_numerical_form extends question_edit_form {
return $errors; return $errors;
} }
function qtype() { function qtype() {
return 'numerical'; 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 <?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. * @author Martin Dougiamas and many others. Tim Hunt.
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank * @package questionbank
* @subpackage questiontypes * @subpackage questiontypes
*//** */ */
require_once("$CFG->dirroot/question/type/shortanswer/questiontype.php"); require_once("$CFG->dirroot/question/type/shortanswer/questiontype.php");
@ -19,6 +35,7 @@ require_once("$CFG->dirroot/question/type/shortanswer/questiontype.php");
* @package questionbank * @package questionbank
* @subpackage questiontypes * @subpackage questiontypes
*/ */
class question_numerical_qtype extends question_shortanswer_qtype { class question_numerical_qtype extends question_shortanswer_qtype {
public $virtualqtype = false; public $virtualqtype = false;
@ -52,7 +69,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
$this->get_numerical_units($question); $this->get_numerical_units($question);
//get_numerical_options() need to know if there are units //get_numerical_options() need to know if there are units
// to set correctly default values // to set correctly default values
$this->get_numerical_options($question); $this->get_numerical_options($question);
// If units are defined we strip off the default unit from the answer, if // If units are defined we strip off the default unit from the answer, if
@ -75,7 +92,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
if (!$options = $DB->get_record('question_numerical_options', array('question' => $question->id))) { if (!$options = $DB->get_record('question_numerical_options', array('question' => $question->id))) {
$question->options->unitgradingtype = 0; // total grade $question->options->unitgradingtype = 0; // total grade
$question->options->unitpenalty = 0; $question->options->unitpenalty = 0;
// the default // the default
if ($defaultunit = $this->get_default_numerical_unit($question)) { if ($defaultunit = $this->get_default_numerical_unit($question)) {
// so units can be graded // so units can be graded
$question->options->showunits = NUMERICALQUESTIONUNITTEXTINPUTDISPLAY ; $question->options->showunits = NUMERICALQUESTIONUNITTEXTINPUTDISPLAY ;
@ -85,18 +102,20 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$question->options->showunits = NUMERICALQUESTIONUNITNODISPLAY ; $question->options->showunits = NUMERICALQUESTIONUNITNODISPLAY ;
} }
$question->options->unitsleft = 0 ; $question->options->unitsleft = 0 ;
$question->options->instructions = '' ; $question->options->instructions = '';
$question->options->instructionsformat = editors_get_preferred_format();
} else { } else {
$question->options->unitgradingtype = $options->unitgradingtype; $question->options->unitgradingtype = $options->unitgradingtype;
$question->options->unitpenalty = $options->unitpenalty; $question->options->unitpenalty = $options->unitpenalty;
$question->options->showunits = $options->showunits ; $question->options->showunits = $options->showunits;
$question->options->unitsleft = $options->unitsleft ; $question->options->unitsleft = $options->unitsleft;
$question->options->instructions = $options->instructions ; $question->options->instructions = $options->instructions;
$question->options->instructionsformat = $options->instructionsformat;
} }
return true; return true;
} }
function get_numerical_units(&$question) { function get_numerical_units(&$question) {
global $DB; global $DB;
if ($units = $DB->get_records('question_numerical_units', array('question' => $question->id), 'id ASC')) { 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) { function save_question_options($question) {
global $DB; global $DB;
$context = $question->context;
// Get old versions of the objects // Get old versions of the objects
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) { if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array(); $oldanswers = array();
@ -148,7 +169,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
foreach ($question->answer as $key => $dataanswer) { foreach ($question->answer as $key => $dataanswer) {
// Check for, and ingore, completely blank answer from the form. // Check for, and ingore, completely blank answer from the form.
if (trim($dataanswer) == '' && $question->fraction[$key] == 0 && if (trim($dataanswer) == '' && $question->fraction[$key] == 0 &&
html_is_blank($question->feedback[$key])) { html_is_blank($question->feedback[$key]['text'])) {
continue; continue;
} }
@ -163,13 +184,23 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
} }
$answer->fraction = $question->fraction[$key]; $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 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; $answer->id = $oldanswer->id;
$DB->update_record("question_answers", $answer); $DB->update_record("question_answers", $answer);
} else { // This is a completely new answer } else { // This is a completely new answer
$answer->feedback = $feedbacktext;
$answer->id = $DB->insert_record("question_answers", $answer); $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 // Set up the options object
@ -220,10 +251,11 @@ class question_numerical_qtype extends question_shortanswer_qtype {
function save_numerical_options(&$question) { function save_numerical_options(&$question) {
global $DB; global $DB;
$result = new stdClass; $result = new stdClass;
// numerical options // numerical options
$update = true ; $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) { if (!$options) {
$update = false; $update = false;
$options = new stdClass; $options = new stdClass;
@ -246,7 +278,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}else { }else {
$options->showunits = $question->showunits1; $options->showunits = $question->showunits1;
} }
}else { }else {
if(isset($question->showunits)){ if(isset($question->showunits)){
$options->showunits = $question->showunits; $options->showunits = $question->showunits;
}else { }else {
@ -264,22 +296,27 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}else { }else {
$options->unitsleft = 0 ; $options->unitsleft = 0 ;
} }
$options->instructionsformat = $question->instructions['format'];
if(isset($question->instructions)){ if(isset($question->instructions)){
$options->instructions = trim($question->instructions); $options->instructions = trim($question->instructions['text']);
}else { }else {
$options->instructions = '' ; $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 ($update) {
if (!$DB->update_record("question_numerical_options", $options)) { $DB->update_record("question_numerical_options", $options);
$result->error = "Could not update numerical question options! (id=$options->id)";
return $result;
}
} else { } else {
if (!$DB->insert_record("question_numerical_options", $options)) { $id = $DB->insert_record("question_numerical_options", $options);
$result->error = "Could not insert numerical question options!";
return $result;
}
} }
return $result; return $result;
} }
@ -320,7 +357,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$state->responses = array(); $state->responses = array();
$state->responses['answer'] = ''; $state->responses['answer'] = '';
$state->responses['unit'] = ''; $state->responses['unit'] = '';
return true; return true;
} }
function restore_session_and_responses(&$question, &$state) { function restore_session_and_responses(&$question, &$state) {
@ -356,10 +393,10 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$rawresponse = str_replace($search, $replace, trim($rawresponse)); $rawresponse = str_replace($search, $replace, trim($rawresponse));
if (preg_match('~^([+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][-+]?[0-9]+)?)([^0-9].*)?$~', if (preg_match('~^([+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][-+]?[0-9]+)?)([^0-9].*)?$~',
$rawresponse, $responseparts)) { $rawresponse, $responseparts)) {
if(isset($responseparts[5]) ){ if(isset($responseparts[5]) ){
$unit = $responseparts[5] ; $unit = $responseparts[5] ;
} }
if(isset($responseparts[1]) ){ if(isset($responseparts[1]) ){
$answer = $responseparts[1] ; $answer = $responseparts[1] ;
} }
} }
@ -404,30 +441,33 @@ class question_numerical_qtype extends question_shortanswer_qtype {
*/ */
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) { function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG; global $CFG;
$context = $this->get_context_by_category_id($question->category);
$readonly = empty($options->readonly) ? '' : 'readonly="readonly"'; $readonly = empty($options->readonly) ? '' : 'readonly="readonly"';
$formatoptions = new stdClass; $formatoptions = new stdClass;
$formatoptions->noclean = true; $formatoptions->noclean = true;
$formatoptions->para = false; $formatoptions->para = false;
$nameprefix = $question->name_prefix; $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 /// Print question text and media
$questiontext = format_text($question->questiontext, $questiontext = format_text($question->questiontext,
$question->questiontextformat, $question->questiontextformat,
$formatoptions, $cmoptions->course); $formatoptions, $cmoptions->course);
$image = get_question_image($question);
/// Print input controls /// Print input controls
// as the entry is controlled the question type here is numerical // as the entry is controlled the question type here is numerical
// In all cases there is a text input for the number // In all cases there is a text input for the number
// If $question->options->showunits == NUMERICALQUESTIONUNITTEXTDISPLAY // If $question->options->showunits == NUMERICALQUESTIONUNITTEXTDISPLAY
// there is an additional text input for the unit // there is an additional text input for the unit
// If $question->options->showunits == NUMERICALQUESTIONUNITMULTICHOICEDISPLAY" // If $question->options->showunits == NUMERICALQUESTIONUNITMULTICHOICEDISPLAY"
// radio elements display the defined unit // radio elements display the defined unit
// The code allows the input number elememt to be displayed // The code allows the input number elememt to be displayed
// before i.e. at left or after at rigth of the unit variants. // before i.e. at left or after at rigth of the unit variants.
$nameanswer = "name=\"".$question->name_prefix."answer\"";
$nameunit = "name=\"".$question->name_prefix."unit\""; $nameunit = "name=\"".$question->name_prefix."unit\"";
$nameanswer = "name=\"".$question->name_prefix."answer\"";
if (isset($state->responses['']) && $state->responses[''] != '' && !isset($state->responses['answer'])){ 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'] ); $this->split_old_answer($state->responses[''], $question->options->units, $state->responses['answer'] ,$state->responses['unit'] );
} }
@ -438,7 +478,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
if (isset($state->responses['unit']) && $state->responses['unit']!='') { if (isset($state->responses['unit']) && $state->responses['unit']!='') {
$valueunit = ' value="'.s($state->responses['unit']).'" '; $valueunit = ' value="'.s($state->responses['unit']).'" ';
} else { } else {
$valueunit = ' value="" '; $valueunit = ' value="" ';
if ($question->options->showunits == NUMERICALQUESTIONUNITTEXTDISPLAY ){ if ($question->options->showunits == NUMERICALQUESTIONUNITTEXTDISPLAY ){
$valueunit = ' value="'.s($question->options->units[0]->unit).'" '; $valueunit = ' value="'.s($question->options->units[0]->unit).'" ';
@ -464,72 +504,75 @@ class question_numerical_qtype extends question_shortanswer_qtype {
//this is OK for the first answer with a good response //this is OK for the first answer with a good response
// having to test for * so response as long as not empty // having to test for * so response as long as not empty
$response = $this->extract_numerical_response($state->responses['answer']); $response = $this->extract_numerical_response($state->responses['answer']);
$break = 0 ; $break = 0 ;
foreach($question->options->answers as $answer) { foreach($question->options->answers as $answer) {
// if * then everything has the $answer->fraction value // if * then everything has the $answer->fraction value
if ($answer->answer !== '*' ) { if ($answer->answer !== '*' ) {
$this->get_tolerance_interval($answer); $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 === '*') { if ($answer->answer === '*') {
$answerasterisk = true ; $answerasterisk = true ;
$rawgrade = $answer->fraction ; $rawgrade = $answer->fraction ;
$class = question_get_feedback_class($answer->fraction); $class = question_get_feedback_class($answer->fraction);
$feedbackimg = question_get_feedback_image($answer->fraction); $feedbackimg = question_get_feedback_image($answer->fraction);
$classunitvalue = $class ; $classunitvalue = $class ;
$classunit = question_get_feedback_class($answer->fraction); $classunit = question_get_feedback_class($answer->fraction);
$feedbackimgunit = question_get_feedback_image($answer->fraction, $options->feedback); $feedbackimgunit = question_get_feedback_image($answer->fraction, $options->feedback);
if ($answer->feedback) { if ($answer->feedback) {
$feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course); $feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course);
} }
if ( isset($question->options->units)) if ( isset($question->options->units))
{ {
$valid_numerical_unit = true ; $valid_numerical_unit = true ;
} }
$break = 1 ; $break = 1 ;
} else if ($response !== false && isset($question->options->units) && count($question->options->units) > 0) { } else if ($response !== false && isset($question->options->units) && count($question->options->units) > 0) {
$hasunits = 1 ; $hasunits = 1 ;
foreach($question->options->units as $key => $unit){ foreach($question->options->units as $key => $unit){
// The student did type a number, so check it with tolerances. // The student did type a number, so check it with tolerances.
$testresponse = $response /$unit->multiplier ; $testresponse = $response /$unit->multiplier ;
if($answer->min <= $testresponse && $testresponse <= $answer->max) { if($answer->min <= $testresponse && $testresponse <= $answer->max) {
$unittested = $unit->unit ; $unittested = $unit->unit ;
$rawgrade = $answer->fraction ; $rawgrade = $answer->fraction ;
$class = question_get_feedback_class($answer->fraction); $class = question_get_feedback_class($answer->fraction);
$feedbackimg = question_get_feedback_image($answer->fraction); $feedbackimg = question_get_feedback_image($answer->fraction);
if ($answer->feedback) { if ($answer->feedback) {
$feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course); $feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course);
} }
if($state->responses['unit'] == $unit->unit){ if($state->responses['unit'] == $unit->unit){
$classunitvalue = $answer->fraction ; $classunitvalue = $answer->fraction ;
}else { }else {
$classunitvalue == 0 ; $classunitvalue == 0 ;
} }
$classunit = question_get_feedback_class($classunitvalue); $classunit = question_get_feedback_class($classunitvalue);
$feedbackimgunit = question_get_feedback_image($classunitvalue, $options->feedback); $feedbackimgunit = question_get_feedback_image($classunitvalue, $options->feedback);
$break = 1 ; $break = 1 ;
break; break;
} }
} }
}else if($response !== false && ($answer->min <= $response && $response <= $answer->max) ) { } else if($response !== false && ($answer->min <= $response && $response <= $answer->max) ) {
$rawgrade = $answer->fraction ; $rawgrade = $answer->fraction ;
$class = question_get_feedback_class($answer->fraction); $class = question_get_feedback_class($answer->fraction);
$feedbackimg = question_get_feedback_image($answer->fraction); $feedbackimg = question_get_feedback_image($answer->fraction);
if ($answer->feedback) { if ($answer->feedback) {
$feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course); $feedback = format_text($answer->feedback, $answer->feedbackformat, $formatoptions, $cmoptions->course);
} }
$break = 1 ; $break = 1 ;
} }
if ($break) break; if ($break) {
break;
}
} }
} }
$state->options->raw_unitpenalty = 0 ; $state->options->raw_unitpenalty = 0 ;
$raw_unitpenalty = 0 ; $raw_unitpenalty = 0 ;
if( $question->options->showunits == NUMERICALQUESTIONUNITNODISPLAY || if( $question->options->showunits == NUMERICALQUESTIONUNITNODISPLAY ||
$question->options->showunits == NUMERICALQUESTIONUNITTEXTDISPLAY ) { $question->options->showunits == NUMERICALQUESTIONUNITTEXTDISPLAY ) {
$classunitvalue = 1 ; $classunitvalue = 1 ;
} }
if($classunitvalue == 0){ if($classunitvalue == 0){
if($question->options->unitgradingtype == 1){ if($question->options->unitgradingtype == 1){
@ -537,10 +580,9 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}else { }else {
$raw_unitpenalty = $question->options->unitpenalty * $question->maxgrade; $raw_unitpenalty = $question->options->unitpenalty * $question->maxgrade;
} }
$state->options->raw_unitpenalty = $raw_unitpenalty ; $state->options->raw_unitpenalty = $raw_unitpenalty ;
} }
/// Removed correct answer, to be displayed later MDL-7496 /// Removed correct answer, to be displayed later MDL-7496
include("$CFG->dirroot/question/type/numerical/display.html"); include("$CFG->dirroot/question/type/numerical/display.html");
} }
@ -580,7 +622,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
* and but NOT the unit into account. Returns a true for if a response matches the * and but NOT the unit into account. Returns a true for if a response matches the
* answer or in one of the unit , false if it doesn't. * answer or in one of the unit , false if it doesn't.
* the total grading will see if the unit match. * the total grading will see if the unit match.
* if unit != -1 then the test is done only on this unit * if unit != -1 then the test is done only on this unit
*/ */
function test_response(&$question, &$state, $answer ) { function test_response(&$question, &$state, $answer ) {
// Deal with the match anything answer. // Deal with the match anything answer.
@ -589,17 +631,17 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
/* To be able to test (old) questions that do not have an unit /* To be able to test (old) questions that do not have an unit
* input element the test is done using the $state->responses[''] * input element the test is done using the $state->responses['']
* which contains the response which is analyzed by extract_numerical_response() * which contains the response which is analyzed by extract_numerical_response()
* If the data comes from the numerical or calculated display * If the data comes from the numerical or calculated display
* the $state->responses['unit'] comes from either * the $state->responses['unit'] comes from either
* a multichoice radio element NUMERICALQUESTIONUNITMULTICHOICEDISPLAY * a multichoice radio element NUMERICALQUESTIONUNITMULTICHOICEDISPLAY
* where the $state->responses['unit'] value is the key => unit object * where the $state->responses['unit'] value is the key => unit object
* in the the $question->options->units array * in the the $question->options->units array
* or an input text element NUMERICALUNITTEXTINPUTDISPLAY * or an input text element NUMERICALUNITTEXTINPUTDISPLAY
* which contains the student response * which contains the student response
* for NUMERICALQUESTIONUNITTEXTDISPLAY and NUMERICALQUESTIONUNITNODISPLAY * for NUMERICALQUESTIONUNITTEXTDISPLAY and NUMERICALQUESTIONUNITNODISPLAY
* *
*/ */
if (!isset($state->responses['answer']) && isset($state->responses[''])){ if (!isset($state->responses['answer']) && isset($state->responses[''])){
$state->responses['answer'] = $state->responses['']; $state->responses['answer'] = $state->responses[''];
@ -611,14 +653,14 @@ class question_numerical_qtype extends question_shortanswer_qtype {
// The student did type a number, so check it with tolerances. // The student did type a number, so check it with tolerances.
$this->get_tolerance_interval($answer); $this->get_tolerance_interval($answer);
if ($answer->min <= $response && $response <= $answer->max){ if ($answer->min <= $response && $response <= $answer->max){
return true; return true;
} }
// testing for other units // testing for other units
if ( isset($question->options->units) && count($question->options->units) > 0) { if ( isset($question->options->units) && count($question->options->units) > 0) {
foreach($question->options->units as $key =>$unit){ foreach($question->options->units as $key =>$unit){
$testresponse = $response /$unit->multiplier ; $testresponse = $response /$unit->multiplier ;
if($answer->min <= $testresponse && $testresponse<= $answer->max) { if($answer->min <= $testresponse && $testresponse<= $answer->max) {
return true; return true;
} }
} }
} }
@ -626,11 +668,11 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
/** /**
* Performs response processing and grading * Performs response processing and grading
* The function was redefined for handling correctly the two parts * The function was redefined for handling correctly the two parts
* number and unit of numerical or calculated questions * number and unit of numerical or calculated questions
* The code handles also the case when there no unit defined by the user or * The code handles also the case when there no unit defined by the user or
* when used in a multianswer (Cloze) question. * when used in a multianswer (Cloze) question.
* This function performs response processing and grading and updates * This function performs response processing and grading and updates
* the state accordingly. * the state accordingly.
* @return boolean Indicates success or failure. * @return boolean Indicates success or failure.
@ -652,7 +694,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
if (!isset($state->responses['answer']) && isset($state->responses[''])){ if (!isset($state->responses['answer']) && isset($state->responses[''])){
$state->responses['answer'] = $state->responses['']; $state->responses['answer'] = $state->responses[''];
} }
//to apply the unit penalty we need to analyse the response in a more complex way //to apply the unit penalty we need to analyse the response in a more complex way
//the apply_unit() function analysis could be used to obtain the infos //the apply_unit() function analysis could be used to obtain the infos
// however it is used to detect good or bad numbers but also // however it is used to detect good or bad numbers but also
@ -664,33 +706,33 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$hasunits = 0 ; $hasunits = 0 ;
$response = $this->extract_numerical_response($state->responses['answer']); $response = $this->extract_numerical_response($state->responses['answer']);
$answerasterisk = false ; $answerasterisk = false ;
$break = 0 ; $break = 0 ;
foreach($question->options->answers as $answer) { foreach($question->options->answers as $answer) {
if ($answer->answer !== '*' ) { if ($answer->answer !== '*' ) {
// The student did type a number, so check it with tolerances. // The student did type a number, so check it with tolerances.
$this->get_tolerance_interval($answer); $this->get_tolerance_interval($answer);
} }
// if * then everything is OK even unit // if * then everything is OK even unit
if ($answer->answer === '*') { if ($answer->answer === '*') {
$state->raw_grade = $answer->fraction; $state->raw_grade = $answer->fraction;
if ( isset($question->options->units)){ if ( isset($question->options->units)){
$valid_numerical_unit = true ; $valid_numerical_unit = true ;
} }
$answerasterisk = true ; $answerasterisk = true ;
$break = 1 ; $break = 1 ;
}else if ($response !== false && isset($question->options->units) && count($question->options->units) > 0) { }else if ($response !== false && isset($question->options->units) && count($question->options->units) > 0) {
$hasunits = 1 ; $hasunits = 1 ;
foreach($question->options->units as $key => $unit){ foreach($question->options->units as $key => $unit){
$testresponse = $response /$unit->multiplier ; $testresponse = $response /$unit->multiplier ;
if($answer->min <= $testresponse && $testresponse <= $answer->max) { if($answer->min <= $testresponse && $testresponse <= $answer->max) {
$state->raw_grade = $answer->fraction; $state->raw_grade = $answer->fraction;
$unittested = $unit->unit ; $unittested = $unit->unit ;
$break = 1 ; $break = 1 ;
break; break;
} }
} }
}else if ($response !== false) { }else if ($response !== false) {
if($this->test_response($question, $state, $answer)) { if($this->test_response($question, $state, $answer)) {
@ -700,9 +742,9 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
if ($break) break; if ($break) break;
} //foreach($question->options } //foreach($question->options
// in all cases the unit should be tested // in all cases the unit should be tested
if( $question->options->showunits == NUMERICALQUESTIONUNITNODISPLAY || if( $question->options->showunits == NUMERICALQUESTIONUNITNODISPLAY ||
$question->options->showunits == NUMERICALQUESTIONUNITTEXTDISPLAY ) { $question->options->showunits == NUMERICALQUESTIONUNITTEXTDISPLAY ) {
$valid_numerical_unit = true ; $valid_numerical_unit = true ;
}else { }else {
@ -714,7 +756,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$valid_numerical_unit = true ; $valid_numerical_unit = true ;
} }
} }
// apply unit penalty // apply unit penalty
$raw_unitpenalty = 0 ; $raw_unitpenalty = 0 ;
if(!empty($question->options->unitpenalty)&& $valid_numerical_unit != true ){ if(!empty($question->options->unitpenalty)&& $valid_numerical_unit != true ){
@ -725,7 +767,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
$state->raw_grade -= $raw_unitpenalty ; $state->raw_grade -= $raw_unitpenalty ;
} }
// Make sure we don't assign negative or too high marks. // Make sure we don't assign negative or too high marks.
$state->raw_grade = min(max((float) $state->raw_grade, $state->raw_grade = min(max((float) $state->raw_grade,
0.0), 1.0) * $question->maxgrade; 0.0), 1.0) * $question->maxgrade;
@ -857,7 +899,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
// test if a . is present or there are multiple , (i.e. 2,456,789 ) so that we don't need spaces and , // test if a . is present or there are multiple , (i.e. 2,456,789 ) so that we don't need spaces and ,
if ( strpos($rawresponse,'.' ) !== false || substr_count($rawresponse,',') > 1 ) { if ( strpos($rawresponse,'.' ) !== false || substr_count($rawresponse,',') > 1 ) {
$replace = array('', ''); $replace = array('', '');
}else { // remove spaces and normalise , to a . . }else { // remove spaces and normalise , to a . .
$replace = array('', '.'); $replace = array('', '.');
} }
$rawresponse = str_replace($search, $replace, $rawresponse); $rawresponse = str_replace($search, $replace, $rawresponse);
@ -890,7 +932,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
// test if a . is present or there are multiple , (i.e. 2,456,789 ) so that we don't need spaces and , // test if a . is present or there are multiple , (i.e. 2,456,789 ) so that we don't need spaces and ,
if ( strpos($rawresponse,'.' ) !== false || substr_count($rawresponse,',') > 1 ) { if ( strpos($rawresponse,'.' ) !== false || substr_count($rawresponse,',') > 1 ) {
$replace = array('', ''); $replace = array('', '');
}else { // remove spaces and normalise , to a . . }else { // remove spaces and normalise , to a . .
$replace = array('', '.'); $replace = array('', '.');
} }
$rawresponse = str_replace($search, $replace, $rawresponse); $rawresponse = str_replace($search, $replace, $rawresponse);
@ -920,10 +962,10 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
/** /**
* function used in function definition_inner() * function used in function definition_inner()
* of edit_..._form.php for * of edit_..._form.php for
* numerical, calculated, calculatedsimple * numerical, calculated, calculatedsimple
*/ */
function add_units_options(&$mform, &$that){ function add_units_options(&$mform, &$that){
$mform->addElement('header', 'unithandling', get_string('unitshandling', 'qtype_numerical')); $mform->addElement('header', 'unithandling', get_string('unitshandling', 'qtype_numerical'));
// Units are graded // Units are graded
@ -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('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); $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->addGroup($showunits0grp, 'showunits0grp', get_string('studentunitanswer', 'qtype_numerical'),' OR ' , false);
$mform->addElement('htmleditor', 'instructions', get_string('instructions', 'qtype_numerical'), $mform->addElement('editor', 'instructions', get_string('instructions', 'qtype_numerical'), null, $that->editoroptions);
array('rows' => 10, 'course' => $that->coursefilesid));
$mform->addElement('static', 'separator1', '<HR/>', '<HR/>'); $mform->addElement('static', 'separator1', '<HR/>', '<HR/>');
// Units are not graded // Units are not graded
$mform->addElement('radio', 'unitrole', get_string('unitnotgraded', 'qtype_numerical'), get_string('onlynumerical', 'qtype_numerical'),1); $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('unitsleft', 'showunits1','eq','3');
$mform->disabledIf('showunits1','unitrole','eq','0'); $mform->disabledIf('showunits1','unitrole','eq','0');
$mform->disabledIf('showunits0','unitrole','eq','1'); $mform->disabledIf('showunits0','unitrole','eq','1');
} }
/** /**
@ -974,7 +1017,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
* of edit_..._form.php for * of edit_..._form.php for
* numerical, calculated, calculatedsimple * numerical, calculated, calculatedsimple
*/ */
function add_units_elements(& $mform,& $that) { function add_units_elements(& $mform,& $that) {
$repeated = array(); $repeated = array();
$repeated[] =& $mform->createElement('header', 'unithdr', get_string('unithdr', 'qtype_numerical', '{no}')); $repeated[] =& $mform->createElement('header', 'unithdr', get_string('unithdr', 'qtype_numerical', '{no}'));
@ -1006,31 +1049,48 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
/** /**
* function used in in function setdata () * function used in in function data_preprocessing() of edit_numerical_form.php for
* of edit_..._form.php for * numerical, calculated, calculatedsimple
* numerical, calculated, calculatedsimple */
*/ function set_numerical_unit_data($mform, &$question, &$default_values){
function set_numerical_unit_data(&$question,&$default_values){
list($categoryid) = explode(',', $question->category);
$context = $this->get_context_by_category_id($categoryid);
if (isset($question->options)){ if (isset($question->options)){
$default_values['unitgradingtype'] = $question->options->unitgradingtype ; $default_values['unitgradingtype'] = $question->options->unitgradingtype ;
$default_values['unitpenalty'] = $question->options->unitpenalty ; $default_values['unitpenalty'] = $question->options->unitpenalty ;
switch ($question->options->showunits){ switch ($question->options->showunits){
case 'O' : case 'O' :
case '1' : case '1' :
$default_values['showunits0'] = $question->options->showunits ; $default_values['showunits0'] = $question->options->showunits ;
$default_values['unitrole'] = 0 ; $default_values['unitrole'] = 0 ;
break; break;
case '2' : case '2' :
case '3' : case '3' :
$default_values['showunits1'] = $question->options->showunits ; $default_values['showunits1'] = $question->options->showunits ;
$default_values['unitrole'] = 1 ; $default_values['unitrole'] = 1 ;
break; break;
} }
$default_values['unitsleft'] = $question->options->unitsleft ; $default_values['unitsleft'] = $question->options->unitsleft ;
$default_values['instructions'] = $question->options->instructions ;
// processing files
if (isset($question->options->units)){ $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); $units = array_values($question->options->units);
if (!empty($units)) { if (!empty($units)) {
foreach ($units as $key => $unit){ foreach ($units as $key => $unit){
@ -1042,11 +1102,11 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
} }
/** /**
* function use in in function validation() * function use in in function validation()
* of edit_..._form.php for * of edit_..._form.php for
* numerical, calculated, calculatedsimple * numerical, calculated, calculatedsimple
*/ */
function validate_numerical_options(& $data, & $errors){ function validate_numerical_options(& $data, & $errors){
$units = $data['unit']; $units = $data['unit'];
@ -1055,9 +1115,9 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}else { }else {
$showunits = $data['showunits1']; $showunits = $data['showunits1'];
} }
if (($showunits == NUMERICALQUESTIONUNITTEXTINPUTDISPLAY) || if (($showunits == NUMERICALQUESTIONUNITTEXTINPUTDISPLAY) ||
($showunits == NUMERICALQUESTIONUNITMULTICHOICEDISPLAY ) || ($showunits == NUMERICALQUESTIONUNITMULTICHOICEDISPLAY ) ||
($showunits == NUMERICALQUESTIONUNITTEXTDISPLAY )){ ($showunits == NUMERICALQUESTIONUNITTEXTDISPLAY )){
if (trim($units[0]) == ''){ if (trim($units[0]) == ''){
$errors['unit[0]'] = 'You must set a valid unit name' ; $errors['unit[0]'] = 'You must set a valid unit name' ;
@ -1072,8 +1132,8 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
} }
} }
// Check double units. // Check double units.
$alreadyseenunits = array(); $alreadyseenunits = array();
if (isset($data['unit'])) { if (isset($data['unit'])) {
@ -1106,7 +1166,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
} }
} }
} }
} }
} }
@ -1284,6 +1344,71 @@ class question_numerical_qtype extends question_shortanswer_qtype {
return $this->save_question($question, $form, $course); 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. // INITIATION - Without this line the question type is not in use.

View file

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

View file

@ -1,4 +1,20 @@
<?php <?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. * The default questiontype class.
* *
@ -30,6 +46,7 @@ require_once($CFG->libdir . '/questionlib.php');
* @subpackage questiontypes * @subpackage questiontypes
*/ */
class default_questiontype { class default_questiontype {
public static $fileoptions = array('subdirs'=>false, 'maxfiles'=>-1, 'maxbytes'=>0);
/** /**
* Name of the question type * Name of the question type
@ -294,31 +311,35 @@ class default_questiontype {
*/ */
function save_question($question, $form, $course) { function save_question($question, $form, $course) {
global $USER, $DB, $OUTPUT; 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 // This default implementation is suitable for most
// question types. // question types.
// First, save the basic question itself // First, save the basic question itself
$question->name = trim($form->name); $question->name = trim($form->name);
$question->questiontext = trim($form->questiontext);
$question->questiontextformat = $form->questiontextformat;
$question->parent = isset($form->parent) ? $form->parent : 0; $question->parent = isset($form->parent) ? $form->parent : 0;
$question->length = $this->actual_number_of_questions($question); $question->length = $this->actual_number_of_questions($question);
$question->penalty = isset($form->penalty) ? $form->penalty : 0; $question->penalty = isset($form->penalty) ? $form->penalty : 0;
if (empty($form->image)) { if (empty($form->questiontext['text'])) {
$question->image = ''; $question->questiontext = '';
} else { } 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 = ''; $question->generalfeedback = '';
} else { } 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)) { 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)) { if (empty($question->name)) {
$question->name = '-'; $question->name = '-';
} }
@ -332,14 +353,16 @@ class default_questiontype {
$question->defaultgrade = $form->defaultgrade; $question->defaultgrade = $form->defaultgrade;
} }
list($question->category) = explode(',', $form->category);
if (!empty($question->id)) { if (!empty($question->id)) {
/// Question already exists, update. /// Question already exists, update.
$question->modifiedby = $USER->id; $question->modifiedby = $USER->id;
$question->timemodified = time(); $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 { } else {
/// New question. /// New question.
// Set the unique code // Set the unique code
@ -349,6 +372,12 @@ class default_questiontype {
$question->timecreated = time(); $question->timecreated = time();
$question->timemodified = time(); $question->timemodified = time();
$question->id = $DB->insert_record('question', $question); $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 // Now to save all the answers and type-specific options
@ -356,11 +385,14 @@ class default_questiontype {
$form->qtype = $question->qtype; $form->qtype = $question->qtype;
$form->category = $question->category; $form->category = $question->category;
$form->questiontext = $question->questiontext; $form->questiontext = $question->questiontext;
$form->questiontextformat = $question->questiontextformat;
// current context
$form->context = $context;
$result = $this->save_question_options($form); $result = $this->save_question_options($form);
if (!empty($result->error)) { if (!empty($result->error)) {
print_error('questionsaveerror', 'question', '', $result->error); print_error($result->error);
} }
if (!empty($result->notice)) { if (!empty($result->notice)) {
@ -888,12 +920,18 @@ class default_questiontype {
* @param object $cmoptions * @param object $cmoptions
* @param object $options An object describing the rendering options. * @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 /* The default implementation should work for most question types
provided the member functions it calls are overridden where required. provided the member functions it calls are overridden where required.
The layout is determined by the template question.html */ The layout is determined by the template question.html */
global $CFG, $OUTPUT; 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); $isgraded = question_state_is_graded($state->last_graded);
if (isset($question->randomquestionid)) { if (isset($question->randomquestionid)) {
@ -908,7 +946,7 @@ class default_questiontype {
$generalfeedback = ''; $generalfeedback = '';
if ($isgraded && $options->generalfeedback) { if ($isgraded && $options->generalfeedback) {
$generalfeedback = $this->format_text($question->generalfeedback, $generalfeedback = $this->format_text($question->generalfeedback,
$question->questiontextformat, $cmoptions); $question->generalfeedbackformat, $cmoptions);
} }
$grade = ''; $grade = '';
@ -1246,6 +1284,22 @@ class default_questiontype {
.' been implemented for question type '.$this->name()); .' 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 * 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){ function find_file_links($question, $courseid){
$urls = array(); $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. /// Questiontext and general feedback.
$urls += question_find_file_links_from_html($question->questiontext, $courseid); $urls += question_find_file_links_from_html($question->questiontext, $courseid);
$urls += question_find_file_links_from_html($question->generalfeedback, $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){ function replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination){
global $CFG, $DB; global $CFG, $DB;
$updateqrec = false; $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. /// Questiontext and general feedback.
$question->questiontext = question_replace_file_links_in_html($question->questiontext, $fromcourseid, $tocourseid, $url, $destination, $updateqrec); $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); $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; $question->qtype = $this->qtype;
return array($form, $question); 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->addGroup($buttonarray, 'buttonar', '', array(' '), false);
$mform->closeHeaderBefore('buttonar'); $mform->closeHeaderBefore('buttonar');
} }
function validation($fromform, $files) { function validation($fromform, $files) {
//validation of category //validation of category
//is not relevant for this question type //is not relevant for this question type
return array(); return array();
} }
function qtype() { function qtype() {
return 'random'; return 'random';
} }

View file

@ -88,6 +88,7 @@ class random_qtype extends default_questiontype {
} }
function display_question_editing_page(&$mform, $question, $wizardnow){ function display_question_editing_page(&$mform, $question, $wizardnow){
global $OUTPUT;
$heading = $this->get_heading(empty($question->id)); $heading = $this->get_heading(empty($question->id));
echo $OUTPUT->heading_with_help($heading, $this->name(), $this->plugin_name()); echo $OUTPUT->heading_with_help($heading, $this->name(), $this->plugin_name());
$mform->display(); $mform->display();
@ -424,5 +425,3 @@ class random_qtype extends default_questiontype {
//// INITIATION - Without this line the question type is not in use... /// //// INITIATION - Without this line the question type is not in use... ///
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
question_register_questiontype(new random_qtype()); 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. * @param MoodleQuickForm $mform the form being built.
*/ */
function definition_inner(&$mform) { function definition_inner(&$mform) {
$mform->removeElement('image');
$questionstoselect = array(); $questionstoselect = array();
for ($i=2; $i<=QUESTION_NUMANS; $i++){ for ($i=2; $i<=QUESTION_NUMANS; $i++){
$questionstoselect[$i] = $i; $questionstoselect[$i] = $i;
@ -33,15 +31,15 @@ class question_edit_randomsamatch_form extends question_edit_form {
$mform->setType('fraction', PARAM_RAW); $mform->setType('fraction', PARAM_RAW);
} }
function set_data($question) { function data_preprocessing($question) {
if (empty($question->name)) { if (empty($question->name)) {
$question->name = get_string("randomsamatch", "quiz"); $question->name = get_string("randomsamatch", "quiz");
} }
if (empty($question->questiontext)) { if (empty($question->questiontext)) {
$question->questiontext = get_string("randomsamatchintro", "quiz"); $question->questiontext = get_string("randomsamatchintro", "quiz");
} }
parent::set_data($question); return $question;
} }
function qtype() { function qtype() {
@ -70,7 +68,5 @@ class question_edit_randomsamatch_form extends question_edit_form {
$errors['choose'] = get_string('notenoughsaincategory', 'qtype_randomsamatch', $a); $errors['choose'] = get_string('notenoughsaincategory', 'qtype_randomsamatch', $a);
} }
return $errors; return $errors;
} }
} }

View file

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

View file

@ -1,4 +1,22 @@
<?php <?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. * Defines the editing form for the shortanswer question type.
* *
@ -30,22 +48,40 @@ class question_edit_shortanswer_form extends question_edit_form {
$creategrades->gradeoptions); $creategrades->gradeoptions);
} }
function set_data($question) { function data_preprocessing($question) {
if (isset($question->options)){ if (isset($question->options)){
$answers = $question->options->answers; $answers = $question->options->answers;
$answers_ids = array();
if (count($answers)) { if (count($answers)) {
$key = 0; $key = 0;
foreach ($answers as $answer){ foreach ($answers as $answer){
$answers_ids[] = $answer->id;
$default_values['answer['.$key.']'] = $answer->answer; $default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction; $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++; $key++;
} }
} }
$default_values['usecase'] = $question->options->usecase; $default_values['usecase'] = $question->options->usecase;
$question = (object)((array)$question + $default_values); $question = (object)((array)$question + $default_values);
} }
parent::set_data($question); return $question;
} }
function validation($data, $files) { function validation($data, $files) {
$errors = parent::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) { if ($data['fraction'][$key] == 1) {
$maxgrade = true; $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'); $errors["answer[$key]"] = get_string('answermustbegiven', 'qtype_shortanswer');
$answercount++; $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 <?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 /// /// SHORTANSWER ///
/////////////////// ///////////////////
@ -27,17 +42,48 @@ class question_shortanswer_qtype extends default_questiontype {
} }
function extra_question_fields() { function extra_question_fields() {
return array('question_shortanswer','answers','usecase'); return array('question_shortanswer', 'answers', 'usecase');
} }
function questionid_column_name() { function questionid_column_name() {
return 'question'; 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) { function save_question_options($question) {
global $DB; global $DB;
$result = new stdClass; $result = new stdClass;
$context = $question->context;
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) { if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array(); $oldanswers = array();
} }
@ -49,23 +95,36 @@ class question_shortanswer_qtype extends default_questiontype {
foreach ($question->answer as $key => $dataanswer) { foreach ($question->answer as $key => $dataanswer) {
// Check for, and ingore, completely blank answer from the form. // Check for, and ingore, completely blank answer from the form.
if (trim($dataanswer) == '' && $question->fraction[$key] == 0 && if (trim($dataanswer) == '' && $question->fraction[$key] == 0 &&
html_is_blank($question->feedback[$key])) { html_is_blank($question->feedback[$key]['text'])) {
continue; continue;
} }
$feedbackformat = $question->feedback[$key]['format'];
if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
$answer = $oldanswer; $answer = $oldanswer;
$answer->answer = trim($dataanswer); $answer->answer = trim($dataanswer);
$answer->fraction = $question->fraction[$key]; $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); $DB->update_record("question_answers", $answer);
} else { // This is a completely new answer } else { // This is a completely new answer
$answer = new stdClass; $answer = new stdClass;
$answer->answer = trim($dataanswer); $answer->answer = trim($dataanswer);
$answer->question = $question->id; $answer->question = $question->id;
$answer->fraction = $question->fraction[$key]; $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); $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; $answers[] = $answer->id;
if ($question->fraction[$key] > $maxfraction) { 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) { 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' /// This implementation is also used by question type 'numerical'
$readonly = empty($options->readonly) ? '' : 'readonly="readonly"'; $readonly = empty($options->readonly) ? '' : 'readonly="readonly"';
$formatoptions = new stdClass; $formatoptions = new stdClass;
@ -109,7 +170,6 @@ class question_shortanswer_qtype extends default_questiontype {
$questiontext = format_text($question->questiontext, $questiontext = format_text($question->questiontext,
$question->questiontextformat, $question->questiontextformat,
$formatoptions, $cmoptions->course); $formatoptions, $cmoptions->course);
$image = get_question_image($question);
/// Print input controls /// Print input controls
@ -135,6 +195,7 @@ class question_shortanswer_qtype extends default_questiontype {
$class = question_get_feedback_class($answer->fraction); $class = question_get_feedback_class($answer->fraction);
$feedbackimg = question_get_feedback_image($answer->fraction); $feedbackimg = question_get_feedback_image($answer->fraction);
if ($answer->feedback) { 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); $feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course);
} }
break; break;
@ -145,7 +206,7 @@ class question_shortanswer_qtype extends default_questiontype {
/// Removed correct answer, to be displayed later MDL-7496 /// Removed correct answer, to be displayed later MDL-7496
include($this->get_display_html_path()); include($this->get_display_html_path());
} }
function get_display_html_path() { function get_display_html_path() {
global $CFG; global $CFG;
return $CFG->dirroot.'/question/type/shortanswer/display.html'; return $CFG->dirroot.'/question/type/shortanswer/display.html';
@ -330,12 +391,12 @@ class question_shortanswer_qtype extends default_questiontype {
// print grade for this submission // print grade for this submission
print_string('gradingdetails', 'quiz', $grade) ; print_string('gradingdetails', 'quiz', $grade) ;
// A unit penalty for numerical was applied so display it // A unit penalty for numerical was applied so display it
// a temporary solution for unit rendering in numerical // a temporary solution for unit rendering in numerical
// waiting for the new question engine code for a permanent one // waiting for the new question engine code for a permanent one
if(isset($state->options->raw_unitpenalty) && $state->options->raw_unitpenalty > 0.0 ){ if(isset($state->options->raw_unitpenalty) && $state->options->raw_unitpenalty > 0.0 ){
echo ' '; echo ' ';
print_string('unitappliedpenalty','qtype_numerical',question_format_grade($cmoptions, $state->options->raw_unitpenalty )); print_string('unitappliedpenalty','qtype_numerical',question_format_grade($cmoptions, $state->options->raw_unitpenalty ));
} }
if ($cmoptions->penaltyscheme) { if ($cmoptions->penaltyscheme) {
// print details of grade adjustment due to penalties // print details of grade adjustment due to penalties
if ($state->last_graded->raw_grade > $state->last_graded->grade){ if ($state->last_graded->raw_grade > $state->last_graded->grade){
@ -390,6 +451,33 @@ class question_shortanswer_qtype extends default_questiontype {
return $this->save_question($question, $form, $course); 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 //// //// END OF CLASS ////

View file

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

View file

@ -1,9 +1,26 @@
<?php <?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')) { if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page 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'); require_once($CFG->dirroot.'/question/type/edit_question_form.php');
/** /**
* Defines the editing form for the thruefalse question type. * 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 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank * @package questionbank
* @subpackage questiontypes * @subpackage questiontypes
*//** */ */
/** /**
* truefalse editing form definition. * 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'), $mform->addElement('select', 'correctanswer', get_string('correctanswer', 'qtype_truefalse'),
array(0 => get_string('false', 'qtype_truefalse'), 1 => get_string('true', 'qtype_truefalse'))); array(0 => get_string('false', 'qtype_truefalse'), 1 => get_string('true', 'qtype_truefalse')));
$mform->addElement('htmleditor', 'feedbacktrue', get_string('feedbacktrue', 'qtype_truefalse'), $mform->addElement('editor', 'feedbacktrue', get_string('feedbacktrue', 'qtype_truefalse'), null, $this->editoroptions);;
array('course' => $this->coursefilesid));;
$mform->setType('feedbacktrue', PARAM_RAW); $mform->setType('feedbacktrue', PARAM_RAW);
$mform->addElement('htmleditor', 'feedbackfalse', get_string('feedbackfalse', 'qtype_truefalse'), $mform->addElement('editor', 'feedbackfalse', get_string('feedbackfalse', 'qtype_truefalse'), null, $this->editoroptions);
array('course' => $this->coursefilesid));
$mform->setType('feedbackfalse', PARAM_RAW); $mform->setType('feedbackfalse', PARAM_RAW);
// Fix penalty factor at 1. // Fix penalty factor at 1.
@ -43,9 +58,45 @@ class question_edit_truefalse_form extends question_edit_form {
function set_data($question) { function set_data($question) {
if (!empty($question->options->trueanswer)) { if (!empty($question->options->trueanswer)) {
$trueanswer = $question->options->answers[$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->correctanswer = ($trueanswer->fraction != 0);
$question->feedbacktrue = $trueanswer->feedback; $question->feedbacktrue = array();
$question->feedbackfalse = $question->options->answers[$question->options->falseanswer]->feedback; $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); 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 <?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 /// /// TRUEFALSE ///
///////////////// /////////////////
@ -24,34 +41,52 @@ class question_truefalse_qtype extends default_questiontype {
$oldanswers = array(); $oldanswers = array();
} }
$feedbacktext = $question->feedbacktrue['text'];
$feedbackitemid = $question->feedbacktrue['itemid'];
$feedbackformat = $question->feedbacktrue['format'];
// Save answer 'True' // Save answer 'True'
if ($true = array_shift($oldanswers)) { // Existing answer, so reuse it if ($true = array_shift($oldanswers)) { // Existing answer, so reuse it
$true->answer = get_string("true", "quiz"); $true->answer = get_string("true", "quiz");
$true->fraction = $question->correctanswer; $true->fraction = $question->correctanswer;
$true->feedback = $question->feedbacktrue; $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); $DB->update_record("question_answers", $true);
} else { } else {
unset($true); unset($true);
$true = new stdclass;
$true->answer = get_string("true", "quiz"); $true->answer = get_string("true", "quiz");
$true->question = $question->id; $true->question = $question->id;
$true->fraction = $question->correctanswer; $true->fraction = $question->correctanswer;
$true->feedback = $question->feedbacktrue; $true->feedback = '';
$true->feedbackformat = $feedbackformat;
$true->id = $DB->insert_record("question_answers", $true); $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' // Save answer 'False'
if ($false = array_shift($oldanswers)) { // Existing answer, so reuse it if ($false = array_shift($oldanswers)) { // Existing answer, so reuse it
$false->answer = get_string("false", "quiz"); $false->answer = get_string("false", "quiz");
$false->fraction = 1 - (int)$question->correctanswer; $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); $DB->update_record("question_answers", $false);
} else { } else {
unset($false); unset($false);
$false = new stdclass;
$false->answer = get_string("false", "quiz"); $false->answer = get_string("false", "quiz");
$false->question = $question->id; $false->question = $question->id;
$false->fraction = 1 - (int)$question->correctanswer; $false->fraction = 1 - (int)$question->correctanswer;
$false->feedback = $question->feedbackfalse; $false->feedback = '';
$false->feedbackformat = $feedbackformat;
$false->id = $DB->insert_record("question_answers", $false); $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) // 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 * Prints the main content of the question including any interactions
*/ */
function print_question_formulation_and_controls(&$question, &$state, function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
$cmoptions, $options) {
global $CFG; global $CFG;
$context = $this->get_context_by_category_id($question->category);
$readonly = $options->readonly ? ' disabled="disabled"' : ''; $readonly = $options->readonly ? ' disabled="disabled"' : '';
@ -148,7 +183,6 @@ class question_truefalse_qtype extends default_questiontype {
$questiontext = format_text($question->questiontext, $questiontext = format_text($question->questiontext,
$question->questiontextformat, $question->questiontextformat,
$formatoptions, $cmoptions->course); $formatoptions, $cmoptions->course);
$image = get_question_image($question);
$answers = &$question->options->answers; $answers = &$question->options->answers;
$trueanswer = &$answers[$question->options->trueanswer]; $trueanswer = &$answers[$question->options->trueanswer];
@ -198,12 +232,33 @@ class question_truefalse_qtype extends default_questiontype {
$feedback = ''; $feedback = '';
if ($options->feedback and isset($answers[$response])) { if ($options->feedback and isset($answers[$response])) {
$chosenanswer = $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); $feedback = format_text($chosenanswer->feedback, true, $formatoptions, $cmoptions->course);
} }
include("$CFG->dirroot/question/type/truefalse/display.html"); 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) { function grade_responses(&$question, &$state, $cmoptions) {
if (isset($state->responses['']) && isset($question->options->answers[$state->responses['']])) { if (isset($state->responses['']) && isset($question->options->answers[$state->responses['']])) {
$state->raw_grade = $question->options->answers[$state->responses['']]->fraction * $question->maxgrade; $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); 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 //// //// 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... /// //// INITIATION - Without this line the question type is not in use... ///
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
question_register_questiontype(new question_truefalse_qtype()); 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 // This is compared against the values stored in the database to determine
// whether upgrades should be performed (see lib/db/*.php) // 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 // XX = daily increments
$release = '2.0 Preview 4+ (Build: 20100810)'; // Human-friendly version name $release = '2.0 Preview 4+ (Build: 20100810)'; // Human-friendly version name