MDL-16094 File storage conversion Quiz and Questions

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

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20100803" COMMENT="XMLDB file for core Moodle tables"
<XMLDB PATH="lib/db" VERSION="20100806" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@ -1246,8 +1246,9 @@
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="name"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="contextid"/>
<FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="context that this category is shared in" PREVIOUS="name" NEXT="info"/>
<FIELD NAME="info" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="stamp"/>
<FIELD NAME="stamp" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="info" NEXT="parent"/>
<FIELD NAME="info" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="infoformat"/>
<FIELD NAME="infoformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="info" NEXT="stamp"/>
<FIELD NAME="stamp" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="infoformat" NEXT="parent"/>
<FIELD NAME="parent" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="stamp" NEXT="sortorder"/>
<FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="999" SEQUENCE="false" PREVIOUS="parent"/>
</FIELDS>
@ -1266,10 +1267,10 @@
<FIELD NAME="parent" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="category" NEXT="name"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="parent" NEXT="questiontext"/>
<FIELD NAME="questiontext" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="name" NEXT="questiontextformat"/>
<FIELD NAME="questiontextformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="questiontext" NEXT="image"/>
<FIELD NAME="image" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="questiontextformat" NEXT="generalfeedback"/>
<FIELD NAME="generalfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="to store the question feedback" PREVIOUS="image" NEXT="defaultgrade"/>
<FIELD NAME="defaultgrade" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" DECIMALS="7" PREVIOUS="generalfeedback" NEXT="penalty"/>
<FIELD NAME="questiontextformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="questiontext" NEXT="generalfeedback"/>
<FIELD NAME="generalfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="to store the question feedback" PREVIOUS="questiontextformat" NEXT="generalfeedbackformat"/>
<FIELD NAME="generalfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="generalfeedback" NEXT="defaultgrade"/>
<FIELD NAME="defaultgrade" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" DECIMALS="7" PREVIOUS="generalfeedbackformat" NEXT="penalty"/>
<FIELD NAME="penalty" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="false" DEFAULT="0.1" SEQUENCE="false" DECIMALS="7" PREVIOUS="defaultgrade" NEXT="qtype"/>
<FIELD NAME="qtype" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" PREVIOUS="penalty" NEXT="length"/>
<FIELD NAME="length" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="qtype" NEXT="stamp"/>
@ -1293,9 +1294,11 @@
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="question"/>
<FIELD NAME="question" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="answer"/>
<FIELD NAME="answer" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="question" NEXT="fraction"/>
<FIELD NAME="fraction" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="7" PREVIOUS="answer" NEXT="feedback"/>
<FIELD NAME="feedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="fraction"/>
<FIELD NAME="answer" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="question" NEXT="answerformat"/>
<FIELD NAME="answerformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="answer" NEXT="fraction"/>
<FIELD NAME="fraction" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="7" PREVIOUS="answerformat" NEXT="feedback"/>
<FIELD NAME="feedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="fraction" NEXT="feedbackformat"/>
<FIELD NAME="feedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="feedback"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/>
@ -1338,8 +1341,9 @@
<FIELD NAME="newest" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="questionid" NEXT="newgraded"/>
<FIELD NAME="newgraded" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="newest" NEXT="sumpenalty"/>
<FIELD NAME="sumpenalty" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="7" PREVIOUS="newgraded" NEXT="manualcomment"/>
<FIELD NAME="manualcomment" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="sumpenalty" NEXT="flagged"/>
<FIELD NAME="flagged" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="The person attempting the question may mark certain questions within their question_attempt if the module that owns the attempt allow it. This field stores the status of that flag." PREVIOUS="manualcomment"/>
<FIELD NAME="manualcomment" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="sumpenalty" NEXT="manualcommentformat"/>
<FIELD NAME="manualcommentformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="manualcomment" NEXT="flagged"/>
<FIELD NAME="flagged" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="The person attempting the question may mark certain questions within their question_attempt if the module that owns the attempt allow it. This field stores the status of that flag." PREVIOUS="manualcommentformat"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="attemptid"/>
@ -1575,7 +1579,7 @@
<KEY NAME="handlerid" TYPE="foreign" FIELDS="handlerid" REFTABLE="events_handlers" REFFIELDS="id" PREVIOUS="queuedeventid"/>
</KEYS>
</TABLE>
<TABLE NAME="grade_outcomes" COMMENT="This table describes the outcomes used in the system. An outcome is a statement tied to a rubric scale from low to high, such as “Not met, Borderline, Met” (stored as 0,1 or 2)" PREVIOUS="events_queue_handlers" NEXT="grade_outcomes_courses">
<TABLE NAME="grade_outcomes" COMMENT="This table describes the outcomes used in the system. An outcome is a statement tied to a rubric scale from low to high, such as “Not met, Borderline, Met” (stored as 0,1 or 2)" PREVIOUS="events_queue_handlers" NEXT="grade_outcomes_courses">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" COMMENT="id of the table" NEXT="courseid"/>
<FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="Mostly these are defined site wide ie NULL" PREVIOUS="id" NEXT="shortname"/>
@ -2709,4 +2713,4 @@
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
</XMLDB>

View file

@ -4911,7 +4911,170 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
upgrade_main_savepoint(true, 2010080305);
}
if ($oldversion < 2010080900) {
/// Define field generalfeedbackformat to be added to question
$table = new xmldb_table('question');
$field = new xmldb_field('generalfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'generalfeedback');
/// Conditionally launch add field generalfeedbackformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define field infoformat to be added to question_categories
$table = new xmldb_table('question_categories');
$field = new xmldb_field('infoformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'info');
/// Conditionally launch add field infoformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define field answerformat to be added to question_answers
$table = new xmldb_table('question_answers');
$field = new xmldb_field('answerformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'answer');
/// Conditionally launch add field answerformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define field feedbackformat to be added to question_answers
$field = new xmldb_field('feedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'feedback');
/// Conditionally launch add field feedbackformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define field manualcommentformat to be added to question_sessions
$table = new xmldb_table('question_sessions');
$field = new xmldb_field('manualcommentformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'manualcomment');
/// Conditionally launch add field manualcommentformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Main savepoint reached
upgrade_main_savepoint(true, 2010080900);
}
/// updating question image
if ($oldversion < 2010080901) {
$fs = get_file_storage();
$rs = $DB->get_recordset('question');
$textlib = textlib_get_instance();
foreach ($rs as $question) {
if (empty($question->image)) {
continue;
}
if (!$category = $DB->get_record('question_categories', array('id'=>$question->category))) {
continue;
}
$categorycontext = get_context_instance_by_id($category->contextid);
// question files are stored in course level
// so we have to find course context
switch ($categorycontext->contextlevel){
case CONTEXT_COURSE :
$context = $categorycontext;
break;
case CONTEXT_MODULE :
$courseid = $DB->get_field('course_modules', 'course', array('id'=>$categorycontext->instanceid));
$context = get_context_instance(CONTEXT_COURSE, $courseid);
break;
case CONTEXT_COURSECAT :
case CONTEXT_SYSTEM :
$context = get_system_context();
break;
default :
continue;
}
if ($textlib->substr($textlib->strtolower($question->image), 0, 7) == 'http://') {
// it is a link, appending to existing question text
$question->questiontext .= ' <img src="' . $question->image . '" />';
// update question record
$DB->update_record('question', $question);
} else {
$filename = basename($question->image);
$filepath = dirname($question->image);
if (empty($filepath) or $filepath == '.' or $filepath == '/') {
$filepath = '/';
} else {
// append /
$filepath = '/'.trim($filepath, './@#$ ').'/';
}
// course files already moved to file pool by previous upgrade block
// so we just create copy from course_legacy area
if ($image = $fs->get_file($context->id, 'course', 'legacy', 0, $filepath, $filename)) {
// move files to file pool
$file_record = array(
'contextid'=>$category->contextid,
'component'=>'question',
'filearea'=>'questiontext',
'itemid'=>$question->id
);
$fs->create_file_from_storedfile($file_record, $image);
$question->questiontext .= ' <img src="@@PLUGINFILE@@' . $filepath . $filename . '" />';
// update question record
$DB->update_record('question', $question);
}
}
}
$rs->close();
// Define field image to be dropped from question
$table = new xmldb_table('question');
$field = new xmldb_field('image');
// Conditionally launch drop field image
if ($dbman->field_exists($table, $field)) {
$dbman->drop_field($table, $field);
}
// fix fieldformat
$sql = 'SELECT a.*, q.qtype FROM {question_answers} a, {question} q WHERE a.question = q.id';
$rs = $DB->get_recordset_sql($sql);
foreach ($rs as $record) {
// generalfeedback should use questiontext format
if ($CFG->texteditors !== 'textarea') {
if (!empty($record->feedback)) {
$record->feedback = text_to_html($record->feedback);
}
$record->feedbackformat = FORMAT_HTML;
} else {
$record->feedbackformat = FORMAT_MOODLE;
$record->answerformat = FORMAT_MOODLE;
}
unset($record->qtype);
$DB->update_record('question_answers', $record);
}
$rs->close();
$rs = $DB->get_recordset('question');
foreach ($rs as $record) {
if ($CFG->texteditors !== 'textarea') {
if (!empty($record->questiontext)) {
$record->questiontext = text_to_html($record->questiontext);
}
$record->questiontextformat = FORMAT_HTML;
// conver generalfeedback text to html
if (!empty($record->generalfeedback)) {
$record->generalfeedback = text_to_html($record->generalfeedback);
}
} else {
$record->questiontextformat = FORMAT_MOODLE;
}
// generalfeedbackformat should be the save as questiontext format
$record->generalfeedbackformat = $record->questiontextformat;
$DB->update_record('question', $record);
}
$rs->close();
// Main savepoint reached
upgrade_main_savepoint(true, 2010080901);
}
return true;
}

View file

@ -851,18 +851,31 @@ function question_delete_activity($cm, $feedback=true) {
*
* @global object
* @param string $questionids a comma-separated list of question ids.
* @param integer $newcategory the id of the category to move to.
* @param integer $newcategoryid the id of the category to move to.
*/
function question_move_questions_to_category($questionids, $newcategory) {
global $DB;
function question_move_questions_to_category($questionids, $newcategoryid) {
global $DB, $QTYPES;
$result = true;
$ids = explode(',', $questionids);
foreach ($ids as $questionid) {
$questionid = (int)$questionid;
$params = array();
$params[] = $questionid;
$sql = 'SELECT q.*, c.id AS contextid, c.contextlevel, c.instanceid, c.path, c.depth
FROM {question} q, {question_categories} qc, {context} c
WHERE q.category=qc.id AND q.id=? AND qc.contextid=c.id';
$question = $DB->get_record_sql($sql, $params);
$category = $DB->get_record('question_categories', array('id'=>$newcategoryid));
// process files
$QTYPES[$question->qtype]->move_files($question, $category);
}
// Move the questions themselves.
$result = $result && $DB->set_field_select('question', 'category', $newcategory, "id IN ($questionids)");
$result = $result && $DB->set_field_select('question', 'category', $newcategoryid, "id IN ($questionids)");
// Move any subquestions belonging to them.
$result = $result && $DB->set_field_select('question', 'category', $newcategory, "parent IN ($questionids)");
$result = $result && $DB->set_field_select('question', 'category', $newcategoryid, "parent IN ($questionids)");
// TODO Deal with datasets.
@ -1080,6 +1093,7 @@ function question_load_states(&$questions, &$states, $cmoptions, $attempt, $last
$states[$qid]->last_graded = clone($states[$qid]);
}
} else {
if ($lastattemptid) {
// If the new attempt is to be based on this previous attempt.
// Find the responses from the previous attempt and save them to the new session
@ -2091,7 +2105,7 @@ function question_init_qengine_js() {
$module = array(
'name' => 'core_question_flags',
'fullpath' => '/question/flags.js',
'requires' => array('base', 'dom', 'event-delegate', 'io-base'),
'requires' => array('base', 'dom', 'event-delegate', 'io-base'),
);
$actionurl = $CFG->wwwroot . '/question/toggleflag.php';
$flagattributes = array(
@ -2162,9 +2176,9 @@ function question_get_editing_head_contributions($question) {
* @param object $cmoptions The options specified by the course module
* @param object $options An object specifying the rendering options.
*/
function print_question(&$question, &$state, $number, $cmoptions, $options=null) {
function print_question(&$question, &$state, $number, $cmoptions, $options=null, $context=null) {
global $QTYPES;
$QTYPES[$question->qtype]->print_question($question, $state, $number, $cmoptions, $options);
$QTYPES[$question->qtype]->print_question($question, $state, $number, $cmoptions, $options, $context);
}
/**
* Saves question options
@ -3191,4 +3205,124 @@ class question_edit_contexts {
print_error('nopermissions', '', '', 'access question edit tab '.$tabname);
}
}
}
}
/**
* Rewrite question url, file_rewrite_pluginfile_urls always build url by
* $file/$contextid/$component/$filearea/$itemid/$pathname_in_text, so we cannot add
* extra questionid and attempted in url by it, so we create quiz_rewrite_question_urls
* to build url here
*
* @param string $text text being processed
* @param string $file the php script used to serve files
* @param int $contextid
* @param string $component component
* @param string $filearea filearea
* @param array $ids other IDs will be used to check file permission
* @param int $itemid
* @param array $options
* @return string
*/
function quiz_rewrite_question_urls($text, $file, $contextid, $component, $filearea, array $ids, $itemid, array $options=null) {
global $CFG;
$options = (array)$options;
if (!isset($options['forcehttps'])) {
$options['forcehttps'] = false;
}
if (!$CFG->slasharguments) {
$file = $file . '?file=';
}
$baseurl = "$CFG->wwwroot/$file/$contextid/$component/$filearea/";
if (!empty($ids)) {
$baseurl .= (implode('/', $ids) . '/');
}
if ($itemid !== null) {
$baseurl .= "$itemid/";
}
if ($options['forcehttps']) {
$baseurl = str_replace('http://', 'https://', $baseurl);
}
return str_replace('@@PLUGINFILE@@/', $baseurl, $text);
}
/**
* Called by pluginfile.php to serve files related to the 'question' core
* component and for files belonging to qtypes.
*
* For files that relate to questions in a question_attempt, then we delegate to
* a function in the component that owns the attempt (for example in the quiz,
* or in core question preview) to get necessary inforation.
*
* (Note that, at the moment, all question file areas relate to questions in
* attempts, so the If at the start of the last paragraph is always true.)
*
* Does not return, either calls send_file_not_found(); or serves the file.
*
* @param object $course course settings object
* @param object $context context object
* @param string $component the name of the component we are serving files for.
* @param string $filearea the name of the file area.
* @param array $args the remaining bits of the file path.
* @param bool $forcedownload whether the user must be forced to download the file.
*/
function question_pluginfile($course, $context, $component, $filearea, $args, $forcedownload) {
global $DB, $CFG;
$attemptid = (int)array_shift($args);
$questionid = (int)array_shift($args);
require_login($course, false);
if ($attemptid === 0) {
// preview
require_once($CFG->dirroot . '/question/previewlib.php');
return question_preview_question_pluginfile($course, $context,
$component, $filearea, $attemptid, $questionid, $args, $forcedownload);
} else {
$module = $DB->get_field('question_attempts', 'modulename',
array('id' => $attemptid));
$dir = get_component_directory($module);
if (!file_exists("$dir/lib.php")) {
send_file_not_found();
}
include_once("$dir/lib.php");
$filefunction = $module . '_question_pluginfile';
if (!function_exists($filefunction)) {
send_file_not_found();
}
$filefunction($course, $context, $component, $filearea, $attemptid, $questionid,
$args, $forcedownload);
send_file_not_found();
}
}
/**
* Final test for whether a studnet should be allowed to see a particular file.
* This delegates the decision to the question type plugin.
*
* @param object $question The question to be rendered.
* @param object $state The state to render the question in.
* @param object $options An object specifying the rendering options.
* @param string $component the name of the component we are serving files for.
* @param string $filearea the name of the file area.
* @param array $args the remaining bits of the file path.
* @param bool $forcedownload whether the user must be forced to download the file.
*/
function question_check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args, $forcedownload) {
global $QTYPES;
return $QTYPES[$question->qtype]->check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args, $forcedownload);
}

View file

@ -283,6 +283,10 @@ class quiz {
return $this->accessmanager;
}
public function get_overall_feedback($grade) {
return quiz_feedback_for_grade($grade, $this->quiz, $this->context, $this->cm);
}
/**
* Wrapper round the has_capability funciton that automatically passes in the quiz context.
*/
@ -571,7 +575,7 @@ class quiz_attempt extends quiz {
return $this->attempt->timefinish != 0;
}
/** @return boolean whether this attemp is a preview attempt. */
/** @return boolean whether this attempt is a preview attempt. */
public function is_preview() {
return $this->attempt->preview;
}
@ -629,10 +633,12 @@ class quiz_attempt extends quiz {
/**
* Wrapper that calls get_render_options with the appropriate arguments.
*
* @param integer questionid the quetsion to get the render options for.
* @return object the render options for this user on this attempt.
*/
public function get_render_options($state) {
return quiz_get_renderoptions($this->quiz, $this->attempt, $this->context, $state);
public function get_render_options($questionid) {
return quiz_get_renderoptions($this->quiz, $this->attempt, $this->context,
$this->get_question_state($questionid));
}
/**
@ -669,7 +675,7 @@ class quiz_attempt extends quiz {
case QUESTION_EVENTCLOSEANDGRADE:
case QUESTION_EVENTCLOSE:
case QUESTION_EVENTMANUALGRADE:
$options = $this->get_render_options($this->states[$questionid]);
$options = $this->get_render_options($questionid);
if ($options->scores && $this->questions[$questionid]->maxgrade > 0) {
return question_get_feedback_class($state->last_graded->raw_grade /
$this->questions[$questionid]->maxgrade);
@ -703,7 +709,7 @@ class quiz_attempt extends quiz {
* @return string the formatted grade, to the number of decimal places specified by the quiz.
*/
public function get_question_score($questionid) {
$options = $this->get_render_options($this->states[$questionid]);
$options = $this->get_render_options($questionid);
if ($options->scores) {
return quiz_format_question_grade($this->quiz, $this->states[$questionid]->last_graded->grade);
} else {
@ -805,7 +811,7 @@ class quiz_attempt extends quiz {
if ($reviewing) {
$options = $this->get_review_options();
} else {
$options = $this->get_render_options($this->states[$id]);
$options = $this->get_render_options($id);
}
if ($thispageurl) {
$this->quiz->thispageurl = $thispageurl;
@ -816,6 +822,20 @@ class quiz_attempt extends quiz {
$this->quiz, $options);
}
public function check_file_access($questionid, $isreviewing, $contextid, $component,
$filearea, $args, $forcedownload) {
if ($isreviewing) {
$options = $this->get_review_options();
} else {
$options = $this->get_render_options($questionid);
}
// XXX: mulitichoice type needs quiz id to get maxgrade
$options->quizid = $this->attempt->quiz;
return question_check_file_access($this->questions[$questionid],
$this->get_question_state($questionid), $options, $contextid,
$component, $filearea, $args, $forcedownload);
}
/**
* Triggers the sending of the notification emails at the end of this attempt.
*/

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="mod/quiz/db" VERSION="20100220" COMMENT="XMLDB file for Moodle mod/quiz"
<XMLDB PATH="mod/quiz/db" VERSION="20100722" COMMENT="XMLDB file for Moodle mod/quiz"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
@ -100,8 +100,9 @@
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="quizid"/>
<FIELD NAME="quizid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references quiz.id." PREVIOUS="id" NEXT="feedbacktext"/>
<FIELD NAME="feedbacktext" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="The feedback to show for a attempt where mingrade &lt;= attempt grade &lt; maxgrade. See function quiz_feedback_for_grade in mod/quiz/locallib.php." PREVIOUS="quizid" NEXT="mingrade"/>
<FIELD NAME="mingrade" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The lower limit of this grade band. Inclusive." PREVIOUS="feedbacktext" NEXT="maxgrade"/>
<FIELD NAME="feedbacktext" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="The feedback to show for a attempt where mingrade &lt;= attempt grade &lt; maxgrade. See function quiz_feedback_for_grade in mod/quiz/locallib.php." PREVIOUS="quizid" NEXT="feedbacktextformat"/>
<FIELD NAME="feedbacktextformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="feedbacktext" NEXT="mingrade"/>
<FIELD NAME="mingrade" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The lower limit of this grade band. Inclusive." PREVIOUS="feedbacktextformat" NEXT="maxgrade"/>
<FIELD NAME="maxgrade" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The upper limit of this grade band. Exclusive." PREVIOUS="mingrade"/>
</FIELDS>
<KEYS>

View file

@ -341,6 +341,22 @@ function xmldb_quiz_upgrade($oldversion) {
upgrade_mod_savepoint(true, 2010051800, 'quiz');
}
if ($oldversion < 2010080600) {
// Define field feedbacktextformat to be added to quiz_feedback
$table = new xmldb_table('quiz_feedback');
$field = new xmldb_field('feedbacktextformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'feedbacktext');
// Conditionally launch add field feedbacktextformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// quiz savepoint reached
upgrade_mod_savepoint(true, 2010080600, 'quiz');
}
return true;
}

View file

@ -155,7 +155,7 @@
$grade = get_string('outofshort', 'quiz', $a);
}
if ($alloptions->overallfeedback) {
$feedback = quiz_feedback_for_grade($scores[$quiz->id], $quiz->id);
$feedback = quiz_feedback_for_grade($scores[$quiz->id], $quiz, $context, $cm);
}
}
$data[] = $grade;

View file

@ -104,6 +104,7 @@ define("QUIZ_MAX_EVENT_LENGTH", 5*24*60*60); // 5 days maximum
*/
function quiz_add_instance($quiz) {
global $DB;
$cmid = $quiz->coursemodule;
// Process the options from the form.
$quiz->created = time();
@ -982,10 +983,10 @@ function quiz_process_options(&$quiz) {
if (isset($quiz->feedbacktext)) {
// Clean up the boundary text.
for ($i = 0; $i < count($quiz->feedbacktext); $i += 1) {
if (empty($quiz->feedbacktext[$i])) {
$quiz->feedbacktext[$i] = '';
if (empty($quiz->feedbacktext[$i]['text'])) {
$quiz->feedbacktext[$i]['text'] = '';
} else {
$quiz->feedbacktext[$i] = trim($quiz->feedbacktext[$i]);
$quiz->feedbacktext[$i]['text'] = trim($quiz->feedbacktext[$i]['text']);
}
}
@ -1023,7 +1024,7 @@ function quiz_process_options(&$quiz) {
}
}
for ($i = $numboundaries + 1; $i < count($quiz->feedbacktext); $i += 1) {
if (!empty($quiz->feedbacktext[$i]) && trim($quiz->feedbacktext[$i]) != '') {
if (!empty($quiz->feedbacktext[$i]['text']) && trim($quiz->feedbacktext[$i]['text']) != '') {
return get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1);
}
}
@ -1145,17 +1146,25 @@ function quiz_process_options(&$quiz) {
*/
function quiz_after_add_or_update($quiz) {
global $DB;
$cmid = $quiz->coursemodule;
// we need to use context now, so we need to make sure all needed info is already in db
$DB->set_field('course_modules', 'instance', $quiz->id, array('id'=>$cmid));
$context = get_context_instance(CONTEXT_MODULE, $cmid);
// Save the feedback
$DB->delete_records('quiz_feedback', array('quizid' => $quiz->id));
for ($i = 0; $i <= $quiz->feedbackboundarycount; $i += 1) {
for ($i = 0; $i <= $quiz->feedbackboundarycount; $i++) {
$feedback = new stdClass;
$feedback->quizid = $quiz->id;
$feedback->feedbacktext = $quiz->feedbacktext[$i];
$feedback->feedbacktext = $quiz->feedbacktext[$i]['text'];
$feedback->feedbacktextformat = $quiz->feedbacktext[$i]['format'];
$feedback->mingrade = $quiz->feedbackboundaries[$i];
$feedback->maxgrade = $quiz->feedbackboundaries[$i - 1];
$DB->insert_record('quiz_feedback', $feedback, false);
$feedback->id = $DB->insert_record('quiz_feedback', $feedback);
$feedbacktext = file_save_draft_area_files((int)$quiz->feedbacktext[$i]['itemid'], $context->id, 'mod_quiz', 'feedback', $feedback->id, array('subdirs'=>false, 'maxfiles'=>-1, 'maxbytes'=>0), $quiz->feedbacktext[$i]['text']);
$DB->set_field('quiz_feedback', 'feedbacktext', $feedbacktext, array('id'=>$feedback->id));
}
// Update the events relating to this quiz.
@ -1421,23 +1430,44 @@ function quiz_reset_userdata($data) {
* @param int $questionid int question id
* @return boolean to indicate access granted or denied
*/
function quiz_check_file_access($attemptuniqueid, $questionid) {
global $USER, $DB;
function quiz_check_file_access($attemptuniqueid, $questionid, $context = null) {
global $USER, $DB, $CFG;
require_once(dirname(__FILE__).'/attemptlib.php');
require_once(dirname(__FILE__).'/locallib.php');
$attempt = $DB->get_record('quiz_attempts', array('uniqueid' => $attemptuniqueid));
$quiz = $DB->get_record('quiz', array('id' => $attempt->quiz));
$context = get_context_instance(CONTEXT_COURSE, $quiz->course);
$attemptobj = quiz_attempt::create($attempt->id);
// access granted if the current user submitted this file
if ($attempt->userid == $USER->id) {
return true;
// access granted if the current user has permission to grade quizzes in this course
} else if (has_capability('mod/quiz:viewreports', $context) || has_capability('mod/quiz:grade', $context)) {
return true;
// does question exist?
if (!$question = $DB->get_record('question', array('id' => $questionid))) {
return false;
}
// otherwise, this user does not have permission
return false;
if ($context === null) {
$quiz = $DB->get_record('quiz', array('id' => $attempt->quiz));
$cm = get_coursemodule_from_id('quiz', $quiz->id);
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
}
// Load those questions and the associated states.
$attemptobj->load_questions(array($questionid));
$attemptobj->load_question_states(array($questionid));
// obtain state
$state = $attemptobj->get_question_state($questionid);
// obtain questoin
$question = $attemptobj->get_question($questionid);
// access granted if the current user submitted this file
if ($attempt->userid != $USER->id) {
return false;
// access granted if the current user has permission to grade quizzes in this course
}
if (!(has_capability('mod/quiz:viewreports', $context) || has_capability('mod/quiz:grade', $context))) {
return false;
}
return array($question, $state, array());
}
/**
@ -1682,3 +1712,98 @@ function quiz_extend_settings_navigation($settings, $quiznode) {
question_extend_settings_navigation($quiznode, $PAGE->cm->context)->trim_if_empty();
}
/**
* Serves the quiz files.
*
* @param object $course
* @param object $cm
* @param object $context
* @param string $filearea
* @param array $args
* @param bool $forcedownload
* @return bool false if file not found, does not return if found - justsend the file
*/
function quiz_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG, $DB;
if ($context->contextlevel != CONTEXT_MODULE) {
return false;
}
require_login($course, false, $cm);
if (!$quiz = $DB->get_record('quiz', array('id'=>$cm->instance))) {
return false;
}
// 'intro' area is served by pluginfile.php
$fileareas = array('feedback');
if (!in_array($filearea, $fileareas)) {
return false;
}
$feedbackid = (int)array_shift($args);
if (!$feedback = $DB->get_record('quiz_feedback', array('id'=>$feedbackid))) {
return false;
}
$fs = get_file_storage();
$relativepath = implode('/', $args);
$fullpath = "/$context->id/mod_quiz/$filearea/$feedbackid/$relativepath";
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
return false;
}
send_stored_file($file, 0, 0, true);
}
/**
* Called via pluginfile.php -> question_pluginfile to serve files belonging to
* a question in a question_attempt when that attempt is a quiz attempt.
*
* @param object $course course settings object
* @param object $context context object
* @param string $component the name of the component we are serving files for.
* @param string $filearea the name of the file area.
* @param array $args the remaining bits of the file path.
* @param bool $forcedownload whether the user must be forced to download the file.
* @return bool false if file not found, does not return if found - justsend the file
*/
function quiz_question_pluginfile($course, $context, $component,
$filearea, $attemptid, $questionid, $args, $forcedownload) {
global $USER, $CFG;
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
$attemptobj = quiz_attempt::create($attemptid);
require_login($attemptobj->get_courseid(), false, $attemptobj->get_cm());
$questionids = array($questionid);
$attemptobj->load_questions($questionids);
$attemptobj->load_question_states($questionids);
if ($attemptobj->is_own_attempt() && !$attemptobj->is_finished()) {
// In the middle of an attempt.
if (!$attemptobj->is_preview_user()) {
$attemptobj->require_capability('mod/quiz:attempt');
}
$isreviewing = false;
} else {
// Reviewing an attempt.
$attemptobj->check_review_capability();
$isreviewing = true;
}
if (!$attemptobj->check_file_access($questionid, $isreviewing, $context->id,
$component, $filearea, $args, $forcedownload)) {
send_file_not_found();
}
$fs = get_file_storage();
$relativepath = implode('/', $args);
$fullpath = "/$context->id/$component/$filearea/$relativepath";
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
send_stored_file($file, 0, 0, $forcedownload);
}

View file

@ -456,21 +456,22 @@ function quiz_rescale_grade($rawgrade, $quiz, $round = true) {
* @param integer $quizid the id of the quiz object.
* @return string the comment that corresponds to this grade (empty string if there is not one.
*/
function quiz_feedback_for_grade($grade, $quizid) {
function quiz_feedback_for_grade($grade, $quiz, $context, $cm=null) {
global $DB;
$feedback = $DB->get_field_select('quiz_feedback', 'feedbacktext',
"quizid = ? AND mingrade <= ? AND $grade < maxgrade", array($quizid, $grade));
if (empty($feedback)) {
$feedback = '';
$feedback = $DB->get_record_select('quiz_feedback', "quizid = ? AND mingrade <= ? AND $grade < maxgrade", array($quiz->id, $grade));
if (empty($feedback->feedbacktext)) {
$feedback->feedbacktext = '';
}
// Clean the text, ready for display.
$formatoptions = new stdClass;
$formatoptions->noclean = true;
$feedback = format_text($feedback, FORMAT_MOODLE, $formatoptions);
$feedbacktext = file_rewrite_pluginfile_urls($feedback->feedbacktext, 'pluginfile.php', $context->id, 'mod_quiz', 'feedback', $feedback->id);
$feedbacktext = format_text($feedbacktext, $feedback->feedbacktextformat, $formatoptions);
return $feedback;
return $feedbacktext;
}
/**
@ -1263,4 +1264,4 @@ function quiz_get_js_module() {
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%');
$repeatarray = array();
$repeatarray[] = &MoodleQuickForm::createElement('text', 'feedbacktext', get_string('feedback', 'quiz'), array('size' => 50));
$repeatarray[] = &MoodleQuickForm::createElement('editor', 'feedbacktext', get_string('feedback', 'quiz'), null, array('maxfiles'=>EDITOR_UNLIMITED_FILES, 'noclean'=>true, 'context'=>$this->context));
$mform->setType('feedbacktext', PARAM_RAW);
$repeatarray[] = &MoodleQuickForm::createElement('text', 'feedbackboundaries', get_string('gradeboundary', 'quiz'), array('size' => 10));
$mform->setType('feedbackboundaries', PARAM_NOTAGS);
@ -313,7 +313,7 @@ class mod_quiz_mod_form extends moodleform_mod {
get_string('addmoreoverallfeedbacks', 'quiz'), true);
// Put some extra elements in before the button
$insertEl = &MoodleQuickForm::createElement('text', "feedbacktext[$nextel]", get_string('feedback', 'quiz'), array('size' => 50));
$insertEl = &MoodleQuickForm::createElement('editor', "feedbacktext[$nextel]", get_string('feedback', 'quiz'), null, array('maxfiles'=>EDITOR_UNLIMITED_FILES, 'noclean'=>true, 'context'=>$this->context));
$mform->insertElementBefore($insertEl, 'boundary_add_fields');
$insertEl = &MoodleQuickForm::createElement('static', 'gradeboundarystatic2', get_string('gradeboundary', 'quiz'), '0%');
@ -342,7 +342,19 @@ class mod_quiz_mod_form extends moodleform_mod {
if (count($this->_feedbacks)) {
$key = 0;
foreach ($this->_feedbacks as $feedback){
$default_values['feedbacktext['.$key.']'] = $feedback->feedbacktext;
$draftid = file_get_submitted_draft_itemid('feedbacktext['.$key.']');
$default_values['feedbacktext['.$key.']']['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'mod_quiz', // component
'feedback', // filarea
!empty($feedback->id)?(int)$feedback->id:null, // itemid
null,
$feedback->feedbacktext // text
);
$default_values['feedbacktext['.$key.']']['format'] = $feedback->feedbacktextformat;
$default_values['feedbacktext['.$key.']']['itemid'] = $draftid;
if ($feedback->mingrade > 0) {
$default_values['feedbackboundaries['.$key.']'] = (100.0 * $feedback->mingrade / $default_values['grade']) . '%';
}
@ -433,7 +445,7 @@ class mod_quiz_mod_form extends moodleform_mod {
}
}
for ($i = $numboundaries + 1; $i < count($data['feedbacktext']); $i += 1) {
if (!empty($data['feedbacktext'][$i] ) && trim($data['feedbacktext'][$i] ) != '') {
if (!empty($data['feedbacktext'][$i]['text']) && trim($data['feedbacktext'][$i]['text'] ) != '') {
$errors["feedbacktext[$i]"] = get_string('feedbackerrorjunkinfeedback', 'quiz', $i + 1);
}
}

View file

@ -200,7 +200,7 @@
}
/// Feedback if there is any, and the user is allowed to see it now.
$feedback = quiz_feedback_for_grade($grade, $attempt->quiz);
$feedback = $attemptobj->get_overall_feedback($grade);
if ($options->overallfeedback && $feedback) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('feedback', 'quiz') .
'</th><td class="cell">' . $feedback . '</td></tr>';

View file

@ -5,7 +5,7 @@
// This fragment is called by moodle_needs_upgrading() and /admin/index.php
////////////////////////////////////////////////////////////////////////////////
$module->version = 2010051801; // The (date) version of this module
$module->version = 2010080600; // The (date) version of this module
$module->requires = 2010080300; // Requires this Moodle version
$module->cron = 0; // How often should cron check this module (seconds)?

View file

@ -277,7 +277,7 @@
if ($feedbackcolumn && $attempt->timefinish > 0) {
if ($attemptoptions->overallfeedback) {
$row[] = quiz_feedback_for_grade($attemptgrade, $quiz->id);
$row[] = quiz_feedback_for_grade($attemptgrade, $quiz, $context, $cm);
} else {
$row[] = '';
}
@ -330,7 +330,7 @@
}
if ($feedbackcolumn) {
$resultinfo .= $OUTPUT->heading(get_string('overallfeedback', 'quiz'), 3, 'main');
$resultinfo .= '<p class="quizgradefeedback">'.quiz_feedback_for_grade($mygrade, $quiz->id)."</p>\n";
$resultinfo .= '<p class="quizgradefeedback">'.quiz_feedback_for_grade($mygrade, $quiz, $context, $cm)."</p>\n";
}
if ($resultinfo) {

View file

@ -630,6 +630,12 @@ if ($component === 'blog') {
send_file_not_found();
}
// ========================================================================================================================
} else if ($component === 'question') {
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'question', $filearea, $args, $forcedownload);
send_file_not_found();
// ========================================================================================================================
} else if (strpos($component, 'mod_') === 0) {
$modname = substr($component, 4);

View file

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

View file

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

View file

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

View file

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

View file

@ -211,7 +211,7 @@
$PAGE->set_title($strpreview);
$PAGE->set_heading($COURSE->fullname);
echo $OUTPUT->header();
if (!empty($quizid)) {
echo '<p class="quemodname">'.get_string('modulename', 'quiz') . ': ';
p(format_string($quiz->name));
@ -219,7 +219,7 @@
}
$number = 1;
echo '<form method="post" action="'.$url->out_omit_querystring().'" enctype="multipart/form-data" id="responseform">', "\n";
print_question($questions[$id], $curstate, $number, $quiz, $options);
print_question($questions[$id], $curstate, $number, $quiz, $options, $context);
echo '<div class="controls">';
echo html_writer::input_hidden_params($url);

108
question/previewlib.php Normal file
View file

@ -0,0 +1,108 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Library functions used by question/preview.php.
*
* @package core
* @subpackage questionengine
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Called via pluginfile.php -> question_pluginfile to serve files belonging to
* a question in a question_attempt when that attempt is a preview.
*
* @param object $course course settings object
* @param object $context context object
* @param string $component the name of the component we are serving files for.
* @param string $filearea the name of the file area.
* @param array $args the remaining bits of the file path.
* @param bool $forcedownload whether the user must be forced to download the file.
* @return bool false if file not found, does not return if found - justsend the file
*/
function question_preview_question_pluginfile($course, $context, $component,
$filearea, $attemptid, $questionid, $args, $forcedownload) {
global $USER, $SESSION, $DB, $CFG;
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
if (!$question = $DB->get_record('question', array('id' => $questionid))) {
return send_file_not_found();
}
if (!question_has_capability_on($question, 'use', $question->category)) {
send_file_not_found();
}
if (!isset($SESSION->quizpreview->states) || $SESSION->quizpreview->questionid != $questionid) {
send_file_not_found();
}
$states = end($SESSION->quizpreview->states);
if (!array_key_exists($question->id, $states)) {
send_file_not_found();
}
$state = $states[$question->id];
// Build fake cmoptions
$quiz = new cmoptions;
$quiz->id = 0;
$quiz->review = get_config('quiz', 'review');
if (empty($course->id)) {
$quiz->course = SITEID;
} else {
$quiz->course = $course->id;
}
$quiz->decimalpoints = get_config('quiz', 'decimalpoints');
$questions[$question->id] = $question;
get_question_options($questions);
// Build fake attempt
$timenow = time();
$attempt = new stdclass;
$attempt->quiz = $quiz->id;
$attempt->userid = $USER->id;
$attempt->attempt = 0;
$attempt->sumgrades = 0;
$attempt->timestart = $timenow;
$attempt->timefinish = 0;
$attempt->timemodified = $timenow;
$attempt->uniqueid = 0;
$attempt->id = 0;
$attempt->layout = $question->id;
$options = quiz_get_renderoptions($quiz, $attempt, $context, $state);
$options->noeditlink = true;
// XXX: mulitichoice type needs quiz id to get maxgrade
$options->quizid = 0;
if (!question_check_file_access($question, $state, $options, $context->id, $component,
$filearea, $args, $forcedownload)) {
send_file_not_found();
}
$fs = get_file_storage();
$relativepath = implode('/', $args);
$fullpath = "/$context->id/$component/$filearea/$relativepath";
if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
send_file_not_found();
}
send_stored_file($file, 0, 0, $forcedownload);
}

View file

@ -75,6 +75,7 @@ if ($cmid){
$thiscontext = get_context_instance(CONTEXT_MODULE, $cmid);
} elseif ($courseid) {
require_login($courseid, false);
$PAGE->set_pagelayout('course');
$thiscontext = get_context_instance(CONTEXT_COURSE, $courseid);
$module = null;
$cm = null;
@ -106,7 +107,7 @@ if ($id) {
} else if ($categoryid) {
// Category, but no qtype. They probably came from the addquestion.php
// script without choosing a question type. Send them back.
// script without choosing a question type. Send them back.
$addurl = new moodle_url('/question/addquestion.php', $url->params());
$addurl->param('validationerror', 1);
redirect($addurl);
@ -151,6 +152,8 @@ if ($id) {
} else { // creating a new question
require_capability('moodle/question:add', $categorycontext);
$formeditable = true;
$question->formoptions->canedit = question_has_capability_on($question, 'edit');
$question->formoptions->canmove = (question_has_capability_on($question, 'move') && $addpermission);
$question->formoptions->repeatelements = true;
$question->formoptions->movecontext = false;
}
@ -189,7 +192,9 @@ if ($cm !== null){
} else {
$toform->courseid = $COURSE->id;
}
$toform->inpopup = $inpopup;
$mform->set_data($toform);
if ($mform->is_cancelled()){

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="question/type/calculated/db" VERSION="20100208" COMMENT="XMLDB file for Moodle question/type/calculated. This question type also relies on the question_numerical_units table created by the numerical question type, and the tables created by the datasetdependent question type base class."
<XMLDB PATH="question/type/calculated/db" VERSION="20100720" COMMENT="XMLDB file for Moodle question/type/calculated. This question type also relies on the question_numerical_units table created by the numerical question type, and the tables created by the datasetdependent question type base class."
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
@ -29,10 +29,13 @@
<FIELD NAME="synchronize" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="question" NEXT="single"/>
<FIELD NAME="single" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="If 0 it multiple response (checkboxes). Otherwise it is radio buttons." PREVIOUS="synchronize" NEXT="shuffleanswers"/>
<FIELD NAME="shuffleanswers" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="Whether the choices can be randomly shuffled." PREVIOUS="single" NEXT="correctfeedback"/>
<FIELD NAME="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any correct response." PREVIOUS="shuffleanswers" NEXT="partiallycorrectfeedback"/>
<FIELD NAME="partiallycorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any partially correct response." PREVIOUS="correctfeedback" NEXT="incorrectfeedback"/>
<FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="partiallycorrectfeedback" NEXT="answernumbering"/>
<FIELD NAME="answernumbering" TYPE="char" LENGTH="10" NOTNULL="true" DEFAULT="abc" SEQUENCE="false" COMMENT="Indicates how and whether the choices should be numbered." PREVIOUS="incorrectfeedback"/>
<FIELD NAME="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any correct response." PREVIOUS="shuffleanswers" NEXT="correctfeedbackformat"/>
<FIELD NAME="correctfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="correctfeedback" NEXT="partiallycorrectfeedback"/>
<FIELD NAME="partiallycorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any partially correct response." PREVIOUS="correctfeedbackformat" NEXT="partiallycorrectfeedbackformat"/>
<FIELD NAME="partiallycorrectfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="partiallycorrectfeedback" NEXT="incorrectfeedback"/>
<FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="partiallycorrectfeedbackformat" NEXT="incorrectfeedbackformat"/>
<FIELD NAME="incorrectfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="incorrectfeedback" NEXT="answernumbering"/>
<FIELD NAME="answernumbering" TYPE="char" LENGTH="10" NOTNULL="true" DEFAULT="abc" SEQUENCE="false" COMMENT="Indicates how and whether the choices should be numbered." PREVIOUS="incorrectfeedbackformat"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/>

View file

@ -138,6 +138,62 @@ function xmldb_qtype_calculated_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2010020800, 'qtype', 'calculated');
}
if ($oldversion < 2010020801) {
// Define field correctfeedbackformat to be added to question_calculated_options
$table = new xmldb_table('question_calculated_options');
$field = new xmldb_field('correctfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'correctfeedback');
// Conditionally launch add field correctfeedbackformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Define field partiallycorrectfeedbackformat to be added to question_calculated_options
$field = new xmldb_field('partiallycorrectfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'partiallycorrectfeedback');
// Conditionally launch add field partiallycorrectfeedbackformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Define field incorrectfeedbackformat to be added to question_calculated_options
$field = new xmldb_field('incorrectfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'incorrectfeedback');
// Conditionally launch add field incorrectfeedbackformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// fix fieldformat
$rs = $DB->get_recordset('question_calculated_options');
foreach ($rs as $record) {
if ($CFG->texteditors !== 'textarea') {
if (!empty($record->correctfeedback)) {
$record->correctfeedback = text_to_html($record->correctfeedback);
}
$record->correctfeedbackformat = FORMAT_HTML;
if (!empty($record->partiallycorrectfeedback)) {
$record->partiallycorrectfeedback = text_to_html($record->partiallycorrectfeedback);
}
$record->partiallycorrectfeedbackformat = FORMAT_HTML;
if (!empty($record->incorrectfeedback)) {
$record->incorrectfeedback = text_to_html($record->incorrectfeedback);
}
$record->incorrectfeedbackformat = FORMAT_HTML;
} else {
$record->correctfeedbackformat = FORMAT_MOODLE;
$record->partiallycorrectfeedbackformat = FORMAT_MOODLE;
$record->incorrectfeedbackformat = FORMAT_MOODLE;
}
$DB->update_record('question_calculated_options', $record);
}
$rs->close();
// calculated savepoint reached
upgrade_plugin_savepoint(true, 2010020801, 'qtype', 'calculated');
}
return true;
}

View file

@ -1,4 +1,20 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines the editing form for the calculated question type.
*
@ -19,12 +35,12 @@ class question_edit_calculated_form extends question_edit_form {
* @var question_calculated_qtype
*/
public $qtypeobj;
public $questiondisplay ;
public $activecategory ;
public $categorychanged = false ;
public $questiondisplay;
public $activecategory;
public $categorychanged = false;
public $initialname = '';
public $reload = false ;
public $reload = false;
function question_edit_calculated_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
global $QTYPES, $SESSION, $CFG, $DB;
$this->question = $question;
@ -42,20 +58,20 @@ class question_edit_calculated_form extends question_edit_form {
$regs= array();
if(preg_match('~#\{([^[:space:]]*)#~',$question->name , $regs)){
$question->name = str_replace($regs[0], '', $question->name);
};
};
}
}else {
}
}
parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
}
function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
// $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
$repeated = array();
// $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
$repeated = array();
$repeated[] =& $mform->createElement('header', 'answerhdr', $label);
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
array('course' => $this->coursefilesid));
$repeated[] =& $mform->createElement('editor', 'feedback', get_string('feedback', 'quiz'), null, $this->editoroptions);
$repeatedoptions['answer']['type'] = PARAM_RAW;
$repeatedoptions['fraction']['default'] = 0;
$answersoption = 'answers';
@ -63,8 +79,8 @@ class question_edit_calculated_form extends question_edit_form {
$mform->setType('answer', PARAM_NOTAGS);
$addrepeated = array();
$addrepeated[] =& $mform->createElement('text', 'tolerance', get_string('tolerance', 'qtype_calculated'));
$addrepeated[] =& $mform->createElement('select', 'tolerancetype', get_string('tolerancetype', 'quiz'), $this->qtypeobj->tolerance_types());
$addrepeated[] =& $mform->createElement('text', 'tolerance', get_string('tolerance', 'qtype_calculated'));
$addrepeated[] =& $mform->createElement('select', 'tolerancetype', get_string('tolerancetype', 'quiz'), $this->qtypeobj->tolerance_types());
$repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
$repeatedoptions['tolerance']['default'] = 0.01;
@ -74,7 +90,7 @@ class question_edit_calculated_form extends question_edit_form {
$answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz'));
$addrepeated[] =& $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
array_splice($repeated, 3, 0, $addrepeated);
$repeated[1]->setLabel(get_string('correctanswerformula', 'quiz').'=');
$repeated[1]->setLabel(get_string('correctanswerformula', 'quiz').'=');
return $repeated;
}
@ -87,8 +103,8 @@ class question_edit_calculated_form extends question_edit_form {
function definition_inner(&$mform) {
global $QTYPES;
$this->qtypeobj =& $QTYPES[$this->qtype()];
// echo code left for testing period
// echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
// echo code left for testing period
// echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
$label = get_string('sharedwildcards', 'qtype_calculated');
$mform->addElement('hidden', 'initialcategory', 1);
$mform->addElement('hidden', 'reload', 1);
@ -100,43 +116,42 @@ class question_edit_calculated_form extends question_edit_form {
};
$addfieldsname='updatecategory';
$addstring=get_string("updatecategory", "qtype_calculated");
$mform->registerNoSubmitButton($addfieldsname);
$mform->registerNoSubmitButton($addfieldsname);
$mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
$mform->registerNoSubmitButton('createoptionbutton');
//editing as regular
$mform->setType('single', PARAM_INT);
$mform->setType('single', PARAM_INT);
$mform->addElement('hidden','shuffleanswers', '1');
$mform->setType('shuffleanswers', PARAM_INT);
$mform->addElement('hidden','answernumbering', 'abc');
$mform->setType('answernumbering', PARAM_SAFEDIR);
$mform->addElement('hidden','shuffleanswers', '1');
$mform->setType('shuffleanswers', PARAM_INT);
$mform->addElement('hidden','answernumbering', 'abc');
$mform->setType('answernumbering', PARAM_SAFEDIR);
$creategrades = get_grade_options();
$this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'),
$creategrades->gradeoptions, 1, 1);
$this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'), $creategrades->gradeoptions, 1, 1);
$repeated = array();
$QTYPES['numerical']->add_units_options($mform,$this);
$QTYPES['numerical']->add_units_elements($mform,$this);
$mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
$mform->addElement('hidden', $feedbackname);
$mform->addElement('editor', $feedbackname, get_string($feedbackname, 'qtype_calculated'), null, $this->editoroptions);
$mform->setType($feedbackname, PARAM_RAW);
}
//hidden elements
$mform->addElement('hidden', 'synchronize', '');
$mform->setType('synchronize', PARAM_INT);
$mform->addElement('hidden', 'wizard', 'datasetdefinitions');
$mform->setType('wizard', PARAM_ALPHA);
}
function set_data($question) {
function data_preprocessing($question) {
global $QTYPES;
$default_values = array();
@ -145,26 +160,58 @@ class question_edit_calculated_form extends question_edit_form {
if (count($answers)) {
$key = 0;
foreach ($answers as $answer){
$draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
$default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction;
$default_values['tolerance['.$key.']'] = $answer->tolerance;
$default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
$default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
$default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
$default_values['feedback['.$key.']'] = $answer->feedback;
$default_values['feedback['.$key.']'] = array();
$default_values['feedback['.$key.']']['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'question', // component
'answerfeedback', // filarea
!empty($answer->id)?(int)$answer->id:null, // itemid
$this->fileoptions, // options
$answer->feedback // text
);
$default_values['feedback['.$key.']']['format'] = $answer->feedbackformat;
$default_values['feedback['.$key.']']['itemid'] = $draftid;
$key++;
}
}
$default_values['synchronize'] = $question->options->synchronize ;
$QTYPES['numerical']->set_numerical_unit_data($question,$default_values);
$default_values['synchronize'] = $question->options->synchronize ;
// set unit data, prepare files in instruction area
$QTYPES['numerical']->set_numerical_unit_data($this, $question, $default_values);
}
if (isset($question->options->single)){
$default_values['single'] = $question->options->single;
$default_values['answernumbering'] = $question->options->answernumbering;
$default_values['shuffleanswers'] = $question->options->shuffleanswers;
$default_values['correctfeedback'] = $question->options->correctfeedback;
$default_values['partiallycorrectfeedback'] = $question->options->partiallycorrectfeedback;
$default_values['incorrectfeedback'] = $question->options->incorrectfeedback;
//$default_values['correctfeedback'] = $question->options->correctfeedback;
//$default_values['partiallycorrectfeedback'] = $question->options->partiallycorrectfeedback;
//$default_values['incorrectfeedback'] = $question->options->incorrectfeedback;
// prepare feedback editor to display files in draft area
foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
$draftid = file_get_submitted_draft_itemid($feedbackname);
$text = $question->options->$feedbackname;
$feedbackformat = $feedbackname . 'format';
$format = $question->options->$feedbackformat;
$default_values[$feedbackname] = array();
$default_values[$feedbackname]['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'qtype_calculated', // component
$feedbackname, // filarea
!empty($question->id)?(int)$question->id:null, // itemid
$this->fileoptions, // options
$text // text
);
$default_values[$feedbackname]['format'] = $format;
$default_values[$feedbackname]['itemid'] = $draftid;
}
}
$default_values['submitbutton'] = get_string('nextpage', 'qtype_calculated');
$default_values['makecopy'] = get_string('makecopynextpage', 'qtype_calculated');
@ -173,16 +220,16 @@ class question_edit_calculated_form extends question_edit_form {
update category button. The value can be obtain by
$qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
but is coded using existing functions
*/
$qu = new stdClass;
$el = new stdClass;
/* no need to call elementExists() here */
if ($this->_form->elementExists('category')){
*/
$qu = new stdClass;
$el = new stdClass;
/* no need to call elementExists() here */
if ($this->_form->elementExists('category')){
$el=$this->_form->getElement('category');
} else {
} else {
$el=$this->_form->getElement('categorymoveto');
}
if($value =$el->getSelected()) {
}
if($value =$el->getSelected()) {
$qu->category =$value[0];
}else {
$qu->category=$question->category;// on load $question->category is set by question.php
@ -191,7 +238,7 @@ class question_edit_calculated_form extends question_edit_form {
$this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ;
$question = (object)((array)$question + $default_values);
parent::set_data($question);
return $question;
}
function qtype() {
@ -200,21 +247,21 @@ class question_edit_calculated_form extends question_edit_form {
function validation($data, $files) {
global $QTYPES;
// echo code left for testing period
// echo code left for testing period
// echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
// echo "<p>data <pre>";print_r($data);echo "</pre></p>";
// echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
// echo "<p>data <pre>";print_r($data);echo "</pre></p>";
$errors = parent::validation($data, $files);
//verifying for errors in {=...} in question text;
$qtext = "";
$qtextremaining = $data['questiontext'] ;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
foreach ($possibledatasets as $name => $value) {
$qtextremaining = $data['questiontext']['text'];
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
}
// echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
// echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1];
@ -229,45 +276,45 @@ class question_edit_calculated_form extends question_edit_form {
$answers = $data['answer'];
$answercount = 0;
$maxgrade = false;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
$mandatorydatasets = array();
foreach ($answers as $key => $answer){
$mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
}
if ( count($mandatorydatasets )==0){
// $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
// $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
foreach ($answers as $key => $answer){
$errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
}
}
// regular calculated
foreach ($answers as $key => $answer){
//check no of choices
// the * for everykind of answer not actually implemented
$trimmedanswer = trim($answer);
if (($trimmedanswer!='')||$answercount==0){
$eqerror = qtype_calculated_find_formula_errors($trimmedanswer);
if (FALSE !== $eqerror){
$errors['answer['.$key.']'] = $eqerror;
}
// regular calculated
foreach ($answers as $key => $answer){
//check no of choices
// the * for everykind of answer not actually implemented
$trimmedanswer = trim($answer);
if (($trimmedanswer!='')||$answercount==0){
$eqerror = qtype_calculated_find_formula_errors($trimmedanswer);
if (FALSE !== $eqerror){
$errors['answer['.$key.']'] = $eqerror;
}
if ($trimmedanswer!=''){
if ('2' == $data['correctanswerformat'][$key]
&& '0' == $data['correctanswerlength'][$key]) {
}
if ($trimmedanswer!=''){
if ('2' == $data['correctanswerformat'][$key]
&& '0' == $data['correctanswerlength'][$key]) {
$errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
}
if (!is_numeric($data['tolerance'][$key])){
$errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
}
if ($data['fraction'][$key] == 1) {
$maxgrade = true;
}
$answercount++;
if (!is_numeric($data['tolerance'][$key])){
$errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
}
if ($data['fraction'][$key] == 1) {
$maxgrade = true;
}
//check grades
//TODO how should grade checking work here??
$answercount++;
}
//check grades
//TODO how should grade checking work here??
/*if ($answer != '') {
if ($data['fraction'][$key] > 0) {
$totalfraction += $data['fraction'][$key];
@ -276,10 +323,10 @@ class question_edit_calculated_form extends question_edit_form {
$maxfraction = $data['fraction'][$key];
}
}*/
}
}
//grade checking :
/// Perform sanity checks on fractional grades
//grade checking :
/// Perform sanity checks on fractional grades
/*if ( ) {
if ($maxfraction != 1) {
$maxfraction = $maxfraction * 100;
@ -292,7 +339,7 @@ class question_edit_calculated_form extends question_edit_form {
$errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
}
}
$units = $data['unit'];
$units = $data['unit'];
if (count($units)) {
foreach ($units as $key => $unit){
if (is_numeric($unit)){
@ -309,18 +356,17 @@ class question_edit_calculated_form extends question_edit_form {
}
}
}
}
}*/
$QTYPES['numerical']->validate_numerical_options($data, $errors) ;
if ($answercount==0){
$errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
}
if ($maxgrade == false) {
$errors['fraction[0]'] = get_string('fractionsnomax', 'question');
}
$QTYPES['numerical']->validate_numerical_options($data, $errors) ;
if ($answercount==0){
$errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
}
if ($maxgrade == false) {
$errors['fraction[0]'] = get_string('fractionsnomax', 'question');
}
return $errors;
}
}

View file

@ -45,6 +45,7 @@ $string['choosedatasetproperties'] = 'Choose wildcards dataset properties';
$string['choosedatasetproperties_help'] = 'A dataset is a set of values inserted in place of a wildcard. You can create a private dataset for a specific question, or a shared dataset that can be used for other calculated questions within the category.';
$string['correctanswershows'] = 'Correct answer shows';
$string['correctanswershowsformat'] = 'Format';
$string['correctfeedback'] = 'For any correct response';
$string['dataitemdefined']='with {$a} numerical values already defined is available';
$string['datasetrole']= ' The wild cards <strong>{x..}</strong> will be substituted by a numerical value from their dataset';
$string['deleteitem'] = 'Delete Item';
@ -61,6 +62,7 @@ $string['forceregenerationall'] = 'forceregeneration of all wildcards';
$string['forceregenerationshared'] = 'forceregeneration of only non-shared wildcards';
$string['getnextnow'] = 'Get New \'Item to Add\' Now';
$string['hexanotallowed'] = 'Dataset <strong>{$a->name}</strong> hexadecimal format value $a->value is not allowed' ;
$string['incorrectfeedback'] = 'For any incorrect response';
$string['item(s)'] = 'item(s)';
$string['itemno'] = 'Item {$a}';
$string['itemscount']='Items<br />Count';
@ -91,8 +93,9 @@ $string['nocommaallowed'] = 'The , cannot be used, use . as in 0.013 or 1.3e-2'
$string['nodataset'] = 'nothing - it is not a wild card';
$string['nosharedwildcard'] = 'No shared wild card in this category';
$string['notvalidnumber'] = 'Wild card value is not a valid number ' ;
$string['oneanswertrueansweroutsidelimits'] = 'At least one correct answer outside the true value limits.<br />Modify the answers tolerance settings available as Advanced parameters';
$string['oneanswertrueansweroutsidelimits'] = 'At least one correct answer outside the true value limits.<br />Modify the answers tolerance settings available as Advanced parameters';
$string['param'] = 'Param {<strong>{$a}</strong>}';
$string['partiallycorrectfeedback'] = 'For any partially correct response';
$string['possiblehdr'] = 'Possible wild cards present only in the question text';
$string['questiondatasets'] = 'Question datasets';
$string['questiondatasets_help'] = 'Question datasets of wild cards that will be used in each individual question';

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
$plugin->version = 2010020800;
$plugin->version = 2010020801;
$plugin->requires = 2007101000;

View file

@ -18,11 +18,11 @@ class question_edit_calculatedmulti_form extends question_edit_form {
*
* @var question_calculatedmulti_qtype
*/
var $qtypeobj;
public $qtypeobj;
public $questiondisplay ;
public $initialname = '';
public $reload = false ;
function question_edit_calculatedmulti_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
function question_edit_calculatedmulti_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true) {
global $QTYPES, $SESSION, $CFG, $DB;
$this->question = $question;
$this->qtypeobj =& $QTYPES[$this->question->qtype];
@ -38,22 +38,21 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$regs= array();
if(preg_match('~#\{([^[:space:]]*)#~',$question->name , $regs)){
$question->name = str_replace($regs[0], '', $question->name);
};
};
}
}else {
}
}
parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
}
function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
// $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
$repeated = array();
// $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
$repeated = array();
$repeated[] =& $mform->createElement('header', 'answerhdr', $label);
// if ($this->editasmultichoice == 1){
// if ($this->editasmultichoice == 1){
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
array('course' => $this->coursefilesid));
$repeated[] =& $mform->createElement('editor', 'feedback', get_string('feedback', 'quiz'), null, $this->editoroptions);
$repeatedoptions['answer']['type'] = PARAM_RAW;
$repeatedoptions['fraction']['default'] = 0;
$answersoption = 'answers';
@ -61,8 +60,8 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$mform->setType('answer', PARAM_NOTAGS);
$addrepeated = array();
$addrepeated[] =& $mform->createElement('hidden', 'tolerance');
$addrepeated[] =& $mform->createElement('hidden', 'tolerancetype',1);
$addrepeated[] =& $mform->createElement('hidden', 'tolerance');
$addrepeated[] =& $mform->createElement('hidden', 'tolerancetype',1);
$repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
$repeatedoptions['tolerance']['default'] = 0.01;
@ -72,7 +71,7 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz'));
$addrepeated[] =& $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
array_splice($repeated, 3, 0, $addrepeated);
$repeated[1]->setLabel('...<strong>{={x}+..}</strong>...');
$repeated[1]->setLabel('...<strong>{={x}+..}</strong>...');
return $repeated;
}
@ -85,79 +84,78 @@ class question_edit_calculatedmulti_form extends question_edit_form {
function definition_inner(&$mform) {
global $QTYPES;
$this->qtypeobj =& $QTYPES[$this->qtype()];
// echo code left for testing period
// echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
// echo code left for testing period
// echo "<p>question ".optional_param('multichoice', '', PARAM_RAW)." optional<pre>";print_r($this->question);echo "</pre></p>";
$label = get_string("sharedwildcards", "qtype_calculated");
$mform->addElement('hidden', 'initialcategory', 1);
$mform->addElement('hidden', 'reload', 1);
$mform->setType('initialcategory', PARAM_INT);
// $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question);
$html2 ="";
// $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question);
$html2 ="";
$mform->insertElementBefore($mform->createElement('static','listcategory',$label,$html2),'name');
if(isset($this->question->id )){
$mform->insertElementBefore($mform->createElement('static','initialname',get_string('questionstoredname','qtype_calculated'),$this->initialname),'name');
};
$addfieldsname='updatecategory';
$addstring=get_string("updatecategory", "qtype_calculated");
$mform->registerNoSubmitButton($addfieldsname);
$mform->registerNoSubmitButton($addfieldsname);
$this->editasmultichoice = 1 ;
$mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
$mform->registerNoSubmitButton('createoptionbutton');
$mform->addElement('hidden', 'multichoice',$this->editasmultichoice);
$mform->setType('multichoice', PARAM_INT);
$mform->addElement('hidden', 'multichoice',$this->editasmultichoice);
$mform->setType('multichoice', PARAM_INT);
// $mform->addElement('header', 'choicehdr',get_string('multichoicecalculatedquestion', 'qtype_calculated'));
$menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice'));
$mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu);
$mform->setDefault('single', 1);
$mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0,1));
$mform->addHelpButton('shuffleanswers', 'shuffleanswers', 'qtype_multichoice');
$mform->setDefault('shuffleanswers', 1);
$numberingoptions = $QTYPES['multichoice']->get_numbering_styles();
$menu = array();
foreach ($numberingoptions as $numberingoption) {
$menu[$numberingoption] = get_string('answernumbering' . $numberingoption, 'qtype_multichoice');
}
$mform->addElement('select', 'answernumbering', get_string('answernumbering', 'qtype_multichoice'), $menu);
$mform->setDefault('answernumbering', 'abc');
// $mform->addElement('header', 'choicehdr',get_string('multichoicecalculatedquestion', 'qtype_calculated'));
$menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice'));
$mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu);
$mform->setDefault('single', 1);
$mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0,1));
$mform->setHelpButton('shuffleanswers', array('multichoiceshuffle', get_string('shuffleanswers','qtype_multichoice'), 'qtype_multichoice'));
$mform->setDefault('shuffleanswers', 1);
$numberingoptions = $QTYPES['multichoice']->get_numbering_styles();
$menu = array();
foreach ($numberingoptions as $numberingoption) {
$menu[$numberingoption] = get_string('answernumbering' . $numberingoption, 'qtype_multichoice');
}
$mform->addElement('select', 'answernumbering', get_string('answernumbering', 'qtype_multichoice'), $menu);
$mform->setDefault('answernumbering', 'abc');
$creategrades = get_grade_options();
$this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'),
$creategrades->gradeoptionsfull, max(5, QUESTION_NUMANS_START));
$this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'),
$creategrades->gradeoptionsfull, max(5, QUESTION_NUMANS_START));
$repeated = array();
// if ($this->editasmultichoice == 1){
$nounits = optional_param('nounits', 1, PARAM_INT);
$mform->addElement('hidden', 'nounits', $nounits);
$mform->setType('nounits', PARAM_INT);
$mform->setConstants(array('nounits'=>$nounits));
for ($i=0; $i< $nounits; $i++) {
$mform->addElement('hidden','unit'."[$i]", optional_param('unit'."[$i]", '', PARAM_NOTAGS));
$mform->setType('unit'."[$i]", PARAM_NOTAGS);
$mform->addElement('hidden', 'multiplier'."[$i]", optional_param('multiplier'."[$i]", '', PARAM_NUMBER));
$mform->setType('multiplier'."[$i]", PARAM_NUMBER);
}
$mform->addElement('hidden','unitgradingtype',optional_param('unitgradingtype', '', PARAM_INT)) ;
$mform->addElement('hidden','unitpenalty',optional_param('unitpenalty', '', PARAM_NUMBER)) ;
$mform->addElement('hidden','showunits',optional_param('showunits', '', PARAM_INT)) ;
$mform->addElement('hidden','unitsleft',optional_param('unitsleft', '', PARAM_INT)) ;
$mform->addElement('hidden','instructions',optional_param('instructions', '', PARAM_RAW)) ;
// if ($this->editasmultichoice == 1){
$nounits = optional_param('nounits', 1, PARAM_INT);
$mform->addElement('hidden', 'nounits', $nounits);
$mform->setType('nounits', PARAM_INT);
$mform->setConstants(array('nounits'=>$nounits));
for ($i=0; $i< $nounits; $i++) {
$mform->addElement('hidden','unit'."[$i]", optional_param('unit'."[$i]", '', PARAM_NOTAGS));
$mform->setType('unit'."[$i]", PARAM_NOTAGS);
$mform->addElement('hidden', 'multiplier'."[$i]", optional_param('multiplier'."[$i]", '', PARAM_NUMBER));
$mform->setType('multiplier'."[$i]", PARAM_NUMBER);
}
$mform->addElement('hidden','unitgradingtype',optional_param('unitgradingtype', '', PARAM_INT)) ;
$mform->addElement('hidden','unitpenalty',optional_param('unitpenalty', '', PARAM_NUMBER)) ;
$mform->addElement('hidden','showunits',optional_param('showunits', '', PARAM_INT)) ;
$mform->addElement('hidden','unitsleft',optional_param('unitsleft', '', PARAM_INT)) ;
$mform->addElement('hidden','instructions',optional_param('instructions', '', PARAM_RAW)) ;
$mform->setType('addunits','hidden');
$mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
$mform->setType('addunits','hidden');
$mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
$mform->addElement('htmleditor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'),
array('course' => $this->coursefilesid));
$mform->setType($feedbackname, PARAM_RAW);
}
foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
$mform->addElement('editor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'), null, $this->editoroptions);
$mform->setType($feedbackname, PARAM_RAW);
}
//hidden elements
$mform->addElement('hidden', 'synchronize', '');
$mform->setType('synchronize', PARAM_INT);
@ -168,40 +166,43 @@ class question_edit_calculatedmulti_form extends question_edit_form {
}
$mform->addElement('hidden', 'wizard', 'datasetdefinitions');
$mform->setType('wizard', PARAM_ALPHA);
}
function set_data($question) {
$default_values['multichoice']= $this->editasmultichoice ; //$this->editasmultichoice ;
function data_preprocessing($question) {
$default_values['multichoice']= $this->editasmultichoice ; //$this->editasmultichoice ;
if (isset($question->options)){
$answers = $question->options->answers;
if (count($answers)) {
$key = 0;
foreach ($answers as $answer){
$draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
$default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction;
$default_values['tolerance['.$key.']'] = $answer->tolerance;
$default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
$default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
$default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
$default_values['feedback['.$key.']'] = $answer->feedback;
$default_values['feedback['.$key.']'] = array();
// prepare draftarea
$default_values['feedback['.$key.']']['text'] = file_prepare_draft_area($draftid, $this->context->id, 'question', 'answerfeedback', empty($answer->id)?null:(int)$answer->id, null, $answer->feedback);
$default_values['feedback['.$key.']']['format'] = $answer->feedbackformat;
$default_values['feedback['.$key.']']['itemid'] = $draftid;
$key++;
}
}
// $default_values['unitgradingtype'] = $question->options->unitgradingtype ;
// $default_values['unitpenalty'] = $question->options->unitpenalty ;
// $default_values['showunits'] = $question->options->showunits ;
// $default_values['unitsleft'] = $question->options->unitsleft ;
// $default_values['instructions'] = $question->options->instructions ;
$default_values['synchronize'] = $question->options->synchronize ;
// $default_values['unitgradingtype'] = $question->options->unitgradingtype ;
// $default_values['unitpenalty'] = $question->options->unitpenalty ;
// $default_values['showunits'] = $question->options->showunits ;
// $default_values['unitsleft'] = $question->options->unitsleft ;
// $default_values['instructions'] = $question->options->instructions ;
$default_values['synchronize'] = $question->options->synchronize ;
if (isset($question->options->units)){
$units = array_values($question->options->units);
// make sure the default unit is at index 0
usort($units, create_function('$a, $b',
'if (1.0 === (float)$a->multiplier) { return -1; } else '.
'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
'if (1.0 === (float)$a->multiplier) { return -1; } else '.
'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
if (count($units)) {
$key = 0;
foreach ($units as $unit){
@ -213,30 +214,51 @@ class question_edit_calculatedmulti_form extends question_edit_form {
}
}
if (isset($question->options->single)){
$default_values['single'] = $question->options->single;
$default_values['answernumbering'] = $question->options->answernumbering;
$default_values['shuffleanswers'] = $question->options->shuffleanswers;
$default_values['correctfeedback'] = $question->options->correctfeedback;
$default_values['partiallycorrectfeedback'] = $question->options->partiallycorrectfeedback;
$default_values['incorrectfeedback'] = $question->options->incorrectfeedback;
}
$default_values['single'] = $question->options->single;
$default_values['answernumbering'] = $question->options->answernumbering;
$default_values['shuffleanswers'] = $question->options->shuffleanswers;
}
$default_values['submitbutton'] = get_string('nextpage', 'qtype_calculated');
$default_values['makecopy'] = get_string('makecopynextpage', 'qtype_calculated');
/* set the wild cards category display given that on loading the category element is
unselected when processing this function but have a valid value when processing the
update category button. The value can be obtain by
$qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
but is coded using existing functions
*/
$qu = new stdClass;
$el = new stdClass;
/* no need to call elementExists() here */
if ($this->_form->elementExists('category')){
// prepare draft files
foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
if (!isset($question->options->$feedbackname)) {
continue;
}
$text = $question->options->$feedbackname;
$draftid = file_get_submitted_draft_itemid($feedbackname);
$feedbackformat = $feedbackname . 'format';
$format = $question->options->$feedbackformat;
$default_values[$feedbackname] = array();
$default_values[$feedbackname]['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'qtype_calculatedmulti', // component
$feedbackname, // filarea
!empty($question->id)?(int)$question->id:null, // itemid
$this->fileoptions, // options
$text // text
);
$default_values[$feedbackname]['format'] = $format;
$default_values[$feedbackname]['itemid'] = $draftid;
}
/**
* set the wild cards category display given that on loading the category element is
* unselected when processing this function but have a valid value when processing the
* update category button. The value can be obtain by
* $qu->category =$this->_form->_elements[$this->_form->_elementIndex['category']]->_values[0];
* but is coded using existing functions
*/
$qu = new stdClass;
$el = new stdClass;
/* no need to call elementExists() here */
if ($this->_form->elementExists('category')){
$el=$this->_form->getElement('category');
} else {
} else {
$el=$this->_form->getElement('categorymoveto');
}
if($value =$el->getSelected()) {
}
if($value =$el->getSelected()) {
$qu->category =$value[0];
}else {
$qu->category=$question->category;// on load $question->category is set by question.php
@ -244,8 +266,7 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$html2 = $this->qtypeobj->print_dataset_definitions_category($qu);
$this->_form->_elements[$this->_form->_elementIndex['listcategory']]->_text = $html2 ;
$question = (object)((array)$question + $default_values);
parent::set_data($question);
return $question;
}
function qtype() {
@ -253,21 +274,20 @@ class question_edit_calculatedmulti_form extends question_edit_form {
}
function validation($data, $files) {
// echo code left for testing period
// echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
// echo "<p>data <pre>";print_r($data);echo "</pre></p>";
// echo code left for testing period
// echo "<p>question <pre>";print_r($this->question);echo "</pre></p>";
// echo "<p>data <pre>";print_r($data);echo "</pre></p>";
$errors = parent::validation($data, $files);
//verifying for errors in {=...} in question text;
$qtext = "";
$qtextremaining = $data['questiontext'] ;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
foreach ($possibledatasets as $name => $value) {
$qtextremaining = $data['questiontext']['text'];
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
}
// echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
// echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1];
@ -282,13 +302,13 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$answers = $data['answer'];
$answercount = 0;
$maxgrade = false;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
$mandatorydatasets = array();
foreach ($answers as $key => $answer){
$mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
}
if ( count($mandatorydatasets )==0){
// $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
// $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
foreach ($answers as $key => $answer){
$errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
}
@ -296,15 +316,15 @@ class question_edit_calculatedmulti_form extends question_edit_form {
if ($data['multichoice']== 1 ){
foreach ($answers as $key => $answer){
$trimmedanswer = trim($answer);
if (($trimmedanswer!='')||$answercount==0){
if (($trimmedanswer!='')||$answercount==0){
//verifying for errors in {=...} in answer text;
$qanswer = "";
$qanswerremaining = $trimmedanswer ;
$possibledatasets = $this->qtypeobj->find_dataset_names($trimmedanswer);
foreach ($possibledatasets as $name => $value) {
foreach ($possibledatasets as $name => $value) {
$qanswerremaining = str_replace('{'.$name.'}', '1', $qanswerremaining);
}
// echo "numericalquestion qanswerremaining <pre>";print_r($possibledatasets);
// echo "numericalquestion qanswerremaining <pre>";print_r($possibledatasets);
while (preg_match('~\{=([^[:space:]}]*)}~', $qanswerremaining, $regs1)) {
$qanswersplits = explode($regs1[0], $qanswerremaining, 2);
$qanswer =$qanswer.$qanswersplits[0];
@ -320,16 +340,16 @@ class question_edit_calculatedmulti_form extends question_edit_form {
}
if ($trimmedanswer!=''){
if ('2' == $data['correctanswerformat'][$key]
&& '0' == $data['correctanswerlength'][$key]) {
$errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
}
&& '0' == $data['correctanswerlength'][$key]) {
$errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
}
if (!is_numeric($data['tolerance'][$key])){
$errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
}
if ($data['fraction'][$key] == 1) {
$maxgrade = true;
$maxgrade = true;
}
$answercount++;
}
//check grades
@ -342,14 +362,14 @@ class question_edit_calculatedmulti_form extends question_edit_form {
if ($data['fraction'][$key] > $maxfraction) {
$maxfraction = $data['fraction'][$key];
}
}
}
}
if ($answercount==0){
$errors['answer[0]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
$errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
} elseif ($answercount==1){
$errors['answer[1]'] = get_string('notenoughanswers', 'qtype_multichoice', 2);
}
/// Perform sanity checks on fractional grades
@ -365,17 +385,15 @@ class question_edit_calculatedmulti_form extends question_edit_form {
$errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
}
}
if ($answercount==0){
$errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
}
if ($maxgrade == false) {
$errors['fraction[0]'] = get_string('fractionsnomax', 'question');
}
}
return $errors;
}
}

View file

@ -0,0 +1,31 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Serve question type files
*
* @since 2.0
* @package questionbank
* @subpackage questiontypes
* @author Dongsheng Cai <dongsheng@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function qtype_calculatedmulti_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $DB, $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_calculatedmulti', $filearea, $args, $forcedownload);
}

View file

@ -1,17 +1,30 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/////////////////
// CALCULATED ///
/////////////////
/// QUESTION TYPE CLASS //////////////////
class question_calculatedmulti_qtype extends question_calculated_qtype {
// Used by the function custom_generator_tools:
var $calcgenerateidhasbeenadded = false;
public $calcgenerateidhasbeenadded = false;
public $virtualqtype = false;
function name() {
@ -28,14 +41,13 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
function save_question_options($question) {
//$options = $question->subtypeoptions;
// Get old answers:
global $CFG, $DB, $QTYPES ;
$context = $question->context;
if (isset($question->answer) && !isset($question->answers)) {
$question->answers = $question->answer;
}
// calculated options
$update = true ;
$update = true ;
$options = $DB->get_record("question_calculated_options", array("question" => $question->id));
if (!$options) {
$update = false;
@ -46,19 +58,20 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$options->single = $question->single;
$options->answernumbering = $question->answernumbering;
$options->shuffleanswers = $question->shuffleanswers;
$options->correctfeedback = trim($question->correctfeedback);
$options->partiallycorrectfeedback = trim($question->partiallycorrectfeedback);
$options->incorrectfeedback = trim($question->incorrectfeedback);
// save question feedback files
foreach (array('correct', 'partiallycorrect', 'incorrect') as $feedbacktype) {
$feedbackname = $feedbacktype . 'feedback';
$feedbackformat = $feedbackname . 'format';
$feedback = $question->$feedbackname;
$options->$feedbackformat = $feedback['format'];
$options->$feedbackname = file_save_draft_area_files($feedback['itemid'], $context->id, 'qtype_calculatedmulti', $feedbackname, $question->id, self::$fileoptions, trim($feedback['text']));
}
if ($update) {
if (!$DB->update_record("question_calculated_options", $options)) {
$result->error = "Could not update calculated question options! (id=$options->id)";
return $result;
}
$DB->update_record("question_calculated_options", $options);
} else {
if (!$DB->insert_record("question_calculated_options", $options)) {
$result->error = "Could not insert calculated question options!";
return $result;
}
$DB->insert_record("question_calculated_options", $options);
}
// Get old versions of the objects
@ -71,8 +84,9 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
}
// Save the units.
$virtualqtype = $this->get_virtual_qtype( $question);
// $result = $virtualqtype->save_numerical_units($question);
$virtualqtype = $this->get_virtual_qtype($question);
// TODO: What is this?
// $result = $virtualqtype->save_numerical_units($question);
if (isset($result->error)) {
return $result;
} else {
@ -80,21 +94,25 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
}
// Insert all the new answers
if (isset($question->answer) && !isset($question->answers)) {
$question->answers=$question->answer;
$question->answers = $question->answer;
}
foreach ($question->answers as $key => $dataanswer) {
if ( trim($dataanswer) != '' ) {
if ( trim($dataanswer) != '' ) {
$answer = new stdClass;
$answer->question = $question->id;
$answer->answer = trim($dataanswer);
$answer->fraction = $question->fraction[$key];
$answer->feedback = trim($question->feedback[$key]);
$answer->feedback = trim($question->feedback[$key]['text']);
$answer->feedbackformat = $question->feedback[$key]['format'];
if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
$answer->id = $oldanswer->id;
$answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $answer->feedback);
$DB->update_record("question_answers", $answer);
} else { // This is a completely new answer
$answer->id = $DB->insert_record("question_answers", $answer);
$feedbacktext = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $answer->feedback);
$DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$answer->id));
}
// Set up the options object
@ -129,15 +147,15 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$DB->delete_records('question_calculated', array('id' => $oo->id));
}
}
// $result = $QTYPES['numerical']->save_numerical_options($question);
// if (isset($result->error)) {
// return $result;
// }
// $result = $QTYPES['numerical']->save_numerical_options($question);
// if (isset($result->error)) {
// return $result;
// }
if( isset($question->import_process)&&$question->import_process){
$this->import_datasets($question);
}
}
// Report any problems.
if (!empty($result->notice)) {
return $result;
@ -148,30 +166,25 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
// Find out how many datasets are available
global $CFG, $DB, $QTYPES, $OUTPUT ;
if(!$maxnumber = (int)$DB->get_field_sql(
"SELECT MIN(a.itemcount)
FROM {question_dataset_definitions} a,
{question_datasets} b
WHERE b.question = ?
AND a.id = b.datasetdefinition", array($question->id))) {
$maxnumber = (int)$DB->get_field_sql(
"SELECT MIN(a.itemcount)
FROM {question_dataset_definitions} a, {question_datasets} b
WHERE b.question = ? AND a.id = b.datasetdefinition", array($question->id));
if (!$maxnumber) {
print_error('cannotgetdsforquestion', 'question', '', $question->id);
}
$sql = "SELECT i.*
FROM {question_datasets} d,
{question_dataset_definitions} i
WHERE d.question = ?
AND d.datasetdefinition = i.id
AND i.category != 0
";
$sql = "SELECT i.*
FROM {question_datasets} d, {question_dataset_definitions} i
WHERE d.question = ? AND d.datasetdefinition = i.id AND i.category != 0";
if (!$question->options->synchronize || !$records = $DB->get_records_sql($sql, array($question->id))) {
$synchronize_calculated = false ;
}else {
// i.e records is true so test coherence
$coherence = true ;
$a = new stdClass ;
$a->qid = $question->id ;
$a->qcat = $question->category ;
foreach($records as $def ){
$synchronize_calculated = false ;
} else {
// i.e records is true so test coherence
$coherence = true ;
$a = new stdClass ;
$a->qid = $question->id ;
$a->qcat = $question->category ;
foreach($records as $def ){
if ($def->category != $question->category){
$a->name = $def->name;
$a->sharedcat = $def->category ;
@ -180,11 +193,11 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
}
}
if(!$coherence){
echo $OUTPUT->notification(get_string('nocoherencequestionsdatyasetcategory','qtype_calculated',$a));
}
$synchronize_calculated = true ;
}
echo $OUTPUT->notification(get_string('nocoherencequestionsdatyasetcategory','qtype_calculated',$a));
}
$synchronize_calculated = true ;
}
// Choose a random dataset
// maxnumber sould not be breater than 100
@ -194,41 +207,40 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
if ( $synchronize_calculated === false ) {
$state->options->datasetitem = rand(1, $maxnumber);
}else{
$state->options->datasetitem = intval( $maxnumber * substr($attempt->timestart,-2) /100 ) ;
$state->options->datasetitem = intval( $maxnumber * substr($attempt->timestart,-2) /100 ) ;
if ($state->options->datasetitem < 1) {
$state->options->datasetitem =1 ;
} else if ($state->options->datasetitem > $maxnumber){
$state->options->datasetitem = $maxnumber ;
}
};
};
$state->options->dataset =
$this->pick_question_dataset($question,$state->options->datasetitem);
// create an array of answerids ??? why so complicated ???
$answerids = array_values(array_map(create_function('$val',
'return $val->id;'), $question->options->answers));
// Shuffle the answers if required
if (!empty($cmoptions->shuffleanswers) and !empty($question->options->shuffleanswers)) {
$answerids = swapshuffle($answerids);
}
$state->options->order = $answerids;
// Create empty responses
if ($question->options->single) {
$state->responses = array('' => '');
} else {
$state->responses = array();
}
return true;
$this->pick_question_dataset($question,$state->options->datasetitem);
// create an array of answerids ??? why so complicated ???
$answerids = array_values(array_map(create_function('$val',
'return $val->id;'), $question->options->answers));
// Shuffle the answers if required
if (!empty($cmoptions->shuffleanswers) and !empty($question->options->shuffleanswers)) {
$answerids = swapshuffle($answerids);
}
$state->options->order = $answerids;
// Create empty responses
if ($question->options->single) {
$state->responses = array('' => '');
} else {
$state->responses = array();
}
return true;
}
function save_session_and_responses(&$question, &$state) {
global $DB;
$responses = 'dataset'.$state->options->datasetitem.'-' ;
$responses = 'dataset'.$state->options->datasetitem.'-' ;
$responses .= implode(',', $state->options->order) . ':';
$responses .= implode(',', $state->responses);
// Set the legacy answer field
// Set the legacy answer field
if (!$DB->set_field('question_states', 'answer', $responses, array('id'=> $state->id))) {
return false;
}
@ -241,7 +253,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
foreach ($form->answers as $key => $answer) {
$a->answer = trim($form->answer[$key]);
$a->fraction = $form->fraction[$key];//new
$a->tolerance = $form->tolerance[$key];
$a->tolerance = $form->tolerance[$key];
$a->tolerancetype = $form->tolerancetype[$key];
$a->correctanswerlength = $form->correctanswerlength[$key];
$a->correctanswerformat = $form->correctanswerformat[$key];
@ -251,57 +263,53 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
return $question;
}
function convert_answers (&$question, &$state){
foreach ($question->options->answers as $key => $answer) {
$answer->answer = $this->substitute_variables($answer->answer, $state->options->dataset);
//evaluate the equations i.e {=5+4)
$qtext = "";
$qtextremaining = $answer->answer ;
// while (preg_match('~\{(=)|%[[:digit]]\.=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
foreach ($question->options->answers as $key => $answer) {
$answer->answer = $this->substitute_variables($answer->answer, $state->options->dataset);
//evaluate the equations i.e {=5+4)
$qtext = "";
$qtextremaining = $answer->answer ;
// while (preg_match('~\{(=)|%[[:digit]]\.=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1];
if (empty($regs1[1])) {
$str = '';
} else {
if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
$str=$formulaerrors ;
}else {
eval('$str = '.$regs1[1].';');
$texteval= qtype_calculated_calculate_answer(
$str, $state->options->dataset, $answer->tolerance,
$answer->tolerancetype, $answer->correctanswerlength,
$answer->correctanswerformat, '');
$qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext = $qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1];
if (empty($regs1[1])) {
$str = '';
} else {
if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
$str=$formulaerrors ;
}else {
eval('$str = '.$regs1[1].';');
$texteval= qtype_calculated_calculate_answer(
$str, $state->options->dataset, $answer->tolerance,
$answer->tolerancetype, $answer->correctanswerlength,
$answer->correctanswerformat, '');
$str = $texteval->answer;
}
}
$qtext = $qtext.$str ;
}
}
$answer->answer = $qtext.$qtextremaining ; ;
$qtext = $qtext.$str ;
}
$answer->answer = $qtext.$qtextremaining ; ;
}
}
function get_default_numerical_unit($question,$virtualqtype){
$unit = '';
return $unit ;
}
function get_default_numerical_unit($question, $virtualqtype){
$unit = '';
return $unit ;
}
function grade_responses(&$question, &$state, $cmoptions) {
// Forward the grading to the virtual qtype
// We modify the question to look like a multichoice question
// for grading nothing to do
// for grading nothing to do
/* $numericalquestion = fullclone($question);
foreach ($numericalquestion->options->answers as $key => $answer) {
$answer = $numericalquestion->options->answers[$key]->answer; // for PHP 4.x
$numericalquestion->options->answers[$key]->answer = $this->substitute_variables_and_eval($answer,
$state->options->dataset);
}*/
$virtualqtype = $this->get_virtual_qtype( $question);
}*/
$virtualqtype = $this->get_virtual_qtype( $question);
return $virtualqtype->grade_responses($question, $state, $cmoptions) ;
}
@ -319,7 +327,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$this->convert_answers ($numericalquestion, $state);
$this->convert_questiontext ($numericalquestion, $state);
/* $numericalquestion->questiontext = $this->substitute_variables_and_eval(
$numericalquestion->questiontext, $state->options->dataset);*/
$numericalquestion->questiontext, $state->options->dataset);*/
$responses = $virtualqtype->get_all_responses($numericalquestion, $state);
$response = reset($responses->responses);
$correct = $response->answer.' : ';
@ -335,8 +343,8 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
function create_virtual_qtype() {
global $CFG;
require_once("$CFG->dirroot/question/type/multichoice/questiontype.php");
return new question_multichoice_qtype();
require_once("$CFG->dirroot/question/type/multichoice/questiontype.php");
return new question_multichoice_qtype();
}
@ -353,7 +361,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
} else {
$strheader .= $delimiter.$answer->answer;
}
$delimiter = '<br/>';
$delimiter = '<br/>';
}
return $strheader;
}
@ -369,7 +377,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$unit = $unit->unit;
} else {
$unit = '';
}*/
}*/
$answers = fullclone($answers);
$strmin = get_string('min', 'quiz');
@ -377,29 +385,29 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$errors = '';
$delimiter = ': ';
foreach ($answers as $key => $answer) {
$answer->answer = $this->substitute_variables($answer->answer, $data);
//evaluate the equations i.e {=5+4)
$qtext = "";
$qtextremaining = $answer->answer ;
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1];
if (empty($regs1[1])) {
$str = '';
} else {
if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
$str=$formulaerrors ;
}else {
eval('$str = '.$regs1[1].';');
}
}
$qtext = $qtext.$str ;
$answer->answer = $this->substitute_variables($answer->answer, $data);
//evaluate the equations i.e {=5+4)
$qtext = "";
$qtextremaining = $answer->answer ;
while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1];
if (empty($regs1[1])) {
$str = '';
} else {
if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){
$str=$formulaerrors ;
}else {
eval('$str = '.$regs1[1].';');
}
}
$answer->answer = $qtext.$qtextremaining ; ;
$comment->stranswers[$key]= $answer->answer ;
$qtext = $qtext.$str ;
}
$answer->answer = $qtext.$qtextremaining;
$comment->stranswers[$key] = $answer->answer;
/* $formula = $this->substitute_variables($answer->answer,$data);
$formattedanswer = qtype_calculated_calculate_answer(
$answer->answer, $data, $answer->tolerance,
@ -411,7 +419,7 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
}else {
eval('$answer->answer = '.$formula.';') ;
$virtualqtype->get_tolerance_interval($answer);
}
}
if ($answer->min === '') {
// This should mean that something is wrong
$comment->stranswers[$key] = " $formattedanswer->answer".'<br/><br/>';
@ -432,15 +440,11 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
$comment->stranswers[$key] .=get_string('trueanswerinsidelimits','qtype_calculated',$correcttrue);//' True answer :'.$calculated->trueanswer.' inside limits';
}
$comment->stranswers[$key] .='';
}*/
}*/
}
return fullclone($comment);
}
function get_correct_responses1(&$question, &$state) {
$virtualqtype = $this->get_virtual_qtype( $question);
/* if ($question->options->multichoice != 1 ) {
@ -464,71 +468,148 @@ class question_calculatedmulti_qtype extends question_calculated_qtype {
return $correct;
}
}
}else{**/
return $virtualqtype->get_correct_responses($question, $state) ;
// }
}else{**/
return $virtualqtype->get_correct_responses($question, $state) ;
// }
return null;
}
function get_virtual_qtype() {
global $QTYPES;
// if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){
$this->virtualqtype =& $QTYPES['multichoice'];
// }else {
// $this->virtualqtype =& $QTYPES['numerical'];
// }
// if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){
$this->virtualqtype =& $QTYPES['multichoice'];
// }else {
// $this->virtualqtype =& $QTYPES['numerical'];
// }
return $this->virtualqtype;
}
/**
* Runs all the code required to set up and save an essay question for testing purposes.
* Alternate DB table prefix may be used to facilitate data deletion.
*/
function generate_test($name, $courseid = null) {
global $DB;
list($form, $question) = parent::generate_test($name, $courseid);
$form->feedback = 1;
$form->multiplier = array(1, 1);
$form->shuffleanswers = 1;
$form->noanswers = 1;
$form->qtype ='calculatedmulti';
$question->qtype ='calculatedmulti';
$form->answers = array('{a} + {b}');
$form->fraction = array(1);
$form->tolerance = array(0.01);
$form->tolerancetype = array(1);
$form->correctanswerlength = array(2);
$form->correctanswerformat = array(1);
$form->questiontext = "What is {a} + {b}?";
/**
* Runs all the code required to set up and save an essay question for testing purposes.
* Alternate DB table prefix may be used to facilitate data deletion.
*/
function generate_test($name, $courseid = null) {
global $DB;
list($form, $question) = parent::generate_test($name, $courseid);
$form->feedback = 1;
$form->multiplier = array(1, 1);
$form->shuffleanswers = 1;
$form->noanswers = 1;
$form->qtype ='calculatedmulti';
$question->qtype ='calculatedmulti';
$form->answers = array('{a} + {b}');
$form->fraction = array(1);
$form->tolerance = array(0.01);
$form->tolerancetype = array(1);
$form->correctanswerlength = array(2);
$form->correctanswerformat = array(1);
$form->questiontext = "What is {a} + {b}?";
if ($courseid) {
$course = $DB->get_record('course', array('id'=> $courseid));
}
if ($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->nextpageparam["forceregeneration"]= 1;
$dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
$dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
$dataset_form->calclength = array(1 => 1, 2 => 1);
$dataset_form->number = array(1 => 5.4 , 2 => 4.9);
$dataset_form->itemid = array(1 => '' , 2 => '');
$dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
$dataset_form->definition = array(1 => "1-0-a",
2 => "1-0-b");
$dataset_form->nextpageparam = array('forceregeneration' => false);
$dataset_form->addbutton = 1;
$dataset_form->selectadd = 1;
$dataset_form->courseid = $courseid;
$dataset_form->cmid = 0;
$dataset_form->id = $new_question->id;
$this->save_dataset_items($new_question, $dataset_form);
$dataset_form = new stdClass();
$dataset_form->nextpageparam["forceregeneration"]= 1;
$dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
$dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
$dataset_form->calclength = array(1 => 1, 2 => 1);
$dataset_form->number = array(1 => 5.4 , 2 => 4.9);
$dataset_form->itemid = array(1 => '' , 2 => '');
$dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
$dataset_form->definition = array(1 => "1-0-a",
2 => "1-0-b");
$dataset_form->nextpageparam = array('forceregeneration' => false);
$dataset_form->addbutton = 1;
$dataset_form->selectadd = 1;
$dataset_form->courseid = $courseid;
$dataset_form->cmid = 0;
$dataset_form->id = $new_question->id;
$this->save_dataset_items($new_question, $dataset_form);
return $new_question;
}
return $new_question;
}
/**
* When move the category of questions, the belonging files should be moved as well
* @param object $question, question information
* @param object $newcategory, target category information
*/
function move_files($question, $newcategory) {
global $DB;
// move files belonging to question component
parent::move_files($question, $newcategory);
// move files belonging to qtype_calculatedmulti
$fs = get_file_storage();
// process files in answer
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
}
$component = 'question';
$filearea = 'answerfeedback';
foreach ($oldanswers as $answer) {
$files = $fs->get_area_files($question->contextid, $component, $filearea, $answer->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
$newfile = new object();
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
$storedfile->delete();
}
}
}
$component = 'qtype_calculatedmulti';
foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $filearea) {
$files = $fs->get_area_files($question->contextid, $component, $filearea, $question->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
$newfile = new object();
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
$storedfile->delete();
}
}
}
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
$itemid = reset($args);
if (empty($question->maxgrade)) {
$question->maxgrade = $question->defaultgrade;
}
if (in_array($filearea, array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'))) {
$result = $options->feedback && ($itemid == $question->id);
if (!$result) {
return false;
}
if ($state->raw_grade >= $question->maxgrade/1.01) {
$feedbacktype = 'correctfeedback';
} else if ($state->raw_grade > 0) {
$feedbacktype = 'partiallycorrectfeedback';
} else {
$feedbacktype = 'incorrectfeedback';
}
if ($feedbacktype != $filearea) {
return false;
}
return true;
} else if ($component == 'question' && $filearea == 'answerfeedback') {
return $options->feedback && (array_key_exists($itemid, $question->options->answers));
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
}
}
//// END OF CLASS ////
//////////////////////////////////////////////////////////////////////////

View file

@ -1,4 +1,20 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines the editing form for the calculated simplequestion type.
*
@ -8,21 +24,17 @@
* @package questionbank
* @subpackage questiontypes
*/
/**
* calculatedsimple editing form definition.
*/
class question_edit_calculatedsimple_form extends question_edit_form {
/**
* Handle to the question type for this question.
*
* @var question_calculatedsimple_qtype
*/
var $qtypeobj;
public $qtypeobj;
var $wildcarddisplay ;
public $wildcarddisplay ;
var $questiondisplay ;
public $questiondisplay ;
public $datasetdefs;
@ -46,8 +58,6 @@ class question_edit_calculatedsimple_form extends question_edit_form {
public $formdata = array();
function question_edit_calculatedsimple_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
global $QTYPES, $SESSION, $CFG, $DB;
$this->regenerate = true;
@ -60,17 +70,17 @@ class question_edit_calculatedsimple_form extends question_edit_form {
//so this should be removed from here
// get priority to paramdatasets
if ( "1" == optional_param('reload','', PARAM_INT )) {
$this->reload = true ;
if ("1" == optional_param('reload','', PARAM_INT )) {
$this->reload = true;
}else {
$this->reload = false ;
$this->reload = false;
}
if(!$this->reload ){ // use database data as this is first pass
if (!$this->reload) { // use database data as this is first pass
// question->id == 0 so no stored datasets
// else get datasets
// echo "<p>question <pre>";print_r($question);echo "</pre></p>";
if ( !empty($question->id)) {
if (!empty($question->id)) {
/* if (empty($question->options)) {
$this->get_question_options($question);
}*/
@ -127,7 +137,7 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$mandatorydatasets = array();
// should not test on adding a new answer
// should test if there are already olddatasets or if the 'analyzequestion' submit button has been clicked
if ('' != optional_param('datasetdef', '', PARAM_RAW) || '' != optional_param('analyzequestion', '', PARAM_RAW)){
if ('' != optional_param('datasetdef', '', PARAM_RAW) || '' != optional_param('analyzequestion', '', PARAM_RAW)){
if ( $dummyform->answer = optional_param('answer', '', PARAM_NOTAGS)) { // there is always at least one answer...
$fraction = optional_param('fraction', '', PARAM_NUMBER);
@ -297,12 +307,12 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$addfieldsname='updatequestion value';
$addstring=get_string("updatecategory", "qtype_calculated");
$mform->registerNoSubmitButton($addfieldsname);
//put a submit button to stop supplementary answers on update answers parameters
// $mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
// put a submit button to stop supplementary answers on update answers parameters
// $mform->insertElementBefore($mform->createElement('submit', $addfieldsname, $addstring), 'listcategory');
$creategrades = get_grade_options();
$this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'),
$creategrades->gradeoptions, 1, 1);
$creategrades->gradeoptions, 1, 1);
$QTYPES['numerical']->add_units_options($mform,$this);
@ -327,7 +337,7 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$this->noofitems = 0;
}
if(!empty($this->datasetdefs)){//So there are some datadefs
// we put them on the page
// we put them on the page
$key = 0;
$mform->addElement('header', 'additemhdr', get_string('wildcardparam', 'qtype_calculatedsimple'));
$idx = 1;
@ -347,180 +357,183 @@ class question_edit_calculatedsimple_form extends question_edit_form {
}
//this should be done before the elements are created and stored as $this->formdata ;
//fill out all data sets and also the fields for the next item to add.
/*Here we do already the values error analysis so that
* we could force all wild cards values display if there is an error in values.
* as using a , in a number */
$this->numbererrors = array();
/*Here we do already the values error analysis so that
* we could force all wild cards values display if there is an error in values.
* as using a , in a number */
$this->numbererrors = array();
if(!empty($this->datasetdefs)){
$j = $this->noofitems * count($this->datasetdefs);
for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){
$data = array();
$numbererrors = array() ;
$comment = new stdClass;
$comment->stranswers = array();
$comment->outsidelimit = false ;
$comment->answers = array();
$j = $this->noofitems * count($this->datasetdefs);
for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){
$data = array();
$numbererrors = array() ;
$comment = new stdClass;
$comment->stranswers = array();
$comment->outsidelimit = false ;
$comment->answers = array();
foreach ($this->datasetdefs as $defid => $datasetdef){
if (isset($datasetdef->items[$itemnumber])){
$this->formdata["definition[$j]"] = $defid;
$this->formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id;
$data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
$this->formdata["number[$j]"] = $number = $datasetdef->items[$itemnumber]->value;
if(! is_numeric($number)){
$a = new stdClass;
$a->name = '{'.$datasetdef->name.'}' ;
$a->value = $datasetdef->items[$itemnumber]->value ;
if (stristr($number,',')){
foreach ($this->datasetdefs as $defid => $datasetdef){
if (isset($datasetdef->items[$itemnumber])){
$this->formdata["definition[$j]"] = $defid;
$this->formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id;
$data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
$this->formdata["number[$j]"] = $number = $datasetdef->items[$itemnumber]->value;
if(! is_numeric($number)){
$a = new stdClass;
$a->name = '{'.$datasetdef->name.'}' ;
$a->value = $datasetdef->items[$itemnumber]->value ;
if (stristr($number,',')){
$this->numbererrors["number[$j]"]=get_string('nocommaallowed', 'qtype_calculated');
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
}else {
}else {
$this->numbererrors["number[$j]"]= get_string('notvalidnumber','qtype_calculated',$a);
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
//$comment->outsidelimit = false ;
}
}else if( stristr($number,'x')){ // hexa will pass the test
$a = new stdClass;
$a->name = '{'.$datasetdef->name.'}' ;
$a->value = $datasetdef->items[$itemnumber]->value ;
$this->numbererrors['number['.$j.']']= get_string('hexanotallowed','qtype_calculated',$a);
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
} else if( is_nan($number)){
$a = new stdClass;
$a->name = '{'.$datasetdef->name.'}' ;
$a->value = $datasetdef->items[$itemnumber]->value ;
$this->numbererrors["number[$j]"]= get_string('notvalidnumber','qtype_calculated',$a);
$numbererrors .= $this->numbererrors['number['.$j.']']."<br />";
// $val = 1.0 ;
}
}
$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';
$this->formdata['selectadd'] = '1';
$j = $this->noofitems * count($this->datasetdefs)+1;
$data = array(); // data for comment_on_datasetitems later
if($this->noofitems != 0 ) {
if (empty($numbererrors)) {
if (!isset($this->question->id)) {
$this->question->id = 0 ;
}
$this->question->questiontext = !empty($this->question->questiontext)?$this->question->questiontext:'';
$comment = $this->qtypeobj->comment_on_datasetitems($this->qtypeobj, $this->question->id, $this->question->questiontext, $this->nonemptyanswer, $data, $itemnumber);
if ($comment->outsidelimit) {
$this->outsidelimit=$comment->outsidelimit ;
}
$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 ;
foreach ($this->datasetdefs as $defid => $datasetdef){
$this->formdata["datasetdef[$idx]"] = $defid;
$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';
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');
//------------------------------------------------------------------------------------------------------------------------------
$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('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('static','warningnowildcards','','<span class="error">'.get_string('atleastonewildcard', 'qtype_calculatedsimple').'</span>');
$mform->closeHeaderBefore('warningnowildcards');
}
$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
//hidden elements
@ -536,50 +549,64 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$mform->setDefault('cmid', 0);
if (!empty($this->question->id)){
if ($this->question->formoptions->cansaveasnew){
$mform->addElement('header', 'additemhdr', get_string('converttocalculated', 'qtype_calculatedsimple'));
$mform->closeHeaderBefore('additemhdr');
$mform->addElement('header', 'additemhdr', get_string('converttocalculated', 'qtype_calculatedsimple'));
$mform->closeHeaderBefore('additemhdr');
$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;
$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;
$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;
$key++;
}
// prepare draft files
$draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
$default_values['feedback['.$key.']']['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'question', // component
'answerfeedback', // filarea
!empty($answer->id)?(int)$answer->id:null, // itemid
$this->fileoptions, // options
!empty($answer->feedback)?$answer->feedback:'' // text
);
$default_values['feedback['.$key.']']['format'] = !empty($answer->feedbackformat)?$answer->feedbackformat:editors_get_preferred_format();
$default_values['feedback['.$key.']']['itemid'] = $draftid;
$key++;
}
$default_values['synchronize'] = 0 ;
$QTYPES['numerical']->set_numerical_unit_data($question,$default_values);
/* if (isset($question->options)){
}
$default_values['synchronize'] = 0 ;
$QTYPES['numerical']->set_numerical_unit_data($this, $question, $default_values);
/* if (isset($question->options)){
$default_values['unitgradingtype'] = $question->options->unitgradingtype ;
$default_values['unitpenalty'] = $question->options->unitpenalty ;
switch ($question->options->showunits){
case 'O' :
case '1' :
case '1' :
$default_values['showunits0'] = $question->options->showunits ;
$default_values['unitrole'] = 0 ;
break;
case '2' :
case '3' :
case '3' :
$default_values['showunits1'] = $question->options->showunits ;
$default_values['unitrole'] = 1 ;
break;
}
}
$default_values['unitsleft'] = $question->options->unitsleft ;
$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();
$fromform = new stdClass();
//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.
/* if(!empty($this->datasetdefs)){
/* if(!empty($this->datasetdefs)){
$j = $this->noofitems * count($this->datasetdefs);
for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){
for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){
$data = array();
foreach ($this->datasetdefs as $defid => $datasetdef){
if (isset($datasetdef->items[$itemnumber])){
@ -613,27 +640,27 @@ class question_edit_calculatedsimple_form extends question_edit_form {
}
$j--;
}
// 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>answers avant comment <pre>";print_r($answer);echo"</pre></p>";
// echo "<p>data avant comment <pre>";print_r($data);echo"</pre></p>";
if($this->noofitems != 0 ) {
if(!isset($question->id)) $question->id = 0 ;
$comment = $this->qtypeobj->comment_on_datasetitems($question->id,$this->nonemptyanswer, $data, $itemnumber);//$this->
if ($comment->outsidelimit) {
$this->outsidelimit=$comment->outsidelimit ;
}
$totalcomment='';
// echo "<p> comment <pre>";print_r($comment);echo"</pre></p>";
$comment = $this->qtypeobj->comment_on_datasetitems($question->id,$this->nonemptyanswer, $data, $itemnumber);//$this->
if ($comment->outsidelimit) {
$this->outsidelimit=$comment->outsidelimit ;
}
$totalcomment='';
// echo "<p> comment <pre>";print_r($comment);echo"</pre></p>";
foreach ($this->nonemptyanswer as $key => $answer) {
$totalcomment .= $comment->stranswers[$key].'<br/>';
}
foreach ($this->nonemptyanswer as $key => $answer) {
$totalcomment .= $comment->stranswers[$key].'<br/>';
}
$formdata['answercomment['.$itemnumber.']'] = $totalcomment ;
$formdata['answercomment['.$itemnumber.']'] = $totalcomment ;
}
}
}
// $formdata['reload'] = '1';
// $formdata['nextpageparam[forceregeneration]'] = $this->regenerate;
// $formdata['reload'] = '1';
// $formdata['nextpageparam[forceregeneration]'] = $this->regenerate;
$formdata['selectdelete'] = '1';
$formdata['selectadd'] = '1';
$j = $this->noofitems * count($this->datasetdefs)+1;
@ -644,10 +671,10 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$idx++;
}
$formdata = $this->qtypeobj->custom_generator_set_data($this->datasetdefs, $formdata);
}*/
}*/
$question = (object)((array)$question + $default_values+$this->formdata );
parent::set_data($question);
return $question;
}
function qtype() {
@ -659,8 +686,8 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$errors = parent::validation($data, $files);
//verifying for errors in {=...} in question text;
$qtext = "";
$qtextremaining = $data['questiontext'] ;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
$qtextremaining = $data['questiontext']['text'];
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
}
@ -679,7 +706,7 @@ class question_edit_calculatedsimple_form extends question_edit_form {
$answers = $data['answer'];
$answercount = 0;
$maxgrade = false;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']['text']);
$mandatorydatasets = array();
foreach ($answers as $key => $answer){
$mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
@ -797,4 +824,3 @@ class question_edit_calculatedsimple_form extends question_edit_form {
return $errors;
}
}

View file

@ -0,0 +1,31 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Serve question type files
*
* @since 2.0
* @package questionbank
* @subpackage questiontypes
* @author Dongsheng Cai <dongsheng@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function qtype_calculatedsimple_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_calculatedsimple', $filearea, $args, $forcedownload);
}

View file

@ -1,30 +1,41 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/////////////////
// CALCULATED ///
/////////////////
/// QUESTION TYPE CLASS //////////////////
class question_calculatedsimple_qtype extends question_calculated_qtype {
// Used by the function custom_generator_tools:
var $calcgenerateidhasbeenadded = false;
public $calcgenerateidhasbeenadded = false;
public $virtualqtype = false;
function name() {
return 'calculatedsimple';
}
function save_question_options($question) {
global $CFG, $DB , $QTYPES;
$context = $question->context;
//$options = $question->subtypeoptions;
// Get old answers:
global $CFG, $DB , $QTYPES;
if (isset($question->answer) && !isset($question->answers)) {
$question->answers = $question->answer;
@ -52,18 +63,22 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
$question->answers=$question->answer;
}
foreach ($question->answers as $key => $dataanswer) {
if ( trim($dataanswer) != '' ) {
if ( trim($dataanswer) != '' ) {
$answer = new stdClass;
$answer->question = $question->id;
$answer->answer = trim($dataanswer);
$answer->fraction = $question->fraction[$key];
$answer->feedback = trim($question->feedback[$key]);
$answer->feedbackformat = $question->feedback[$key]['format'];
if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
$answer->id = $oldanswer->id;
$answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback[$key]['text']));
$DB->update_record("question_answers", $answer);
} else { // This is a completely new answer
$answer->feedback = '';
$answer->id = $DB->insert_record("question_answers", $answer);
$answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback[$key]['text']));
$DB->set_field('question_answers', 'feedback', $answer->feedback, array('id'=>$answer->id));
}
// Set up the options object
@ -99,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);
}else {
} else {
//save datasets and datatitems from form i.e in question
// $datasetdefs = $this->get_dataset_definitions($question->id, array());
$question->dataset = $question->datasetdef ;
// $this->save_dataset_definitions($question);
// Save datasets
$datasetdefinitions = $this->get_dataset_definitions($question->id, $question->dataset);
$tmpdatasets = array_flip($question->dataset);
$defids = array_keys($datasetdefinitions);
$datasetdefs = array();
foreach ($defids as $defid) {
$datasetdef = &$datasetdefinitions[$defid];
if (isset($datasetdef->id)) {
if (!isset($tmpdatasets[$defid])) {
// This dataset is not used any more, delete it
$DB->delete_records('question_datasets', array('question' => $question->id, 'datasetdefinition' => $datasetdef->id));
// if ($datasetdef->category == 0) { // Question local dataset
// $datasetdefs = $this->get_dataset_definitions($question->id, array());
$question->dataset = $question->datasetdef ;
// $this->save_dataset_definitions($question);
// Save datasets
$datasetdefinitions = $this->get_dataset_definitions($question->id, $question->dataset);
$tmpdatasets = array_flip($question->dataset);
$defids = array_keys($datasetdefinitions);
$datasetdefs = array();
foreach ($defids as $defid) {
$datasetdef = &$datasetdefinitions[$defid];
if (isset($datasetdef->id)) {
if (!isset($tmpdatasets[$defid])) {
// This dataset is not used any more, delete it
$DB->delete_records('question_datasets', array('question' => $question->id, 'datasetdefinition' => $datasetdef->id));
// if ($datasetdef->category == 0) { // Question local dataset
$DB->delete_records('question_dataset_definitions', array('id' => $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]);
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]);
}
// Remove local obsolete datasets as well as relations
// to datasets in other categories:
if (!empty($datasetdefinitions)) {
foreach ($datasetdefinitions as $def) {
$DB->delete_records('question_datasets', array('question' => $question->id, 'datasetdefinition' => $def->id));
if ($def->category == 0) { // Question local dataset
$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) {
$DB->delete_records('question_datasets', array('question' => $question->id, 'datasetdefinition' => $def->id));
if ($def->category == 0) { // Question local dataset
$DB->delete_records('question_dataset_definitions', array('id' => $def->id));
$DB->delete_records('question_dataset_items', array('definition' => $def->id));
}
}
}
$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++;
}
$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.
//convert to calculated
if(!empty($question->makecopy) && !empty($question->convert)) {
@ -190,39 +205,34 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
}
return true;
}
function finished_edit_wizard(&$form) {
function finished_edit_wizard(&$form) {
return true ; //isset($form->backtoquiz);
}
/**
* this version save the available data at the different steps of the question editing process
* without using global $SESSION as storage between steps
* at the first step $wizardnow = 'question'
* when creating a new question
* when modifying a question
* when copying as a new question
* the general parameters and answers are saved using parent::save_question
* then the datasets are prepared and saved
* at the second step $wizardnow = 'datasetdefinitions'
* the datadefs final type are defined as private, category or not a datadef
* at the third step $wizardnow = 'datasetitems'
* the datadefs parameters and the data items are created or defined
*
* @param object question
* @param object $form
* @param int $course
* @param PARAM_ALPHA $wizardnow should be added as we are coming from question2.php
*/
* this version save the available data at the different steps of the question editing process
* without using global $SESSION as storage between steps
* at the first step $wizardnow = 'question'
* when creating a new question
* when modifying a question
* when copying as a new question
* the general parameters and answers are saved using parent::save_question
* then the datasets are prepared and saved
* at the second step $wizardnow = 'datasetdefinitions'
* the datadefs final type are defined as private, category or not a datadef
* at the third step $wizardnow = 'datasetitems'
* the datadefs parameters and the data items are created or defined
*
* @param object question
* @param object $form
* @param int $course
* @param PARAM_ALPHA $wizardnow should be added as we are coming from question2.php
*/
function save_question($question, $form, $course) {
$question = default_questiontype::save_question($question, $form, $course);
return $question;
}
function custom_generator_tools_part(&$mform, $idx, $j){
$minmaxgrp = array();
@ -247,14 +257,14 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
$strheader = "";
$delimiter = '';
// $answers = $question->options->answers;
// $answers = $question->options->answers;
foreach ($answers as $key => $answer) {
/* if (is_string($answer)) {
$strheader .= $delimiter.$answer;
} else {*/
$strheader .= $delimiter.$answer->answer;
// }
} else {*/
$strheader .= $delimiter.$answer->answer;
// }
$delimiter = '<br/><br/><br/>';
}
return $strheader;
@ -262,18 +272,18 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
function tolerance_types() {
return array('1' => get_string('relative', 'quiz'),
'2' => get_string('nominal', 'quiz'),
// '3' => get_string('geometric', 'quiz')
);
'2' => get_string('nominal', 'quiz'),
// '3' => get_string('geometric', 'quiz')
);
}
function dataset_options($form, $name, $mandatory=true,$renameabledatasets=false) {
// Takes datasets from the parent implementation but
// filters options that are currently not accepted by calculated
// It also determines a default selection...
//$renameabledatasets not implemented anmywhere
// Takes datasets from the parent implementation but
// filters options that are currently not accepted by calculated
// It also determines a default selection...
//$renameabledatasets not implemented anmywhere
list($options, $selected) = $this->dataset_options_from_database($form, $name,'','qtype_calculated');
// list($options, $selected) = $this->dataset_optionsa($form, $name);
// list($options, $selected) = $this->dataset_optionsa($form, $name);
foreach ($options as $key => $whatever) {
if (!preg_match('~^1-~', $key) && $key != '0') {
@ -282,7 +292,7 @@ class question_calculatedsimple_qtype extends question_calculated_qtype {
}
if (!$selected) {
if ($mandatory){
$selected = "1-0-$name"; // Default
$selected = "1-0-$name"; // Default
}else {
$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.
* Alternate DB table prefix may be used to facilitate data deletion.
*/
function generate_test($name, $courseid = null) {
global $DB;
list($form, $question) = parent::generate_test($name, $courseid);
$form->feedback = 1;
$form->multiplier = array(1, 1);
$form->shuffleanswers = 1;
$form->noanswers = 1;
$form->qtype ='calculatedsimple';
$question->qtype ='calculatedsimple';
$form->answers = array('{a} + {b}');
$form->fraction = array(1);
$form->tolerance = array(0.01);
$form->tolerancetype = array(1);
$form->correctanswerlength = array(2);
$form->correctanswerformat = array(1);
$form->questiontext = "What is {a} + {b}?";
/**
* Runs all the code required to set up and save an essay question for testing purposes.
* Alternate DB table prefix may be used to facilitate data deletion.
*/
function generate_test($name, $courseid = null) {
global $DB;
list($form, $question) = parent::generate_test($name, $courseid);
$form->feedback = 1;
$form->multiplier = array(1, 1);
$form->shuffleanswers = 1;
$form->noanswers = 1;
$form->qtype ='calculatedsimple';
$question->qtype ='calculatedsimple';
$form->answers = array('{a} + {b}');
$form->fraction = array(1);
$form->tolerance = array(0.01);
$form->tolerancetype = array(1);
$form->correctanswerlength = array(2);
$form->correctanswerformat = array(1);
$form->questiontext = "What is {a} + {b}?";
if ($courseid) {
$course = $DB->get_record('course', array('id'=> $courseid));
}
if ($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->nextpageparam["forceregeneration"]= 1;
$dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
$dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
$dataset_form->calclength = array(1 => 1, 2 => 1);
$dataset_form->number = array(1 => 5.4 , 2 => 4.9);
$dataset_form->itemid = array(1 => '' , 2 => '');
$dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
$dataset_form->definition = array(1 => "1-0-a",
2 => "1-0-b");
$dataset_form->nextpageparam = array('forceregeneration' => false);
$dataset_form->addbutton = 1;
$dataset_form->selectadd = 1;
$dataset_form->courseid = $courseid;
$dataset_form->cmid = 0;
$dataset_form->id = $new_question->id;
$this->save_dataset_items($new_question, $dataset_form);
$dataset_form = new stdClass();
$dataset_form->nextpageparam["forceregeneration"]= 1;
$dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
$dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
$dataset_form->calclength = array(1 => 1, 2 => 1);
$dataset_form->number = array(1 => 5.4 , 2 => 4.9);
$dataset_form->itemid = array(1 => '' , 2 => '');
$dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
$dataset_form->definition = array(1 => "1-0-a",
2 => "1-0-b");
$dataset_form->nextpageparam = array('forceregeneration' => false);
$dataset_form->addbutton = 1;
$dataset_form->selectadd = 1;
$dataset_form->courseid = $courseid;
$dataset_form->cmid = 0;
$dataset_form->id = $new_question->id;
$this->save_dataset_items($new_question, $dataset_form);
return $new_question;
}
return $new_question;
}
/**
* When move the category of questions, the belonging files should be moved as well
* @param object $question, question information
* @param object $newcategory, target category information
*/
function move_files($question, $newcategory) {
global $DB;
parent::move_files($question, $newcategory);
$fs = get_file_storage();
// process files in answer
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
}
$component = 'question';
$filearea = 'feedback';
foreach ($oldanswers as $answer) {
$files = $fs->get_area_files($question->contextid, $component, $filearea, $answer->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
$newfile = new object();
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
$storedfile->delete();
}
}
}
$component = 'qtype_calculatedsimple';
$filearea = 'feedback';
$filearea = 'instruction';
$files = $fs->get_area_files($question->contextid, $component, $filearea, $question->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
$newfile = new object();
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
$storedfile->delete();
}
}
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
$itemid = reset($args);
if ($component == 'question' && $filearea == 'answerfeedback') {
// check if answer id exists
$result = $options->feedback && array_key_exists($itemid, $question->options->answers);
if (!$result) {
return false;
}
// check response
if (!$this->check_response($question, $state)) {
return false;
}
return true;
} else if ($filearea == 'instruction') {
// TODO: should it be display all the time like questiontext?
// check if question id exists
if ($itemid != $question->id) {
return false;
} else {
return true;
}
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
return true;
}
}
//// END OF CLASS ////
@ -349,5 +429,3 @@ question_register_questiontype(new question_calculatedsimple_qtype());
if ( ! defined ("CALCULATEDSIMPLE")) {
define("CALCULATEDSIMPLE", "calculatedsimple");
}

View file

@ -1,4 +1,20 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines the editing form for the description question type.
*

View file

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

View file

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

View file

@ -1,4 +1,20 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* A base class for question editing forms.
*
@ -7,7 +23,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank
* @subpackage questiontypes
*//** */
*/
/**
* Form definition base class. This defines the common fields that
@ -27,24 +43,44 @@ class question_edit_form extends moodleform {
* @var object
*/
public $question;
public $contexts;
public $category;
public $categorycontext;
public $coursefilesid;
/** @var object current context */
public $context;
/** @var array html editor options */
public $editoroptions;
/** @var array options to preapre draft area */
public $fileoptions;
/** @var object instance of question type */
public $instance;
function question_edit_form($submiturl, $question, $category, $contexts, $formeditable = true){
global $DB;
$this->question = $question;
$this->contexts = $contexts;
$record = $DB->get_record('question_categories', array('id'=>$question->category), 'contextid');
$this->context = get_context_instance_by_id($record->contextid);
$this->editoroptions = array('maxfiles' => EDITOR_UNLIMITED_FILES, 'context'=>$this->context);
$this->fileoptions = array('subdir'=>true, 'maxfiles'=>-1, 'maxbytes'=>-1);
$this->category = $category;
$this->categorycontext = get_context_instance_by_id($category->contextid);
//** *
//course id or site id depending on question cat context
$this->coursefilesid = get_filesdir_from_context(get_context_instance_by_id($category->contextid));
if (!empty($question->id)) {
$question->id = (int)$question->id;
//$this->instance = new
}
parent::moodleform($submiturl, null, 'post', '', null, $formeditable);
}
@ -106,26 +142,9 @@ class question_edit_form extends moodleform {
$mform->setType('name', PARAM_TEXT);
$mform->addRule('name', null, 'required', null, 'client');
//TODO: MDL-16094 convert to new editor element
$mform->addElement('htmleditor', 'questiontext', get_string('questiontext', 'quiz'),
array('rows' => 15, 'course' => $this->coursefilesid));
$mform->addElement('editor', 'questiontext', get_string('questiontext', 'quiz'),
array('rows' => 15, 'course' => $this->coursefilesid), $this->editoroptions);
$mform->setType('questiontext', PARAM_RAW);
//$mform->addElement('format', 'questiontextformat', get_string('format'));
$mform->addElement('hidden', 'questiontextformat');
$mform->setType('questiontextformat', PARAM_INT);
make_upload_directory($this->coursefilesid); // Just in case
$coursefiles = get_directory_list("$CFG->dataroot/$this->coursefilesid", $CFG->moddata);
foreach ($coursefiles as $filename) {
if (mimeinfo("icon", $filename) == "image.gif") {
$images["$filename"] = $filename;
}
}
if (empty($images)) {
$mform->addElement('static', 'image', get_string('imagedisplay', 'quiz'), get_string('noimagesyet'));
} else {
$mform->addElement('select', 'image', get_string('imagedisplay', 'quiz'), array_merge(array(''=>get_string('none')), $images));
}
$mform->addElement('text', 'defaultgrade', get_string('defaultgrade', 'quiz'),
array('size' => 3));
@ -140,8 +159,8 @@ class question_edit_form extends moodleform {
$mform->addHelpButton('penalty', 'penaltyfactor', 'question');
$mform->setDefault('penalty', 0.1);
$mform->addElement('htmleditor', 'generalfeedback', get_string('generalfeedback', 'quiz'),
array('rows' => 10, 'course' => $this->coursefilesid));
$mform->addElement('editor', 'generalfeedback', get_string('generalfeedback', 'quiz'),
array('rows' => 10, 'course' => $this->coursefilesid), $this->editoroptions);
$mform->setType('generalfeedback', PARAM_RAW);
$mform->addHelpButton('generalfeedback', 'generalfeedback', 'quiz');
@ -235,7 +254,7 @@ class question_edit_form extends moodleform {
$errors= parent::validation($fromform, $files);
if (empty($fromform->makecopy) && isset($this->question->id)
&& ($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');
}
return $errors;
@ -264,8 +283,8 @@ class question_edit_form extends moodleform {
$repeated[] =& $mform->createElement('header', 'answerhdr', $label);
$repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50));
$repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions);
$repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'),
array('course' => $this->coursefilesid));
$repeated[] =& $mform->createElement('editor', 'feedback', get_string('feedback', 'quiz'),
array('course' => $this->coursefilesid), $this->editoroptions);
$repeatedoptions['answer']['type'] = PARAM_RAW;
$repeatedoptions['fraction']['default'] = 0;
$answersoption = 'answers';
@ -302,9 +321,33 @@ class question_edit_form extends moodleform {
function set_data($question) {
global $QTYPES;
if (empty($question->image)){
unset($question->image);
// prepare question text
$draftid = file_get_submitted_draft_itemid('questiontext');
if (!empty($question->questiontext)) {
$questiontext = $question->questiontext;
} else {
$questiontext = '';
}
$questiontext = file_prepare_draft_area($draftid, $this->context->id, 'question', 'questiontext', empty($question->id)?null:(int)$question->id, null, $questiontext);
$question->questiontext = array();
$question->questiontext['text'] = $questiontext;
$question->questiontext['format'] = empty($question->questiontextformat) ? editors_get_preferred_format() : $question->questiontextformat;
$question->questiontext['itemid'] = $draftid;
// prepare general feedback
$draftid = file_get_submitted_draft_itemid('generalfeedback');
if (empty($question->generalfeedback)) {
$question->generalfeedback = '';
}
$feedback = file_prepare_draft_area($draftid, $this->context->id, 'question', 'generalfeedback', empty($question->id)?null:(int)$question->id, null, $question->generalfeedback);
$question->generalfeedback = array();
$question->generalfeedback['text'] = $feedback;
$question->generalfeedback['format'] = empty($question->generalfeedbackformat) ? editors_get_preferred_format() : $question->generalfeedbackformat;
$question->generalfeedback['itemid'] = $draftid;
// Remove unnecessary trailing 0s form grade fields.
if (isset($question->defaultgrade)) {
@ -324,10 +367,20 @@ class question_edit_form extends moodleform {
}
}
}
// subclass adds data_preprocessing code here
$question = $this->data_preprocessing($question);
parent::set_data($question);
}
/**
* Any preprocessing needed for the settings form for the question type
*
* @param array $question - array to fill in with the default values
*/
function data_preprocessing($question) {
return $question;
}
/**
* Override this in the subclass to question type name.
* @return the question type name, should be the same as the name() method in the question type class.
@ -336,5 +389,3 @@ class question_edit_form extends moodleform {
return '';
}
}

View file

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

View file

@ -1,4 +1,20 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines the editing form for the essay question type.
*
@ -19,8 +35,7 @@ class question_edit_essay_form extends question_edit_form {
* @param MoodleQuickForm $mform the form being built.
*/
function definition_inner(&$mform) {
$mform->addElement('htmleditor', 'feedback', get_string("feedback", "quiz"),
array('course' => $this->coursefilesid));
$mform->addElement('editor', 'feedback', get_string('feedback', 'quiz'), null, $this->editoroptions);
$mform->setType('feedback', PARAM_RAW);
$mform->addElement('hidden', 'fraction', 0);
@ -32,17 +47,29 @@ class question_edit_essay_form extends question_edit_form {
$mform->setType('penalty', PARAM_RAW);
}
function set_data($question) {
function data_preprocessing($question) {
if (!empty($question->options) && !empty($question->options->answers)) {
$answer = reset($question->options->answers);
$question->feedback = $answer->feedback;
$question->feedback = array();
$draftid = file_get_submitted_draft_itemid('feedback');
$question->feedback['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'question', // component
'answerfeedback', // filarea
!empty($answer->id)?(int)$answer->id:null, // itemid
$this->fileoptions, // options
$answer->feedback // text
);
$question->feedback['text'] = $answer->feedback;
$question->feedback['format'] = $answer->feedbackformat;
$question->feedback['itemid'] = $draftid;
}
$question->penalty = 0;
parent::set_data($question);
return $question;
}
function qtype() {
return 'essay';
}
}

View file

@ -0,0 +1,31 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Serve question type files
*
* @since 2.0
* @package questionbank
* @subpackage questiontypes
* @author Dongsheng Cai <dongsheng@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function qtype_essay_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_essay', $filearea, $args, $forcedownload);
}

View file

@ -1,5 +1,20 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
//////////////////
/// ESSAY ///
/////////////////
@ -21,6 +36,7 @@ class question_essay_qtype extends default_questiontype {
function save_question_options($question) {
global $DB;
$context = $question->context;
$result = true;
$update = true;
$answer = $DB->get_record("question_answers", array("question" => $question->id));
@ -29,13 +45,20 @@ class question_essay_qtype extends default_questiontype {
$answer->question = $question->id;
$update = false;
}
$answer->answer = $question->feedback;
$answer->feedback = $question->feedback;
$answer->feedbackformat = $question->feedback['format'];
$answer->answerformat = $question->feedback['format'];
$answer->fraction = $question->fraction;
if ($update) {
$answer->feedback = file_save_draft_area_files($question->feedback['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback['text']));
$answer->answer = $answer->feedback;
$DB->update_record("question_answers", $answer);
} else {
$answer->id = $DB->insert_record("question_answers", $answer);
$answer->feedback = $question->feedback['text'];
$answer->answer = $answer->feedback;
$answer->id = $DB->insert_record('question_answers', $answer);
$answer->feedback = file_save_draft_area_files($question->feedback['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, trim($question->feedback['text']));
$answer->answer = $answer->feedback;
$DB->update_record('question_answers', $answer);
}
return $result;
}
@ -43,12 +66,14 @@ class question_essay_qtype extends default_questiontype {
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG;
$answers = &$question->options->answers;
$readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
$context = $this->get_context_by_category_id($question->category);
$answers = &$question->options->answers;
$readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
// Only use the rich text editor for the first essay question on a page.
$formatoptions = new stdClass;
$formatoptions = new stdClass;
$formatoptions->noclean = true;
$formatoptions->para = false;
@ -60,13 +85,12 @@ class question_essay_qtype extends default_questiontype {
$question->questiontextformat,
$formatoptions, $cmoptions->course);
$image = get_question_image($question);
// feedback handling
$feedback = '';
if ($options->feedback && !empty($answers)) {
foreach ($answers as $answer) {
$feedback = format_text($answer->feedback, '', $formatoptions, $cmoptions->course);
$feedback = quiz_rewrite_question_urls($answer->feedback, 'pluginfile.php', $context->id, 'qtype_essay', 'feedback', array($state->attempt, $state->question), $answer->id);
$feedback = format_text($feedback, $answer->feedbackformat, $formatoptions, $cmoptions->course);
}
}
@ -139,6 +163,54 @@ class question_essay_qtype extends default_questiontype {
return $this->save_question($question, $form, $course);
}
/**
* When move the category of questions, the belonging files should be moved as well
* @param object $question, question information
* @param object $newcategory, target category information
*/
function move_files($question, $newcategory) {
global $DB;
parent::move_files($question, $newcategory);
$fs = get_file_storage();
// process files in answer
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
}
$component = 'question';
$filearea = 'answerfeedback';
foreach ($oldanswers as $answer) {
$files = $fs->get_area_files($question->contextid, $component, $filearea, $answer->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
$newfile = new object();
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
$storedfile->delete();
}
}
}
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
if ($component == 'question' && $filearea == 'answerfeedback') {
$answerid = reset($args); // itemid is answer id.
$answers = &$question->options->answers;
if (isset($state->responses[''])) {
$response = $state->responses[''];
} else {
$response = '';
}
return $options->feedback && !empty($response);
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
}
// Restore method not needed.
}
//// END OF CLASS ////
@ -147,4 +219,3 @@ class question_essay_qtype extends default_questiontype {
//// INITIATION - Without this line the question type is not in use... ///
//////////////////////////////////////////////////////////////////////////
question_register_questiontype(new question_essay_qtype());

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="question/type/match/db" VERSION="20060812" COMMENT="XMLDB file for Moodle question/type/match">
<XMLDB PATH="question/type/match/db" VERSION="20100721" COMMENT="XMLDB file for Moodle question/type/match"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="question_match" COMMENT="Defines fixed matching questions" NEXT="question_match_sub">
<FIELDS>
@ -18,8 +21,9 @@
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="code"/>
<FIELD NAME="code" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Should this point to parent question_match-&amp;gt;id ?" PREVIOUS="id" NEXT="question"/>
<FIELD NAME="question" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="code" NEXT="questiontext"/>
<FIELD NAME="questiontext" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="question" NEXT="answertext"/>
<FIELD NAME="answertext" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="questiontext"/>
<FIELD NAME="questiontext" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="question" NEXT="questiontextformat"/>
<FIELD NAME="questiontextformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="questiontext" NEXT="answertext"/>
<FIELD NAME="answertext" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="questiontextformat"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/>
@ -27,4 +31,4 @@
</KEYS>
</TABLE>
</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; ?>
</div>
<?php if ($image) { ?>
<img class="qimage" src="<?php echo $image; ?>" alt="" />
<?php } ?>
<div class="ablock clearfix">
<table class="answer">

View file

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

View file

@ -0,0 +1,31 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Serve question type files
*
* @since 2.0
* @package questionbank
* @subpackage questiontypes
* @author Dongsheng Cai <dongsheng@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function qtype_match_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $DB, $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_match', $filearea, $args, $forcedownload);
}

View file

@ -1,5 +1,20 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/////////////
/// MATCH ///
/////////////
@ -24,6 +39,7 @@ class question_match_qtype extends default_questiontype {
function save_question_options($question) {
global $DB;
$context = $question->context;
$result = new stdClass;
if (!$oldsubquestions = $DB->get_records("question_match_sub", array("question" => $question->id), "id ASC")) {
@ -35,24 +51,30 @@ class question_match_qtype extends default_questiontype {
// Insert all the new question+answer pairs
foreach ($question->subquestions as $key => $questiontext) {
$questiontext = trim($questiontext);
$itemid = $questiontext['itemid'];
$format = $questiontext['format'];
$questiontext = trim($questiontext['text']);
$answertext = trim($question->subanswers[$key]);
if ($questiontext != '' || $answertext != '') {
if ($subquestion = array_shift($oldsubquestions)) { // Existing answer, so reuse it
$subquestion->questiontext = $questiontext;
$subquestion->answertext = $answertext;
$subquestion->questiontext = file_save_draft_area_files($itemid, $context->id, 'qtype_match', 'subquestion', $subquestion->id, self::$fileoptions, $questiontext);
$subquestion->questiontextformat = $format;
$DB->update_record("question_match_sub", $subquestion);
} else {
$subquestion = new stdClass;
// 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))) {
$subquestion->code = rand();
}
$subquestion->question = $question->id;
$subquestion->questiontext = $questiontext;
$subquestion->answertext = $answertext;
$subquestion->questiontextformat = $format;
$subquestion->answertext = $answertext;
$subquestion->id = $DB->insert_record("question_match_sub", $subquestion);
$questiontext = file_save_draft_area_files($itemid, $context->id, 'qtype_match', 'subquestion', $subquestion->id, self::$fileoptions, $questiontext);
$DB->set_field('question_match_sub', 'questiontext', $questiontext, array('id'=>$subquestion->id));
}
$subquestions[] = $subquestion->id;
}
@ -228,6 +250,7 @@ class question_match_qtype extends default_questiontype {
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG, $OUTPUT;
$context = $this->get_context_by_category_id($question->category);
$subquestions = $state->options->subquestions;
$correctanswers = $this->get_correct_responses($question, $state);
$nameprefix = $question->name_prefix;
@ -263,15 +286,14 @@ class question_match_qtype extends default_questiontype {
// Print formulation
$questiontext = $this->format_text($question->questiontext,
$question->questiontextformat, $cmoptions);
$image = get_question_image($question);
// Print the input controls
foreach ($subquestions as $key => $subquestion) {
if ($subquestion->questiontext !== '' && !is_null($subquestion->questiontext)) {
// Subquestion text:
$a = new stdClass;
$a->text = $this->format_text($subquestion->questiontext,
$question->questiontextformat, $cmoptions);
$text = quiz_rewrite_question_urls($subquestion->questiontext, 'pluginfile.php', $context->id, 'qtype_match', 'subquestion', array($state->attempt, $state->question), $subquestion->id);
$a->text = $this->format_text($text, $subquestion->questiontextformat, $cmoptions);
// Drop-down list:
$menuname = $nameprefix.$subquestion->id;
@ -757,6 +779,53 @@ class question_match_qtype extends default_questiontype {
return $this->save_question($question, $form, $course);
}
function move_files($question, $newcategory) {
global $DB;
// move files belonging to question component
parent::move_files($question, $newcategory);
// move files belonging to qtype_multichoice
$fs = get_file_storage();
// process files in answer
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
}
// process files in sub questions
if (!$subquestions = $DB->get_records('question_match_sub', array('question' => $question->id), 'id ASC')) {
$subquestions = array();
}
$component = 'qtype_match';
$filearea = 'subquestion';
foreach ($subquestions as $sub) {
$files = $fs->get_area_files($question->contextid, $component, $filearea, $sub->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
$newfile = new object();
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
$storedfile->delete();
}
}
}
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
$itemid = reset($args);
if ($filearea == 'subquestion') {
// always display quetion images
// itemid is sub question id
if ($itemid != $question->id) {
return false;
}
return true;
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
}
}
//// END OF CLASS ////
@ -764,4 +833,3 @@ class question_match_qtype extends default_questiontype {
//// INITIATION - Without this line the question type is not in use... ///
//////////////////////////////////////////////////////////////////////////
question_register_questiontype(new question_match_qtype());

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="question/type/multichoice/db" VERSION="20070413" COMMENT="XMLDB file for Moodle question/type/multichoice"
<XMLDB PATH="question/type/multichoice/db" VERSION="20100716" COMMENT="XMLDB file for Moodle question/type/multichoice"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
@ -12,10 +12,13 @@
<FIELD NAME="answers" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="Redundant. Comma-separated list of question_answer ids. SELECT id FROM question_answers WHERE question = ? ORDER BY id." PREVIOUS="layout" NEXT="single"/>
<FIELD NAME="single" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="If 0 it multiple response (checkboxes). Otherwise it is radio buttons." PREVIOUS="answers" NEXT="shuffleanswers"/>
<FIELD NAME="shuffleanswers" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="1" SEQUENCE="false" COMMENT="Whether the choices can be randomly shuffled." PREVIOUS="single" NEXT="correctfeedback"/>
<FIELD NAME="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any correct response." PREVIOUS="shuffleanswers" NEXT="partiallycorrectfeedback"/>
<FIELD NAME="partiallycorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any partially correct response." PREVIOUS="correctfeedback" NEXT="incorrectfeedback"/>
<FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="partiallycorrectfeedback" NEXT="answernumbering"/>
<FIELD NAME="answernumbering" TYPE="char" LENGTH="10" NOTNULL="true" DEFAULT="abc" SEQUENCE="false" COMMENT="Indicates how and whether the choices should be numbered." PREVIOUS="incorrectfeedback"/>
<FIELD NAME="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any correct response." PREVIOUS="shuffleanswers" NEXT="correctfeedbackformat"/>
<FIELD NAME="correctfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="correctfeedback" NEXT="partiallycorrectfeedback"/>
<FIELD NAME="partiallycorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any partially correct response." PREVIOUS="correctfeedbackformat" NEXT="partiallycorrectfeedbackformat"/>
<FIELD NAME="partiallycorrectfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="partiallycorrectfeedback" NEXT="incorrectfeedback"/>
<FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="partiallycorrectfeedbackformat" NEXT="incorrectfeedbackformat"/>
<FIELD NAME="incorrectfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="incorrectfeedback" NEXT="answernumbering"/>
<FIELD NAME="answernumbering" TYPE="char" LENGTH="10" NOTNULL="true" DEFAULT="abc" SEQUENCE="false" COMMENT="Indicates how and whether the choices should be numbered." PREVIOUS="incorrectfeedbackformat"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/>
@ -23,4 +26,4 @@
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
</XMLDB>

View file

@ -52,6 +52,61 @@ function xmldb_qtype_multichoice_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2008021800, 'qtype', 'multichoice');
}
if ($oldversion < 2009021801) {
/// Define field correctfeedbackformat to be added to question_multichoice
$table = new xmldb_table('question_multichoice');
$field = new xmldb_field('correctfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'correctfeedback');
/// Conditionally launch add field correctfeedbackformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define field partiallycorrectfeedbackformat to be added to question_multichoice
$field = new xmldb_field('partiallycorrectfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'partiallycorrectfeedback');
/// Conditionally launch add field partiallycorrectfeedbackformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
/// Define field incorrectfeedbackformat to be added to question_multichoice
$field = new xmldb_field('incorrectfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'incorrectfeedback');
/// Conditionally launch add field incorrectfeedbackformat
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
$rs = $DB->get_recordset('question_multichoice');
foreach ($rs as $record) {
if ($CFG->texteditors !== 'textarea') {
if (!empty($record->correctfeedback)) {
$record->correctfeedback = text_to_html($record->correctfeedback);
}
$record->correctfeedbackformat = FORMAT_HTML;
if (!empty($record->partiallycorrectfeedback)) {
$record->partiallycorrectfeedback = text_to_html($record->partiallycorrectfeedback);
}
$record->partiallycorrectfeedbackformat = FORMAT_HTML;
if (!empty($record->incorrectfeedback)) {
$record->incorrectfeedback = text_to_html($record->incorrectfeedback);
}
$record->incorrectfeedbackformat = FORMAT_HTML;
} else {
$record->correctfeedbackformat = FORMAT_MOODLE;
$record->partiallycorrectfeedbackformat = FORMAT_MOODLE;
$record->incorrectfeedbackformat = FORMAT_MOODLE;
}
$DB->update_record('question_multichoice', $record);
}
$rs->close();
/// multichoice savepoint reached
upgrade_plugin_savepoint(true, 2009021801, 'qtype', 'multichoice');
}
return true;
}

View file

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

View file

@ -1,4 +1,22 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* Defines the editing form for the multichoice question type.
*
@ -47,14 +65,14 @@ class question_edit_multichoice_form extends question_edit_form {
$mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice'));
foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
$mform->addElement('htmleditor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'),
array('course' => $this->coursefilesid));
$mform->addElement('editor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'),
array('course' => $this->coursefilesid), $this->editoroptions);
$mform->setType($feedbackname, PARAM_RAW);
}
}
function set_data($question) {
function data_preprocessing($question) {
if (isset($question->options)){
$answers = $question->options->answers;
if (count($answers)) {
@ -62,19 +80,44 @@ class question_edit_multichoice_form extends question_edit_form {
foreach ($answers as $answer){
$default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction;
$default_values['feedback['.$key.']'] = $answer->feedback;
// prepare question text
$draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
$default_values['feedback['.$key.']'] = array();
$default_values['feedback['.$key.']']['text'] = file_prepare_draft_area($draftid, $this->context->id, 'question', 'answerfeedback', empty($answer->id)?null:(int)$answer->id, null, $answer->feedback);
$default_values['feedback['.$key.']']['format'] = $answer->feedbackformat;
$default_values['feedback['.$key.']']['itemid'] = $draftid;
$key++;
}
}
$default_values['single'] = $question->options->single;
$default_values['answernumbering'] = $question->options->answernumbering;
$default_values['shuffleanswers'] = $question->options->shuffleanswers;
$default_values['correctfeedback'] = $question->options->correctfeedback;
$default_values['partiallycorrectfeedback'] = $question->options->partiallycorrectfeedback;
$default_values['incorrectfeedback'] = $question->options->incorrectfeedback;
// prepare feedback editor to display files in draft area
foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) {
$draftid = file_get_submitted_draft_itemid($feedbackname);
$text = $question->options->$feedbackname;
$feedbackformat = $feedbackname . 'format';
$format = $question->options->$feedbackformat;
$default_values[$feedbackname] = array();
$default_values[$feedbackname]['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'qtype_multichoice', // component
$feedbackname, // filarea
!empty($question->id)?(int)$question->id:null, // itemid
$this->fileoptions, // options
$text // text
);
$default_values[$feedbackname]['format'] = $format;
$default_values[$feedbackname]['itemid'] = $draftid;
}
// prepare files code block ends
$question = (object)((array)$question + $default_values);
}
parent::set_data($question);
return $question;
}
function qtype() {

View file

@ -0,0 +1,31 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Serve question type files
*
* @since 2.0
* @package questionbank
* @subpackage questiontypes
* @author Dongsheng Cai <dongsheng@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function qtype_multichoice_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_multichoice', $filearea, $args, $forcedownload);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,20 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines the editing form for the numerical question type.
*
@ -39,30 +55,36 @@ class question_edit_numerical_form extends question_edit_form {
$creategrades->gradeoptions);
//------------------------------------------------------------------------------------------
$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) {
global $QTYPES ;
function data_preprocessing($question) {
global $QTYPES ;
if (isset($question->options)){
/* $default_values['unitgradingtype'] = $question->options->unitgradingtype ;
$default_values['unitpenalty'] = $question->options->unitpenalty ;
$default_values['showunits'] = $question->options->showunits ;
$default_values['unitsleft'] = $question->options->unitsleft ;
$default_values['instructions'] = $question->options->instructions ;
*/
$answers = $question->options->answers;
if (count($answers)) {
$key = 0;
foreach ($answers as $answer){
$draftid = file_get_submitted_draft_itemid('feedback['.$key.']');
$default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction;
$default_values['tolerance['.$key.']'] = $answer->tolerance;
$default_values['feedback['.$key.']'] = $answer->feedback;
$default_values['feedback['.$key.']'] = array();
$default_values['feedback['.$key.']']['format'] = $answer->feedbackformat;
$default_values['feedback['.$key.']']['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'question', // component
'answerfeedback', // filarea
!empty($answer->id)?(int)$answer->id:null, // itemid
$this->fileoptions, // options
$answer->feedback // text
);
$default_values['feedback['.$key.']']['itemid'] = $draftid;
$key++;
}
}
$QTYPES['numerical']->set_numerical_unit_data($question,$default_values);
$QTYPES['numerical']->set_numerical_unit_data($this, $question, $default_values);
/* if (isset($question->options->units)){
$units = array_values($question->options->units);
@ -75,8 +97,9 @@ class question_edit_numerical_form extends question_edit_form {
}*/
$question = (object)((array)$question + $default_values);
}
parent::set_data($question);
return $question;
}
function validation($data, $files) {
global $QTYPES;
$errors = parent::validation($data, $files);
@ -95,7 +118,7 @@ class question_edit_numerical_form extends question_edit_form {
if ($data['fraction'][$key] == 1) {
$maxgrade = true;
}
} else if ($data['fraction'][$key] != 0 || !html_is_blank($data['feedback'][$key])) {
} else if ($data['fraction'][$key] != 0 || !html_is_blank($data['feedback'][$key]['text'])) {
$errors["answer[$key]"] = get_string('answermustbenumberorstar', 'qtype_numerical');
$answercount++;
}
@ -110,6 +133,7 @@ class question_edit_numerical_form extends question_edit_form {
return $errors;
}
function qtype() {
return 'numerical';
}

View file

@ -0,0 +1,31 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Serve question type files
*
* @since 2.0
* @package questionbank
* @subpackage questiontypes
* @author Dongsheng Cai <dongsheng@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function qtype_numerical_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_numerical', $filearea, $args, $forcedownload);
}

View file

@ -1,10 +1,26 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* @author Martin Dougiamas and many others. Tim Hunt.
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank
* @subpackage questiontypes
*//** */
*/
require_once("$CFG->dirroot/question/type/shortanswer/questiontype.php");
@ -19,6 +35,7 @@ require_once("$CFG->dirroot/question/type/shortanswer/questiontype.php");
* @package questionbank
* @subpackage questiontypes
*/
class question_numerical_qtype extends question_shortanswer_qtype {
public $virtualqtype = false;
@ -52,7 +69,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}
$this->get_numerical_units($question);
//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);
// 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))) {
$question->options->unitgradingtype = 0; // total grade
$question->options->unitpenalty = 0;
// the default
// the default
if ($defaultunit = $this->get_default_numerical_unit($question)) {
// so units can be graded
$question->options->showunits = NUMERICALQUESTIONUNITTEXTINPUTDISPLAY ;
@ -85,18 +102,20 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$question->options->showunits = NUMERICALQUESTIONUNITNODISPLAY ;
}
$question->options->unitsleft = 0 ;
$question->options->instructions = '' ;
$question->options->instructions = '';
$question->options->instructionsformat = editors_get_preferred_format();
} else {
$question->options->unitgradingtype = $options->unitgradingtype;
$question->options->unitpenalty = $options->unitpenalty;
$question->options->showunits = $options->showunits ;
$question->options->unitsleft = $options->unitsleft ;
$question->options->instructions = $options->instructions ;
$question->options->showunits = $options->showunits;
$question->options->unitsleft = $options->unitsleft;
$question->options->instructions = $options->instructions;
$question->options->instructionsformat = $options->instructionsformat;
}
return true;
}
function get_numerical_units(&$question) {
global $DB;
if ($units = $DB->get_records('question_numerical_units', array('question' => $question->id), 'id ASC')) {
@ -127,6 +146,8 @@ class question_numerical_qtype extends question_shortanswer_qtype {
*/
function save_question_options($question) {
global $DB;
$context = $question->context;
// Get old versions of the objects
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
@ -148,7 +169,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
foreach ($question->answer as $key => $dataanswer) {
// Check for, and ingore, completely blank answer from the form.
if (trim($dataanswer) == '' && $question->fraction[$key] == 0 &&
html_is_blank($question->feedback[$key])) {
html_is_blank($question->feedback[$key]['text'])) {
continue;
}
@ -163,13 +184,23 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}
}
$answer->fraction = $question->fraction[$key];
$answer->feedback = trim($question->feedback[$key]);
$feedbacktext = trim($question->feedback[$key]['text']);
$draftid = $question->feedback[$key]['itemid'];
$answer->feedbackformat = $question->feedback[$key]['format'];
if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
$feedbacktext = file_save_draft_area_files($draftid, $context->id, 'question', 'answerfeedback', $oldanswer->id, self::$fileoptions, $feedbacktext);
$answer->feedback = $feedbacktext;
$answer->id = $oldanswer->id;
$DB->update_record("question_answers", $answer);
} else { // This is a completely new answer
$answer->feedback = $feedbacktext;
$answer->id = $DB->insert_record("question_answers", $answer);
$feedbacktext = file_save_draft_area_files($draftid, $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $feedbacktext);
$DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$answer->id));
}
// Set up the options object
@ -220,10 +251,11 @@ class question_numerical_qtype extends question_shortanswer_qtype {
function save_numerical_options(&$question) {
global $DB;
$result = new stdClass;
// numerical options
$update = true ;
$options = $DB->get_record("question_numerical_options", array("question" => $question->id));
$options = $DB->get_record('question_numerical_options', array('question' => $question->id));
if (!$options) {
$update = false;
$options = new stdClass;
@ -246,7 +278,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}else {
$options->showunits = $question->showunits1;
}
}else {
}else {
if(isset($question->showunits)){
$options->showunits = $question->showunits;
}else {
@ -264,22 +296,27 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}else {
$options->unitsleft = 0 ;
}
$options->instructionsformat = $question->instructions['format'];
if(isset($question->instructions)){
$options->instructions = trim($question->instructions);
$options->instructions = trim($question->instructions['text']);
}else {
$options->instructions = '' ;
}
$component = 'qtype_' . $question->qtype;
$options->instructions = file_save_draft_area_files($question->instructions['itemid'],
$question->context->id, // context
$component, // component
'instruction', // filearea
$question->id, // itemid
self::$fileoptions, // options
$question->instructions['text'] // text
);
if ($update) {
if (!$DB->update_record("question_numerical_options", $options)) {
$result->error = "Could not update numerical question options! (id=$options->id)";
return $result;
}
$DB->update_record("question_numerical_options", $options);
} else {
if (!$DB->insert_record("question_numerical_options", $options)) {
$result->error = "Could not insert numerical question options!";
return $result;
}
$id = $DB->insert_record("question_numerical_options", $options);
}
return $result;
}
@ -320,7 +357,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$state->responses = array();
$state->responses['answer'] = '';
$state->responses['unit'] = '';
return true;
}
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));
if (preg_match('~^([+-]?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)([eE][-+]?[0-9]+)?)([^0-9].*)?$~',
$rawresponse, $responseparts)) {
if(isset($responseparts[5]) ){
if(isset($responseparts[5]) ){
$unit = $responseparts[5] ;
}
if(isset($responseparts[1]) ){
if(isset($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) {
global $CFG;
$context = $this->get_context_by_category_id($question->category);
$readonly = empty($options->readonly) ? '' : 'readonly="readonly"';
$formatoptions = new stdClass;
$formatoptions->noclean = true;
$formatoptions->para = false;
$nameprefix = $question->name_prefix;
$component = 'qtype_' . $question->qtype;
// rewrite instructions text
$question->options->instructions = quiz_rewrite_question_urls($question->options->instructions, 'pluginfile.php', $context->id, $component, 'instruction', array($state->attempt, $state->question), $question->id);
/// Print question text and media
$questiontext = format_text($question->questiontext,
$question->questiontextformat,
$formatoptions, $cmoptions->course);
$image = get_question_image($question);
/// Print input controls
// as the entry is controlled the question type here is numerical
// 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
// If $question->options->showunits == NUMERICALQUESTIONUNITMULTICHOICEDISPLAY"
// radio elements display the defined unit
// The code allows the input number elememt to be displayed
// before i.e. at left or after at rigth of the unit variants.
$nameanswer = "name=\"".$question->name_prefix."answer\"";
$nameunit = "name=\"".$question->name_prefix."unit\"";
$nameanswer = "name=\"".$question->name_prefix."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'] );
}
@ -438,7 +478,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}
if (isset($state->responses['unit']) && $state->responses['unit']!='') {
$valueunit = ' value="'.s($state->responses['unit']).'" ';
} else {
} else {
$valueunit = ' value="" ';
if ($question->options->showunits == NUMERICALQUESTIONUNITTEXTDISPLAY ){
$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
// having to test for * so response as long as not empty
$response = $this->extract_numerical_response($state->responses['answer']);
$break = 0 ;
$break = 0 ;
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 !== '*' ) {
$this->get_tolerance_interval($answer);
}
$answer->feedback = quiz_rewrite_question_urls($answer->feedback, 'pluginfile.php', $context->id, 'question', 'answerfeedback', array($state->attempt, $state->question), $answer->id);
if ($answer->answer === '*') {
$answerasterisk = true ;
$rawgrade = $answer->fraction ;
$rawgrade = $answer->fraction ;
$class = question_get_feedback_class($answer->fraction);
$feedbackimg = question_get_feedback_image($answer->fraction);
$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);
if ($answer->feedback) {
$feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course);
}
if ( isset($question->options->units))
{
if ( isset($question->options->units))
{
$valid_numerical_unit = true ;
}
$break = 1 ;
} else if ($response !== false && isset($question->options->units) && count($question->options->units) > 0) {
$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.
$testresponse = $response /$unit->multiplier ;
$testresponse = $response /$unit->multiplier ;
if($answer->min <= $testresponse && $testresponse <= $answer->max) {
$unittested = $unit->unit ;
$rawgrade = $answer->fraction ;
$rawgrade = $answer->fraction ;
$class = question_get_feedback_class($answer->fraction);
$feedbackimg = question_get_feedback_image($answer->fraction);
if ($answer->feedback) {
$feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course);
}
if($state->responses['unit'] == $unit->unit){
if($state->responses['unit'] == $unit->unit){
$classunitvalue = $answer->fraction ;
}else {
}else {
$classunitvalue == 0 ;
}
$classunit = question_get_feedback_class($classunitvalue);
$classunit = question_get_feedback_class($classunitvalue);
$feedbackimgunit = question_get_feedback_image($classunitvalue, $options->feedback);
$break = 1 ;
$break = 1 ;
break;
}
}
}else if($response !== false && ($answer->min <= $response && $response <= $answer->max) ) {
$rawgrade = $answer->fraction ;
} else if($response !== false && ($answer->min <= $response && $response <= $answer->max) ) {
$rawgrade = $answer->fraction ;
$class = question_get_feedback_class($answer->fraction);
$feedbackimg = question_get_feedback_image($answer->fraction);
if ($answer->feedback) {
$feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course);
$feedback = format_text($answer->feedback, $answer->feedbackformat, $formatoptions, $cmoptions->course);
}
$break = 1 ;
}
if ($break) break;
if ($break) {
break;
}
}
}
$state->options->raw_unitpenalty = 0 ;
$raw_unitpenalty = 0 ;
if( $question->options->showunits == NUMERICALQUESTIONUNITNODISPLAY ||
if( $question->options->showunits == NUMERICALQUESTIONUNITNODISPLAY ||
$question->options->showunits == NUMERICALQUESTIONUNITTEXTDISPLAY ) {
$classunitvalue = 1 ;
}
if($classunitvalue == 0){
if($question->options->unitgradingtype == 1){
@ -537,10 +580,9 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}else {
$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
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
* answer or in one of the unit , false if it doesn't.
* 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 ) {
// 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
* 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
* the $state->responses['unit'] comes from either
* the $state->responses['unit'] comes from either
* a multichoice radio element NUMERICALQUESTIONUNITMULTICHOICEDISPLAY
* where the $state->responses['unit'] value is the key => unit object
* in the the $question->options->units array
* or an input text element NUMERICALUNITTEXTINPUTDISPLAY
* which contains the student response
* for NUMERICALQUESTIONUNITTEXTDISPLAY and NUMERICALQUESTIONUNITNODISPLAY
*
*/
*
*/
if (!isset($state->responses['answer']) && isset($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.
$this->get_tolerance_interval($answer);
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) {
foreach($question->options->units as $key =>$unit){
foreach($question->options->units as $key =>$unit){
$testresponse = $response /$unit->multiplier ;
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
* number and unit of numerical or calculated questions
* The code handles also the case when there no unit defined by the user or
* when used in a multianswer (Cloze) question.
* number and unit of numerical or calculated questions
* The code handles also the case when there no unit defined by the user or
* when used in a multianswer (Cloze) question.
* This function performs response processing and grading and updates
* the state accordingly.
* @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[''])){
$state->responses['answer'] = $state->responses[''];
}
//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
// 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 ;
$response = $this->extract_numerical_response($state->responses['answer']);
$answerasterisk = false ;
$break = 0 ;
$break = 0 ;
foreach($question->options->answers as $answer) {
if ($answer->answer !== '*' ) {
// The student did type a number, so check it with tolerances.
$this->get_tolerance_interval($answer);
}
// if * then everything is OK even unit
// if * then everything is OK even unit
if ($answer->answer === '*') {
$state->raw_grade = $answer->fraction;
if ( isset($question->options->units)){
if ( isset($question->options->units)){
$valid_numerical_unit = true ;
}
$answerasterisk = true ;
$answerasterisk = true ;
$break = 1 ;
}else if ($response !== false && isset($question->options->units) && count($question->options->units) > 0) {
$hasunits = 1 ;
foreach($question->options->units as $key => $unit){
$testresponse = $response /$unit->multiplier ;
foreach($question->options->units as $key => $unit){
$testresponse = $response /$unit->multiplier ;
if($answer->min <= $testresponse && $testresponse <= $answer->max) {
$state->raw_grade = $answer->fraction;
$unittested = $unit->unit ;
$break = 1 ;
$break = 1 ;
break;
}
}
}
}else if ($response !== false) {
if($this->test_response($question, $state, $answer)) {
@ -700,9 +742,9 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}
if ($break) break;
} //foreach($question->options
// in all cases the unit should be tested
if( $question->options->showunits == NUMERICALQUESTIONUNITNODISPLAY ||
if( $question->options->showunits == NUMERICALQUESTIONUNITNODISPLAY ||
$question->options->showunits == NUMERICALQUESTIONUNITTEXTDISPLAY ) {
$valid_numerical_unit = true ;
}else {
@ -714,7 +756,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$valid_numerical_unit = true ;
}
}
// apply unit penalty
$raw_unitpenalty = 0 ;
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 ;
}
// Make sure we don't assign negative or too high marks.
$state->raw_grade = min(max((float) $state->raw_grade,
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 ,
if ( strpos($rawresponse,'.' ) !== false || substr_count($rawresponse,',') > 1 ) {
$replace = array('', '');
}else { // remove spaces and normalise , to a . .
}else { // remove spaces and normalise , to a . .
$replace = array('', '.');
}
$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 ,
if ( strpos($rawresponse,'.' ) !== false || substr_count($rawresponse,',') > 1 ) {
$replace = array('', '');
}else { // remove spaces and normalise , to a . .
}else { // remove spaces and normalise , to a . .
$replace = array('', '.');
}
$rawresponse = str_replace($search, $replace, $rawresponse);
@ -920,10 +962,10 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}
/**
* function used in function definition_inner()
* of edit_..._form.php for
* numerical, calculated, calculatedsimple
*/
* function used in function definition_inner()
* of edit_..._form.php for
* numerical, calculated, calculatedsimple
*/
function add_units_options(&$mform, &$that){
$mform->addElement('header', 'unithandling', get_string('unitshandling', 'qtype_numerical'));
// 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('selectunits', 'qtype_numerical') , get_string('unitchoice', 'qtype_numerical'),1);
$mform->addGroup($showunits0grp, 'showunits0grp', get_string('studentunitanswer', 'qtype_numerical'),' OR ' , false);
$mform->addElement('htmleditor', 'instructions', get_string('instructions', 'qtype_numerical'),
array('rows' => 10, 'course' => $that->coursefilesid));
$mform->addElement('editor', 'instructions', get_string('instructions', 'qtype_numerical'), null, $that->editoroptions);
$mform->addElement('static', 'separator1', '<HR/>', '<HR/>');
// Units are not graded
$mform->addElement('radio', 'unitrole', get_string('unitnotgraded', 'qtype_numerical'), get_string('onlynumerical', 'qtype_numerical'),1);
@ -967,6 +1008,8 @@ class question_numerical_qtype extends question_shortanswer_qtype {
$mform->disabledIf('unitsleft', 'showunits1','eq','3');
$mform->disabledIf('showunits1','unitrole','eq','0');
$mform->disabledIf('showunits0','unitrole','eq','1');
}
/**
@ -974,7 +1017,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
* of edit_..._form.php for
* numerical, calculated, calculatedsimple
*/
function add_units_elements(& $mform,& $that) {
function add_units_elements(& $mform,& $that) {
$repeated = array();
$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 ()
* of edit_..._form.php for
* numerical, calculated, calculatedsimple
*/
function set_numerical_unit_data(&$question,&$default_values){
* function used in in function data_preprocessing() of edit_numerical_form.php for
* numerical, calculated, calculatedsimple
*/
function set_numerical_unit_data($mform, &$question, &$default_values){
list($categoryid) = explode(',', $question->category);
$context = $this->get_context_by_category_id($categoryid);
if (isset($question->options)){
$default_values['unitgradingtype'] = $question->options->unitgradingtype ;
$default_values['unitpenalty'] = $question->options->unitpenalty ;
switch ($question->options->showunits){
case 'O' :
case '1' :
case '1' :
$default_values['showunits0'] = $question->options->showunits ;
$default_values['unitrole'] = 0 ;
break;
case '2' :
case '3' :
case '3' :
$default_values['showunits1'] = $question->options->showunits ;
$default_values['unitrole'] = 1 ;
break;
}
}
$default_values['unitsleft'] = $question->options->unitsleft ;
$default_values['instructions'] = $question->options->instructions ;
if (isset($question->options->units)){
// processing files
$component = 'qtype_' . $question->qtype;
$draftid = file_get_submitted_draft_itemid('instructions');
$default_values['instructions'] = array();
$default_values['instructions']['format'] = $question->options->instructionsformat;
$default_values['instructions']['text'] = file_prepare_draft_area(
$draftid, // draftid
$context->id, // context
$component, // component
'instruction', // filarea
!empty($question->id)?(int)$question->id:null, // itemid
$mform->fileoptions, // options
$question->options->instructions // text
);
$default_values['instructions']['itemid'] = $draftid;
if (isset($question->options->units)) {
$units = array_values($question->options->units);
if (!empty($units)) {
foreach ($units as $key => $unit){
@ -1042,11 +1102,11 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}
}
/**
* function use in in function validation()
* of edit_..._form.php for
* numerical, calculated, calculatedsimple
*/
/**
* function use in in function validation()
* of edit_..._form.php for
* numerical, calculated, calculatedsimple
*/
function validate_numerical_options(& $data, & $errors){
$units = $data['unit'];
@ -1055,9 +1115,9 @@ class question_numerical_qtype extends question_shortanswer_qtype {
}else {
$showunits = $data['showunits1'];
}
if (($showunits == NUMERICALQUESTIONUNITTEXTINPUTDISPLAY) ||
($showunits == NUMERICALQUESTIONUNITMULTICHOICEDISPLAY ) ||
if (($showunits == NUMERICALQUESTIONUNITTEXTINPUTDISPLAY) ||
($showunits == NUMERICALQUESTIONUNITMULTICHOICEDISPLAY ) ||
($showunits == NUMERICALQUESTIONUNITTEXTDISPLAY )){
if (trim($units[0]) == ''){
$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.
$alreadyseenunits = array();
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);
}
/**
* When move the category of questions, the belonging files should be moved as well
* @param object $question, question information
* @param object $newcategory, target category information
*/
function move_files($question, $newcategory) {
global $DB;
parent::move_files($question, $newcategory);
$fs = get_file_storage();
// process files in answer
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
}
$component = 'question';
$filearea = 'answerfeedback';
foreach ($oldanswers as $answer) {
$files = $fs->get_area_files($question->contextid, $component, $filearea, $answer->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
$newfile = new object();
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
$storedfile->delete();
}
}
}
$component = 'qtype_numerical';
$filearea = 'instruction';
$files = $fs->get_area_files($question->contextid, $component, $filearea, $question->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
$newfile = new object();
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
$storedfile->delete();
}
}
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
$itemid = reset($args);
if ($component == 'question' && $filearea == 'answerfeedback') {
$result = $options->feedback && array_key_exists($itemid, $question->options->answers);
if (!$result) {
return false;
}
foreach($question->options->answers as $answer) {
if ($this->test_response($question, $state, $answer)) {
return true;
}
}
return false;
} else if ($filearea == 'instruction') {
if ($itemid != $question->id) {
return false;
} else {
return true;
}
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
}
}
// INITIATION - Without this line the question type is not in use.

View file

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

View file

@ -1,4 +1,20 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The default questiontype class.
*
@ -30,6 +46,7 @@ require_once($CFG->libdir . '/questionlib.php');
* @subpackage questiontypes
*/
class default_questiontype {
public static $fileoptions = array('subdirs'=>false, 'maxfiles'=>-1, 'maxbytes'=>0);
/**
* Name of the question type
@ -294,31 +311,35 @@ class default_questiontype {
*/
function save_question($question, $form, $course) {
global $USER, $DB, $OUTPUT;
list($question->category) = explode(',', $form->category);
$context = $this->get_context_by_category_id($question->category);
// This default implementation is suitable for most
// question types.
// First, save the basic question itself
$question->name = trim($form->name);
$question->questiontext = trim($form->questiontext);
$question->questiontextformat = $form->questiontextformat;
$question->parent = isset($form->parent) ? $form->parent : 0;
$question->length = $this->actual_number_of_questions($question);
$question->penalty = isset($form->penalty) ? $form->penalty : 0;
if (empty($form->image)) {
$question->image = '';
if (empty($form->questiontext['text'])) {
$question->questiontext = '';
} else {
$question->image = $form->image;
$question->questiontext = trim($form->questiontext['text']);;
}
$question->questiontextformat = !empty($form->questiontext['format'])?$form->questiontext['format']:0;
if (empty($form->generalfeedback)) {
if (empty($form->generalfeedback['text'])) {
$question->generalfeedback = '';
} else {
$question->generalfeedback = trim($form->generalfeedback);
$question->generalfeedback = trim($form->generalfeedback['text']);
}
$question->generalfeedbackformat = !empty($form->generalfeedback['format'])?$form->generalfeedback['format']:0;
if (empty($question->name)) {
$question->name = shorten_text(strip_tags($question->questiontext), 15);
$question->name = shorten_text(strip_tags($form->questiontext['text']), 15);
if (empty($question->name)) {
$question->name = '-';
}
@ -332,14 +353,16 @@ class default_questiontype {
$question->defaultgrade = $form->defaultgrade;
}
list($question->category) = explode(',', $form->category);
if (!empty($question->id)) {
/// Question already exists, update.
$question->modifiedby = $USER->id;
$question->timemodified = time();
$DB->update_record('question', $question);
// process queston text
$question->questiontext = file_save_draft_area_files($form->questiontext['itemid'], $context->id, 'question', 'questiontext', (int)$question->id, self::$fileoptions, $question->questiontext);
// process feedback text
$question->generalfeedback = file_save_draft_area_files($form->generalfeedback['itemid'], $context->id, 'question', 'generalfeedback', (int)$question->id, self::$fileoptions, $question->generalfeedback);
$DB->update_record('question', $question);
} else {
/// New question.
// Set the unique code
@ -349,6 +372,12 @@ class default_questiontype {
$question->timecreated = time();
$question->timemodified = time();
$question->id = $DB->insert_record('question', $question);
// process queston text
$question->questiontext = file_save_draft_area_files($form->questiontext['itemid'], $context->id, 'question', 'questiontext', (int)$question->id, self::$fileoptions, $question->questiontext);
// process feedback text
$question->generalfeedback = file_save_draft_area_files($form->generalfeedback['itemid'], $context->id, 'question', 'generalfeedback', (int)$question->id, self::$fileoptions, $question->generalfeedback);
$DB->update_record('question', $question);
}
// Now to save all the answers and type-specific options
@ -356,11 +385,14 @@ class default_questiontype {
$form->qtype = $question->qtype;
$form->category = $question->category;
$form->questiontext = $question->questiontext;
$form->questiontextformat = $question->questiontextformat;
// current context
$form->context = $context;
$result = $this->save_question_options($form);
if (!empty($result->error)) {
print_error('questionsaveerror', 'question', '', $result->error);
print_error($result->error);
}
if (!empty($result->notice)) {
@ -888,12 +920,18 @@ class default_questiontype {
* @param object $cmoptions
* @param object $options An object describing the rendering options.
*/
function print_question(&$question, &$state, $number, $cmoptions, $options) {
function print_question(&$question, &$state, $number, $cmoptions, $options, $context=null) {
/* The default implementation should work for most question types
provided the member functions it calls are overridden where required.
The layout is determined by the template question.html */
global $CFG, $OUTPUT;
$context = $this->get_context_by_category_id($question->category);
$question->questiontext = quiz_rewrite_question_urls($question->questiontext, 'pluginfile.php', $context->id, 'question', 'questiontext', array($state->attempt, $state->question), $question->id);
$question->generalfeedback = quiz_rewrite_question_urls($question->generalfeedback, 'pluginfile.php', $context->id, 'question', 'generalfeedback', array($state->attempt, $state->question), $question->id);
$isgraded = question_state_is_graded($state->last_graded);
if (isset($question->randomquestionid)) {
@ -908,7 +946,7 @@ class default_questiontype {
$generalfeedback = '';
if ($isgraded && $options->generalfeedback) {
$generalfeedback = $this->format_text($question->generalfeedback,
$question->questiontextformat, $cmoptions);
$question->generalfeedbackformat, $cmoptions);
}
$grade = '';
@ -1246,6 +1284,22 @@ class default_questiontype {
.' been implemented for question type '.$this->name());
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
if ($component == 'question' && $filearea == 'questiontext') {
// Question text always visible.
return true;
} else if ($component == 'question' && $filearea = 'generalfeedback') {
return $options->generalfeedback && question_state_is_graded($state->last_graded);
} else {
// Unrecognised component or filearea.
return false;
}
}
/**
* Prints the submit button(s) for the question in the given state
*
@ -1494,29 +1548,6 @@ class default_questiontype {
*/
function find_file_links($question, $courseid){
$urls = array();
/// Question image
if ($question->image != ''){
if (substr(strtolower($question->image), 0, 7) == 'http://') {
$matches = array();
//support for older questions where we have a complete url in image field
if (preg_match('!^'.question_file_links_base_url($courseid).'(.*)!i', $question->image, $matches)){
if ($cleanedurl = question_url_check($urls[$matches[2]])){
$urls[$cleanedurl] = null;
}
}
} else {
if ($question->image != ''){
if ($cleanedurl = question_url_check($question->image)){
$urls[$cleanedurl] = null;//will be set later
}
}
}
}
/// Questiontext and general feedback.
$urls += question_find_file_links_from_html($question->questiontext, $courseid);
$urls += question_find_file_links_from_html($question->generalfeedback, $courseid);
@ -1557,21 +1588,6 @@ class default_questiontype {
function replace_file_links($question, $fromcourseid, $tocourseid, $url, $destination){
global $CFG, $DB;
$updateqrec = false;
/// Question image
if (!empty($question->image)){
//support for older questions where we have a complete url in image field
if (substr(strtolower($question->image), 0, 7) == 'http://') {
$questionimage = preg_replace('!^'.question_file_links_base_url($fromcourseid).preg_quote($url, '!').'$!i', $destination, $question->image, 1);
} else {
$questionimage = preg_replace('!^'.preg_quote($url, '!').'$!i', $destination, $question->image, 1);
}
if ($questionimage != $question->image){
$question->image = $questionimage;
$updateqrec = true;
}
}
/// Questiontext and general feedback.
$question->questiontext = question_replace_file_links_in_html($question->questiontext, $fromcourseid, $tocourseid, $url, $destination, $updateqrec);
$question->generalfeedback = question_replace_file_links_in_html($question->generalfeedback, $fromcourseid, $tocourseid, $url, $destination, $updateqrec);
@ -1802,5 +1818,45 @@ class default_questiontype {
$question->qtype = $this->qtype;
return array($form, $question);
}
}
/**
* Get question context by category id
* @param int $category
* @return object $context
*/
function get_context_by_category_id($category) {
global $DB;
$contextid = $DB->get_field('question_categories', 'contextid', array('id'=>$category));
$context = get_context_instance_by_id($contextid);
return $context;
}
/**
* When move the category of questions, the belonging files should be moved as well
* @param object $question, question information
* @param object $newcategory, target category information
*/
function move_files($question, $newcategory) {
global $DB;
$fs = get_file_storage();
$component = 'question';
// process general question files
// Currently we have questiontext and generalfeedback areas
foreach (array('questiontext', 'generalfeedback') as $filearea) {
$files = $fs->get_area_files($question->contextid, $component, $filearea, $question->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
if ($newcategory->contextid == $question->contextid) {
continue;
}
$newfile = new object();
// only contextid changed
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
// delete old files
$storedfile->delete();
}
}
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,22 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* Defines the editing form for the shortanswer question type.
*
@ -30,22 +48,40 @@ class question_edit_shortanswer_form extends question_edit_form {
$creategrades->gradeoptions);
}
function set_data($question) {
function data_preprocessing($question) {
if (isset($question->options)){
$answers = $question->options->answers;
$answers_ids = array();
if (count($answers)) {
$key = 0;
foreach ($answers as $answer){
$answers_ids[] = $answer->id;
$default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction;
$default_values['feedback['.$key.']'] = $answer->feedback;
$default_values['feedback['.$key.']'] = array();
// prepare feedback editor to display files in draft area
$draftid_editor = file_get_submitted_draft_itemid('feedback['.$key.']');
$default_values['feedback['.$key.']']['text'] = file_prepare_draft_area(
$draftid_editor, // draftid
$this->context->id, // context
'question', // component
'answerfeedback', // filarea
!empty($answer->id)?(int)$answer->id:null, // itemid
$this->fileoptions, // options
$answer->feedback // text
);
$default_values['feedback['.$key.']']['itemid'] = $draftid_editor;
// prepare files code block ends
$default_values['feedback['.$key.']']['format'] = $answer->feedbackformat;
$key++;
}
}
$default_values['usecase'] = $question->options->usecase;
$default_values['usecase'] = $question->options->usecase;
$question = (object)((array)$question + $default_values);
}
parent::set_data($question);
return $question;
}
function validation($data, $files) {
$errors = parent::validation($data, $files);
@ -59,7 +95,7 @@ class question_edit_shortanswer_form extends question_edit_form {
if ($data['fraction'][$key] == 1) {
$maxgrade = true;
}
} else if ($data['fraction'][$key] != 0 || !html_is_blank($data['feedback'][$key])) {
} else if ($data['fraction'][$key] != 0 || !html_is_blank($data['feedback'][$key]['text'])) {
$errors["answer[$key]"] = get_string('answermustbegiven', 'qtype_shortanswer');
$answercount++;
}

View file

@ -0,0 +1,31 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Serve question type files
*
* @since 2.0
* @package questionbank
* @subpackage questiontypes
* @author Dongsheng Cai <dongsheng@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function qtype_shortanswer_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $DB, $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_shortanswer', $filearea, $args, $forcedownload);
}

View file

@ -1,5 +1,20 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
///////////////////
/// SHORTANSWER ///
///////////////////
@ -27,17 +42,48 @@ class question_shortanswer_qtype extends default_questiontype {
}
function extra_question_fields() {
return array('question_shortanswer','answers','usecase');
return array('question_shortanswer', 'answers', 'usecase');
}
function questionid_column_name() {
return 'question';
}
/**
* When move the category of questions, the belonging files should be moved as well
* @param object $question, question information
* @param object $newcategory, target category information
*/
function move_files($question, $newcategory) {
global $DB;
parent::move_files($question, $newcategory);
$fs = get_file_storage();
// process files in answer
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
}
$component = 'question';
$filearea = 'answerfeedback';
foreach ($oldanswers as $answer) {
$files = $fs->get_area_files($question->contextid, $component, $filearea, $answer->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
$newfile = new object();
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
$storedfile->delete();
}
}
}
}
function save_question_options($question) {
global $DB;
$result = new stdClass;
$context = $question->context;
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
}
@ -49,23 +95,36 @@ class question_shortanswer_qtype extends default_questiontype {
foreach ($question->answer as $key => $dataanswer) {
// Check for, and ingore, completely blank answer from the form.
if (trim($dataanswer) == '' && $question->fraction[$key] == 0 &&
html_is_blank($question->feedback[$key])) {
html_is_blank($question->feedback[$key]['text'])) {
continue;
}
$feedbackformat = $question->feedback[$key]['format'];
if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
$answer = $oldanswer;
$answer->answer = trim($dataanswer);
$answer->fraction = $question->fraction[$key];
$answer->feedback = $question->feedback[$key];
// save draft file and rewrite text in feedback
$answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $oldanswer->id, self::$fileoptions, $question->feedback[$key]['text']);
$answer->feedbackformat = $feedbackformat;
$DB->update_record("question_answers", $answer);
} else { // This is a completely new answer
$answer = new stdClass;
$answer->answer = trim($dataanswer);
$answer->question = $question->id;
$answer->fraction = $question->fraction[$key];
$answer->feedback = $question->feedback[$key];
// feedback content needs to be rewriten
$answer->feedback = '';
$answer->feedbackformat = $feedbackformat;
$answer->id = $DB->insert_record("question_answers", $answer);
// save draft file and rewrite text in feedback
$answer->feedback = file_save_draft_area_files($question->feedback[$key]['itemid'], $context->id, 'question', 'answerfeedback', $answer->id, self::$fileoptions, $question->feedback[$key]['text']);
// update feedback content
$DB->set_field('question_answers', 'feedback', $answer->feedback, array('id'=>$answer->id));
}
$answers[] = $answer->id;
if ($question->fraction[$key] > $maxfraction) {
@ -97,6 +156,8 @@ class question_shortanswer_qtype extends default_questiontype {
}
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG;
$context = $this->get_context_by_category_id($question->category);
/// This implementation is also used by question type 'numerical'
$readonly = empty($options->readonly) ? '' : 'readonly="readonly"';
$formatoptions = new stdClass;
@ -109,7 +170,6 @@ class question_shortanswer_qtype extends default_questiontype {
$questiontext = format_text($question->questiontext,
$question->questiontextformat,
$formatoptions, $cmoptions->course);
$image = get_question_image($question);
/// Print input controls
@ -135,6 +195,7 @@ class question_shortanswer_qtype extends default_questiontype {
$class = question_get_feedback_class($answer->fraction);
$feedbackimg = question_get_feedback_image($answer->fraction);
if ($answer->feedback) {
$answer->feedback = quiz_rewrite_question_urls($answer->feedback, 'pluginfile.php', $context->id, 'question', 'answerfeedback', array($state->attempt, $state->question), $answer->id);
$feedback = format_text($answer->feedback, true, $formatoptions, $cmoptions->course);
}
break;
@ -145,7 +206,7 @@ class question_shortanswer_qtype extends default_questiontype {
/// Removed correct answer, to be displayed later MDL-7496
include($this->get_display_html_path());
}
function get_display_html_path() {
global $CFG;
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_string('gradingdetails', 'quiz', $grade) ;
// 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
if(isset($state->options->raw_unitpenalty) && $state->options->raw_unitpenalty > 0.0 ){
echo ' ';
print_string('unitappliedpenalty','qtype_numerical',question_format_grade($cmoptions, $state->options->raw_unitpenalty ));
}
}
if ($cmoptions->penaltyscheme) {
// print details of grade adjustment due to penalties
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);
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
if ($component == 'question' && $filearea == 'answerfeedback') {
$answers = &$question->options->answers;
if (isset($state->responses[''])) {
$response = $state->responses[''];
} else {
$response = '';
}
$answerid = reset($args); // itemid is answer id.
if (empty($options->feedback)) {
return false;
}
foreach($answers as $answer) {
if ($this->test_response($question, $state, $answer)) {
return true;
}
}
return false;
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
}
}
//// END OF CLASS ////

View file

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

View file

@ -1,9 +1,26 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
}
require_once($CFG->dirroot.'/question/type/edit_question_form.php');
/**
* Defines the editing form for the thruefalse question type.
*
@ -12,7 +29,7 @@ require_once($CFG->dirroot.'/question/type/edit_question_form.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank
* @subpackage questiontypes
*//** */
*/
/**
* truefalse editing form definition.
@ -27,12 +44,10 @@ class question_edit_truefalse_form extends question_edit_form {
$mform->addElement('select', 'correctanswer', get_string('correctanswer', 'qtype_truefalse'),
array(0 => get_string('false', 'qtype_truefalse'), 1 => get_string('true', 'qtype_truefalse')));
$mform->addElement('htmleditor', 'feedbacktrue', get_string('feedbacktrue', 'qtype_truefalse'),
array('course' => $this->coursefilesid));;
$mform->addElement('editor', 'feedbacktrue', get_string('feedbacktrue', 'qtype_truefalse'), null, $this->editoroptions);;
$mform->setType('feedbacktrue', PARAM_RAW);
$mform->addElement('htmleditor', 'feedbackfalse', get_string('feedbackfalse', 'qtype_truefalse'),
array('course' => $this->coursefilesid));
$mform->addElement('editor', 'feedbackfalse', get_string('feedbackfalse', 'qtype_truefalse'), null, $this->editoroptions);
$mform->setType('feedbackfalse', PARAM_RAW);
// Fix penalty factor at 1.
@ -43,9 +58,45 @@ class question_edit_truefalse_form extends question_edit_form {
function set_data($question) {
if (!empty($question->options->trueanswer)) {
$trueanswer = $question->options->answers[$question->options->trueanswer];
$draftid = file_get_submitted_draft_itemid('trueanswer');
$answerid = $question->options->trueanswer;
$text = $trueanswer->feedback;
$question->correctanswer = ($trueanswer->fraction != 0);
$question->feedbacktrue = $trueanswer->feedback;
$question->feedbackfalse = $question->options->answers[$question->options->falseanswer]->feedback;
$question->feedbacktrue = array();
$question->feedbacktrue['text'] = $trueanswer->feedback;
$question->feedbacktrue['format'] = $trueanswer->feedbackformat;
$question->feedbacktrue['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'question', // component
'answerfeedback', // filarea
!empty($answerid)?(int)$answerid:null, // itemid
$this->fileoptions, // options
$text // text
);
$question->feedbacktrue['itemid'] = $draftid;
}
if (!empty($question->options->falseanswer)) {
$falseanswer = $question->options->answers[$question->options->falseanswer];
$draftid = file_get_submitted_draft_itemid('falseanswer');
$answerid = $question->options->falseanswer;
$text = $falseanswer->feedback;
$question->correctanswer = ($falseanswer->fraction != 0);
$question->feedbackfalse = array();
$question->feedbackfalse['text'] = $falseanswer->feedback;
$question->feedbackfalse['format'] = $falseanswer->feedbackformat;
$question->feedbackfalse['text'] = file_prepare_draft_area(
$draftid, // draftid
$this->context->id, // context
'question', // component
'answerfeedback', // filarea
!empty($answerid)?(int)$answerid:null, // itemid
$this->fileoptions, // options
$text // text
);
$question->feedbackfalse['itemid'] = $draftid;
}
parent::set_data($question);
}

View file

@ -0,0 +1,31 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Serve question type files
*
* @since 2.0
* @package qtype
* @subpackage qtype_truefalse
* @copyright The Open Unviersity
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function qtype_truefalse_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_truefalse', $filearea, $args, $forcedownload);
}

View file

@ -1,5 +1,22 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/////////////////
/// TRUEFALSE ///
/////////////////
@ -24,34 +41,52 @@ class question_truefalse_qtype extends default_questiontype {
$oldanswers = array();
}
$feedbacktext = $question->feedbacktrue['text'];
$feedbackitemid = $question->feedbacktrue['itemid'];
$feedbackformat = $question->feedbacktrue['format'];
// Save answer 'True'
if ($true = array_shift($oldanswers)) { // Existing answer, so reuse it
$true->answer = get_string("true", "quiz");
$true->fraction = $question->correctanswer;
$true->feedback = $question->feedbacktrue;
$true->feedback = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $true->id, self::$fileoptions, $feedbacktext);
$true->feedbackformat = $feedbackformat;
$DB->update_record("question_answers", $true);
} else {
unset($true);
$true = new stdclass;
$true->answer = get_string("true", "quiz");
$true->question = $question->id;
$true->fraction = $question->correctanswer;
$true->feedback = $question->feedbacktrue;
$true->feedback = '';
$true->feedbackformat = $feedbackformat;
$true->id = $DB->insert_record("question_answers", $true);
$feedbacktext = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $true->id, self::$fileoptions, $feedbacktext);
$DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$true->id));
}
$feedbacktext = $question->feedbackfalse['text'];
$feedbackitemid = $question->feedbackfalse['itemid'];
$feedbackformat = $question->feedbackfalse['format'];
// Save answer 'False'
if ($false = array_shift($oldanswers)) { // Existing answer, so reuse it
$false->answer = get_string("false", "quiz");
$false->fraction = 1 - (int)$question->correctanswer;
$false->feedback = $question->feedbackfalse;
$false->feedback = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $false->id, self::$fileoptions, $feedbacktext);
$false->feedbackformat = $feedbackformat;
$DB->update_record("question_answers", $false);
} else {
unset($false);
$false = new stdclass;
$false->answer = get_string("false", "quiz");
$false->question = $question->id;
$false->fraction = 1 - (int)$question->correctanswer;
$false->feedback = $question->feedbackfalse;
$false->feedback = '';
$false->feedbackformat = $feedbackformat;
$false->id = $DB->insert_record("question_answers", $false);
$feedbacktext = file_save_draft_area_files($feedbackitemid, $question->context->id, 'question', 'answerfeedback', $false->id, self::$fileoptions, $feedbacktext);
$DB->set_field('question_answers', 'feedback', $feedbacktext, array('id'=>$false->id));
}
// delete any leftover old answer records (there couldn't really be any, but who knows)
@ -134,9 +169,9 @@ class question_truefalse_qtype extends default_questiontype {
/**
* Prints the main content of the question including any interactions
*/
function print_question_formulation_and_controls(&$question, &$state,
$cmoptions, $options) {
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG;
$context = $this->get_context_by_category_id($question->category);
$readonly = $options->readonly ? ' disabled="disabled"' : '';
@ -148,7 +183,6 @@ class question_truefalse_qtype extends default_questiontype {
$questiontext = format_text($question->questiontext,
$question->questiontextformat,
$formatoptions, $cmoptions->course);
$image = get_question_image($question);
$answers = &$question->options->answers;
$trueanswer = &$answers[$question->options->trueanswer];
@ -198,12 +232,33 @@ class question_truefalse_qtype extends default_questiontype {
$feedback = '';
if ($options->feedback and isset($answers[$response])) {
$chosenanswer = $answers[$response];
$chosenanswer->feedback = quiz_rewrite_question_urls($chosenanswer->feedback, 'pluginfile.php', $context->id, 'question', 'answerfeedback', array($state->attempt, $state->question), $chosenanswer->id);
$feedback = format_text($chosenanswer->feedback, true, $formatoptions, $cmoptions->course);
}
include("$CFG->dirroot/question/type/truefalse/display.html");
}
function check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args) {
if ($component == 'question' && $filearea == 'answerfeedback') {
$answerid = reset($args); // itemid is answer id.
$answers = &$question->options->answers;
if (isset($state->responses[''])) {
$response = $state->responses[''];
} else {
$response = '';
}
return $options->feedback && isset($answers[$response]) && $answerid == $response;
} else {
return parent::check_file_access($question, $state, $options, $contextid, $component,
$filearea, $args);
}
}
function grade_responses(&$question, &$state, $cmoptions) {
if (isset($state->responses['']) && isset($question->options->answers[$state->responses['']])) {
$state->raw_grade = $question->options->answers[$state->responses['']]->fraction * $question->maxgrade;
@ -363,6 +418,34 @@ class question_truefalse_qtype extends default_questiontype {
return $this->save_question($question, $form, $course);
}
/**
* When move the category of questions, the belonging files should be moved as well
* @param object $question, question information
* @param object $newcategory, target category information
*/
function move_files($question, $newcategory) {
global $DB;
parent::move_files($question, $newcategory);
$fs = get_file_storage();
// process files in answer
if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
}
$component = 'question';
$filearea = 'answerfeedback';
foreach ($oldanswers as $answer) {
$files = $fs->get_area_files($question->contextid, $component, $filearea, $answer->id);
foreach ($files as $storedfile) {
if (!$storedfile->is_directory()) {
$newfile = new object();
$newfile->contextid = (int)$newcategory->contextid;
$fs->create_file_from_storedfile($newfile, $storedfile);
$storedfile->delete();
}
}
}
}
}
//// END OF CLASS ////
@ -370,4 +453,3 @@ class question_truefalse_qtype extends default_questiontype {
//// INITIATION - Without this line the question type is not in use... ///
//////////////////////////////////////////////////////////////////////////
question_register_questiontype(new question_truefalse_qtype());

View file

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