diff --git a/lib/db/install.xml b/lib/db/install.xml
index 04d4fb9024bf0..a3a969749d183 100644
--- a/lib/db/install.xml
+++ b/lib/db/install.xml
@@ -1,5 +1,5 @@
- '.quiz_feedback_for_grade($mygrade, $quiz->id)." '.quiz_feedback_for_grade($mygrade, $quiz, $context, $cm)." '.get_string('modulename', 'quiz') . ': ';
p(format_string($quiz->name));
@@ -219,7 +219,7 @@
}
$number = 1;
echo ' question ".optional_param('multichoice', '', PARAM_RAW)." optional
+
";
}else{
- $text .=get_string('nosharedwildcard', 'qtype_calculated');
+ $text .=get_string('nosharedwildcard', 'qtype_calculated');
}
return $text ;
}
/**
- * This function build a table showing the available category shareable
- * wild cards, their name, their definition (Min, Max, Decimal) , the item count
- * and the name of the question where they are used.
- * This table is intended to be add before the question text to help the user use
- * these wild cards
- */
+ * This function build a table showing the available category shareable
+ * wild cards, their name, their definition (Min, Max, Decimal) , the item count
+ * and the name of the question where they are used.
+ * This table is intended to be add before the question text to help the user use
+ * these wild cards
+ */
function print_dataset_definitions_category_shared($question,$datasetdefsq) {
global $CFG, $DB;
@@ -1942,19 +1930,17 @@ function print_dataset_definitions_category_shared($question,$datasetdefsq) {
$rangeofvaluestr=get_string('minmax','qtype_calculated');
$questionusingstr = get_string('usedinquestion','qtype_calculated');
$itemscountstr = get_string('itemscount','qtype_calculated');
- $text ='';
+ $text ='';
if (!empty($question->category)) {
list($category) = explode(',', $question->category);
$sql = "SELECT i.*,d.*
- FROM {question_datasets} d,
- {question_dataset_definitions} i
- WHERE i.id = d.datasetdefinition
- AND i.category = ?";
+ FROM {question_datasets} d, {question_dataset_definitions} i
+ WHERE i.id = d.datasetdefinition AND i.category = ?";
if ($records = $DB->get_records_sql($sql, array($category))) {
foreach ($records as $r) {
$sql1 = "SELECT q.*
- FROM {question} q
- WHERE q.id = ?";
+ FROM {question} q
+ WHERE q.id = ?";
if ( !isset ($datasetdefs["$r->type-$r->category-$r->name"])){
$datasetdefs["$r->type-$r->category-$r->name"]= $r;
}
@@ -1982,7 +1968,7 @@ function print_dataset_definitions_category_shared($question,$datasetdefsq) {
//limit the name length displayed
if (!empty($qu->name)) {
$qu->name = (strlen($qu->name) > $lnamemax) ?
- substr($qu->name, 0, $lnamemax).'...' : $qu->name;
+ substr($qu->name, 0, $lnamemax).'...' : $qu->name;
} else {
$qu->name = '';
}
@@ -1991,42 +1977,42 @@ function print_dataset_definitions_category_shared($question,$datasetdefsq) {
}
$line++;
$text .="
-
\ No newline at end of file
+
diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php
index 0881eeaf8d879..a7b8d79b839f4 100644
--- a/lib/db/upgrade.php
+++ b/lib/db/upgrade.php
@@ -4911,7 +4911,170 @@ function xmldb_main_upgrade($oldversion) {
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 .= ' ';
+ // 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 .= ' ';
+ // 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;
}
diff --git a/lib/questionlib.php b/lib/questionlib.php
index fbecc814fc8c3..c05efd2f44a9e 100644
--- a/lib/questionlib.php
+++ b/lib/questionlib.php
@@ -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 @@ public function require_one_edit_tab_cap($tabname){
print_error('nopermissions', '', '', 'access question edit tab '.$tabname);
}
}
-}
\ No newline at end of file
+}
+
+/**
+ * Rewrite question url, file_rewrite_pluginfile_urls always build url by
+ * $file/$contextid/$component/$filearea/$itemid/$pathname_in_text, so we cannot add
+ * extra questionid and attempted in url by it, so we create quiz_rewrite_question_urls
+ * to build url here
+ *
+ * @param string $text text being processed
+ * @param string $file the php script used to serve files
+ * @param int $contextid
+ * @param string $component component
+ * @param string $filearea filearea
+ * @param array $ids other IDs will be used to check file permission
+ * @param int $itemid
+ * @param array $options
+ * @return string
+ */
+function quiz_rewrite_question_urls($text, $file, $contextid, $component, $filearea, array $ids, $itemid, array $options=null) {
+ global $CFG;
+
+ $options = (array)$options;
+ if (!isset($options['forcehttps'])) {
+ $options['forcehttps'] = false;
+ }
+
+ if (!$CFG->slasharguments) {
+ $file = $file . '?file=';
+ }
+
+ $baseurl = "$CFG->wwwroot/$file/$contextid/$component/$filearea/";
+
+ if (!empty($ids)) {
+ $baseurl .= (implode('/', $ids) . '/');
+ }
+
+ if ($itemid !== null) {
+ $baseurl .= "$itemid/";
+ }
+
+ if ($options['forcehttps']) {
+ $baseurl = str_replace('http://', 'https://', $baseurl);
+ }
+
+ return str_replace('@@PLUGINFILE@@/', $baseurl, $text);
+}
+
+/**
+ * Called by pluginfile.php to serve files related to the 'question' core
+ * component and for files belonging to qtypes.
+ *
+ * For files that relate to questions in a question_attempt, then we delegate to
+ * a function in the component that owns the attempt (for example in the quiz,
+ * or in core question preview) to get necessary inforation.
+ *
+ * (Note that, at the moment, all question file areas relate to questions in
+ * attempts, so the If at the start of the last paragraph is always true.)
+ *
+ * Does not return, either calls send_file_not_found(); or serves the file.
+ *
+ * @param object $course course settings object
+ * @param object $context context object
+ * @param string $component the name of the component we are serving files for.
+ * @param string $filearea the name of the file area.
+ * @param array $args the remaining bits of the file path.
+ * @param bool $forcedownload whether the user must be forced to download the file.
+ */
+function question_pluginfile($course, $context, $component, $filearea, $args, $forcedownload) {
+ global $DB, $CFG;
+
+ $attemptid = (int)array_shift($args);
+ $questionid = (int)array_shift($args);
+
+ require_login($course, false);
+
+ if ($attemptid === 0) {
+ // preview
+ require_once($CFG->dirroot . '/question/previewlib.php');
+ return question_preview_question_pluginfile($course, $context,
+ $component, $filearea, $attemptid, $questionid, $args, $forcedownload);
+
+ } else {
+ $module = $DB->get_field('question_attempts', 'modulename',
+ array('id' => $attemptid));
+
+ $dir = get_component_directory($module);
+ if (!file_exists("$dir/lib.php")) {
+ send_file_not_found();
+ }
+ include_once("$dir/lib.php");
+
+ $filefunction = $module . '_question_pluginfile';
+ if (!function_exists($filefunction)) {
+ send_file_not_found();
+ }
+
+ $filefunction($course, $context, $component, $filearea, $attemptid, $questionid,
+ $args, $forcedownload);
+
+ send_file_not_found();
+ }
+}
+
+/**
+ * Final test for whether a studnet should be allowed to see a particular file.
+ * This delegates the decision to the question type plugin.
+ *
+ * @param object $question The question to be rendered.
+ * @param object $state The state to render the question in.
+ * @param object $options An object specifying the rendering options.
+ * @param string $component the name of the component we are serving files for.
+ * @param string $filearea the name of the file area.
+ * @param array $args the remaining bits of the file path.
+ * @param bool $forcedownload whether the user must be forced to download the file.
+ */
+function question_check_file_access($question, $state, $options, $contextid, $component,
+ $filearea, $args, $forcedownload) {
+ global $QTYPES;
+ return $QTYPES[$question->qtype]->check_file_access($question, $state, $options, $contextid, $component,
+ $filearea, $args, $forcedownload);
+}
diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php
index 3d4fae405967a..51ce60a775ade 100644
--- a/mod/quiz/attemptlib.php
+++ b/mod/quiz/attemptlib.php
@@ -283,6 +283,10 @@ public function get_access_manager($timenow) {
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 @@ public function is_finished() {
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 @@ public function get_review_options() {
/**
* 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 @@ public function get_question_status($questionid) {
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 @@ public function is_question_flagged($questionid) {
* @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 @@ public function print_question($id, $reviewing, $thispageurl = '') {
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 @@ public function print_question($id, $reviewing, $thispageurl = '') {
$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.
*/
diff --git a/mod/quiz/db/install.xml b/mod/quiz/db/install.xml
index d85a3fec5b89f..2196e070142c3 100755
--- a/mod/quiz/db/install.xml
+++ b/mod/quiz/db/install.xml
@@ -1,5 +1,5 @@
- ';
diff --git a/mod/quiz/version.php b/mod/quiz/version.php
index 42c59baa3ac5d..9cc23d75beab1 100644
--- a/mod/quiz/version.php
+++ b/mod/quiz/version.php
@@ -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)?
diff --git a/mod/quiz/view.php b/mod/quiz/view.php
index e5579164a5b63..f402cbe9af9e9 100644
--- a/mod/quiz/view.php
+++ b/mod/quiz/view.php
@@ -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 .= '' . get_string('feedback', 'quiz') .
' ' . $feedback . ' $qu->name ";
- $nb_of_quiz = 0;
- $nb_of_attempts=0;
- $used_in_quiz = false ;
- if ($list = $DB->get_records('quiz_question_instances', array( 'question'=> $qu->id))){
- $used_in_quiz = true;
- foreach($list as $key => $li){
- $nb_of_quiz ++;
- if($att = $DB->get_records('quiz_attempts',array( 'quiz'=> $li->quiz, 'preview'=> '0'))){
- $nb_of_attempts+= count($att);
+ $nb_of_quiz = 0;
+ $nb_of_attempts=0;
+ $used_in_quiz = false ;
+ if ($list = $DB->get_records('quiz_question_instances', array( 'question'=> $qu->id))){
+ $used_in_quiz = true;
+ foreach($list as $key => $li){
+ $nb_of_quiz ++;
+ if($att = $DB->get_records('quiz_attempts',array( 'quiz'=> $li->quiz, 'preview'=> '0'))){
+ $nb_of_attempts+= count($att);
+ }
}
}
- }
- if($used_in_quiz){
- $text .="$nb_of_quiz ";
- }else {
- $text .="0 ";
- }
- if($used_in_quiz){
- $text .="$nb_of_attempts";
- }else {
- $text .="
";
- }
+ if($used_in_quiz){
+ $text .="$nb_of_quiz ";
+ }else {
+ $text .="0 ";
+ }
+ if($used_in_quiz){
+ $text .="$nb_of_attempts";
+ }else {
+ $text .=" ";
+ $text .="";
}
}
$text .="";
}else{
- $text .=get_string('nosharedwildcard', 'qtype_calculated');
+ $text .=get_string('nosharedwildcard', 'qtype_calculated');
}
return $text ;
}
function find_math_equations($text) {
- /// Returns the possible dataset names found in the text as an array
- /// The array has the dataset name for both key and value
+ /// Returns the possible dataset names found in the text as an array
+ /// The array has the dataset name for both key and value
$equations = array();
/* $qtext = "";
$qtextremaining = $numericalquestion->questiontext ;
@@ -2038,7 +2024,7 @@ function find_math_equations($text) {
if (empty($regs1[1])) {
$str = '';
} else {
-*/
+ */
while (preg_match('~\{=([^[:space:]}]*)}~', $text, $regs)) {
$equations[] = $regs[1];
$text = str_replace($regs[0], '', $text);
@@ -2048,12 +2034,12 @@ function find_math_equations($text) {
function get_virtual_qtype() {
global $QTYPES;
- $this->virtualqtype =& $QTYPES['numerical'];
+ $this->virtualqtype =& $QTYPES['numerical'];
return $this->virtualqtype;
}
-/// BACKUP FUNCTIONS ////////////////////////////
+ /// BACKUP FUNCTIONS ////////////////////////////
/*
* Backup the data in the question
@@ -2105,7 +2091,7 @@ function backup($bf,$preferences,$question,$level=6) {
return $status;
}
-/// RESTORE FUNCTIONS /////////////////
+ /// RESTORE FUNCTIONS /////////////////
/*
* Restores the data in the question
@@ -2154,43 +2140,43 @@ function restore($old_question_id,$new_question_id,$info,$restore) {
}
backup_flush(300);
}
- //Get the calculated_options array
- // need to check as old questions don't have calculated_options record
- if(isset($info['#']['CALCULATED_OPTIONS'])){
- $calculatedoptions = $info['#']['CALCULATED_OPTIONS'];
-
- //Iterate over calculated_options
- for($i = 0; $i < sizeof($calculatedoptions); $i++){
- $cal_info = $calculatedoptions[$i];
- //traverse_xmlize($cal_info); //Debug
- //print_object ($GLOBALS['traverse_array']); //Debug
- //$GLOBALS['traverse_array']=""; //Debug
-
- //Now, build the question_calculated_options record structure
- $calculated_options->questionid = $new_question_id;
- $calculated_options->synchronize = backup_todb($cal_info['#']['SYNCHRONIZE']['0']['#']);
- $calculated_options->single = backup_todb($cal_info['#']['SINGLE']['0']['#']);
- $calculated_options->shuffleanswers = isset($cal_info['#']['SHUFFLEANSWERS']['0']['#'])?backup_todb($mul_info['#']['SHUFFLEANSWERS']['0']['#']):'';
- $calculated_options->correctfeedback = backup_todb($cal_info['#']['CORRECTFEEDBACK']['0']['#']);
- $calculated_options->partiallycorrectfeedback = backup_todb($cal_info['#']['PARTIALLYCORRECTFEEDBACK']['0']['#']);
- $calculated_options->incorrectfeedback = backup_todb($cal_info['#']['INCORRECTFEEDBACK']['0']['#']);
- $calculated_options->answernumbering = backup_todb($cal_info['#']['ANSWERNUMBERING']['0']['#']);
-
- //The structure is equal to the db, so insert the question_calculated_options
- $newid = $DB->insert_record ("question_calculated_options",$calculated_options);
-
- //Do some output
- if (($i+1) % 50 == 0) {
- if (!defined('RESTORE_SILENTLY')) {
- echo ".";
- if (($i+1) % 1000 == 0) {
- echo "
";
+ }
- $text .="
";
+ //Get the calculated_options array
+ // need to check as old questions don't have calculated_options record
+ if(isset($info['#']['CALCULATED_OPTIONS'])){
+ $calculatedoptions = $info['#']['CALCULATED_OPTIONS'];
+
+ //Iterate over calculated_options
+ for($i = 0; $i < sizeof($calculatedoptions); $i++){
+ $cal_info = $calculatedoptions[$i];
+ //traverse_xmlize($cal_info); //Debug
+ //print_object ($GLOBALS['traverse_array']); //Debug
+ //$GLOBALS['traverse_array']=""; //Debug
+
+ //Now, build the question_calculated_options record structure
+ $calculated_options->questionid = $new_question_id;
+ $calculated_options->synchronize = backup_todb($cal_info['#']['SYNCHRONIZE']['0']['#']);
+ $calculated_options->single = backup_todb($cal_info['#']['SINGLE']['0']['#']);
+ $calculated_options->shuffleanswers = isset($cal_info['#']['SHUFFLEANSWERS']['0']['#'])?backup_todb($mul_info['#']['SHUFFLEANSWERS']['0']['#']):'';
+ $calculated_options->correctfeedback = backup_todb($cal_info['#']['CORRECTFEEDBACK']['0']['#']);
+ $calculated_options->partiallycorrectfeedback = backup_todb($cal_info['#']['PARTIALLYCORRECTFEEDBACK']['0']['#']);
+ $calculated_options->incorrectfeedback = backup_todb($cal_info['#']['INCORRECTFEEDBACK']['0']['#']);
+ $calculated_options->answernumbering = backup_todb($cal_info['#']['ANSWERNUMBERING']['0']['#']);
+
+ //The structure is equal to the db, so insert the question_calculated_options
+ $newid = $DB->insert_record ("question_calculated_options",$calculated_options);
+
+ //Do some output
+ if (($i+1) % 50 == 0) {
+ if (!defined('RESTORE_SILENTLY')) {
+ echo ".";
+ if (($i+1) % 1000 == 0) {
+ echo "
";
+ }
}
+ backup_flush(300);
}
- backup_flush(300);
}
}
- }
//Now restore numerical_units
$status = question_restore_numerical_units ($old_question_id,$new_question_id,$cal_info,$restore);
$status = question_restore_numerical_options($old_question_id,$new_question_id,$info,$restore);
@@ -2207,53 +2193,125 @@ function restore($old_question_id,$new_question_id,$info,$restore) {
return $status;
}
- /**
- * Runs all the code required to set up and save an essay question for testing purposes.
- * Alternate DB table prefix may be used to facilitate data deletion.
- */
- function generate_test($name, $courseid = null) {
- global $DB;
- list($form, $question) = parent::generate_test($name, $courseid);
- $form->feedback = 1;
- $form->multiplier = array(1, 1);
- $form->shuffleanswers = 1;
- $form->noanswers = 1;
- $form->qtype ='calculated';
- $question->qtype ='calculated';
- $form->answers = array('{a} + {b}');
- $form->fraction = array(1);
- $form->tolerance = array(0.01);
- $form->tolerancetype = array(1);
- $form->correctanswerlength = array(2);
- $form->correctanswerformat = array(1);
- $form->questiontext = "What is {a} + {b}?";
-
- if ($courseid) {
- $course = $DB->get_record('course', array('id'=> $courseid));
- }
-
- $new_question = $this->save_question($question, $form, $course);
-
- $dataset_form = new stdClass();
- $dataset_form->nextpageparam["forceregeneration"]= 1;
- $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
- $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
- $dataset_form->calclength = array(1 => 1, 2 => 1);
- $dataset_form->number = array(1 => 5.4 , 2 => 4.9);
- $dataset_form->itemid = array(1 => '' , 2 => '');
- $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
- $dataset_form->definition = array(1 => "1-0-a",
- 2 => "1-0-b");
- $dataset_form->nextpageparam = array('forceregeneration' => false);
- $dataset_form->addbutton = 1;
- $dataset_form->selectadd = 1;
- $dataset_form->courseid = $courseid;
- $dataset_form->cmid = 0;
- $dataset_form->id = $new_question->id;
- $this->save_dataset_items($new_question, $dataset_form);
-
- return $new_question;
- }
+ /**
+ * Runs all the code required to set up and save an essay question for testing purposes.
+ * Alternate DB table prefix may be used to facilitate data deletion.
+ */
+ function generate_test($name, $courseid = null) {
+ global $DB;
+ list($form, $question) = parent::generate_test($name, $courseid);
+ $form->feedback = 1;
+ $form->multiplier = array(1, 1);
+ $form->shuffleanswers = 1;
+ $form->noanswers = 1;
+ $form->qtype ='calculated';
+ $question->qtype ='calculated';
+ $form->answers = array('{a} + {b}');
+ $form->fraction = array(1);
+ $form->tolerance = array(0.01);
+ $form->tolerancetype = array(1);
+ $form->correctanswerlength = array(2);
+ $form->correctanswerformat = array(1);
+ $form->questiontext = "What is {a} + {b}?";
+
+ if ($courseid) {
+ $course = $DB->get_record('course', array('id'=> $courseid));
+ }
+
+ $new_question = $this->save_question($question, $form, $course);
+
+ $dataset_form = new stdClass();
+ $dataset_form->nextpageparam["forceregeneration"]= 1;
+ $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
+ $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
+ $dataset_form->calclength = array(1 => 1, 2 => 1);
+ $dataset_form->number = array(1 => 5.4 , 2 => 4.9);
+ $dataset_form->itemid = array(1 => '' , 2 => '');
+ $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
+ $dataset_form->definition = array(1 => "1-0-a",
+ 2 => "1-0-b");
+ $dataset_form->nextpageparam = array('forceregeneration' => false);
+ $dataset_form->addbutton = 1;
+ $dataset_form->selectadd = 1;
+ $dataset_form->courseid = $courseid;
+ $dataset_form->cmid = 0;
+ $dataset_form->id = $new_question->id;
+ $this->save_dataset_items($new_question, $dataset_form);
+
+ return $new_question;
+ }
+
+ /**
+ * When move the category of questions, the belonging files should be moved as well
+ * @param object $question, question information
+ * @param object $newcategory, target category information
+ */
+ function move_files($question, $newcategory) {
+ global $DB;
+ parent::move_files($question, $newcategory);
+
+ $fs = get_file_storage();
+ // process files in answer
+ if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) {
+ $oldanswers = array();
+ }
+ $component = 'question';
+ $filearea = 'answerfeedback';
+ foreach ($oldanswers as $answer) {
+ $files = $fs->get_area_files($question->contextid, $component, $filearea, $answer->id);
+ foreach ($files as $storedfile) {
+ if (!$storedfile->is_directory()) {
+ $newfile = new object();
+ $newfile->contextid = (int)$newcategory->contextid;
+ $fs->create_file_from_storedfile($newfile, $storedfile);
+ $storedfile->delete();
+ }
+ }
+ }
+ $component = 'qtype_numerical';
+ $filearea = 'instruction';
+ $files = $fs->get_area_files($question->contextid, $component, $filearea, $question->id);
+ foreach ($files as $storedfile) {
+ if (!$storedfile->is_directory()) {
+ $newfile = new object();
+ $newfile->contextid = (int)$newcategory->contextid;
+ $fs->create_file_from_storedfile($newfile, $storedfile);
+ $storedfile->delete();
+ }
+ }
+ }
+
+ function check_file_access($question, $state, $options, $contextid, $component,
+ $filearea, $args) {
+ $itemid = reset($args);
+ if ($component == 'question' && $filearea == 'answerfeedback') {
+
+ // check if answer id exists
+ $result = $options->feedback && array_key_exists($itemid, $question->options->answers);
+ if (!$result) {
+ return false;
+ }
+ // check response
+ if (!$this->check_response($question, $state)) {
+ return false;
+ }
+ return true;
+ } else if ($filearea == 'instruction') {
+ // TODO: should it be display all the time like questiontext?
+ // check if question id exists
+ if ($itemid != $question->id) {
+ return false;
+ } else {
+ return true;
+ }
+ } else if (in_array($filearea, array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'))) {
+ // TODO: calculated type doesn't display question feedback yet
+ return false;
+ } else {
+ return parent::check_file_access($question, $state, $options, $contextid, $component,
+ $filearea, $args);
+ }
+ }
}
//// END OF CLASS ////
@@ -2267,11 +2325,11 @@ function generate_test($name, $courseid = null) {
}
function qtype_calculated_calculate_answer($formula, $individualdata,
- $tolerance, $tolerancetype, $answerlength, $answerformat='1', $unit='') {
-/// The return value has these properties:
-/// ->answer the correct answer
-/// ->min the lower bound for an acceptable response
-/// ->max the upper bound for an accetpable response
+ $tolerance, $tolerancetype, $answerlength, $answerformat='1', $unit='') {
+ /// The return value has these properties:
+ /// ->answer the correct answer
+ /// ->min the lower bound for an acceptable response
+ /// ->max the upper bound for an accetpable response
/// Exchange formula variables with the correct values...
global $QTYPES;
@@ -2290,12 +2348,12 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
if (preg_match('~^(.*\\.)(.*)$~', $calculated->answer, $regs)) {
$calculated->answer = $regs[1] . substr(
- $regs[2] . '00000000000000000000000000000000000000000x',
- 0, $answerlength)
- . $unit;
+ $regs[2] . '00000000000000000000000000000000000000000x',
+ 0, $answerlength)
+ . $unit;
} else {
$calculated->answer .=
- substr('.00000000000000000000000000000000000000000x',
+ substr('.00000000000000000000000000000000000000000x',
0, $answerlength + 1) . $unit;
}
} else {
@@ -2337,9 +2395,9 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
} else {
// Attach additional zeros at the end of $answer,
$answer .= (1==strlen($answer) ? '.' : '')
- . '00000000000000000000000000000000000000000x';
+ . '00000000000000000000000000000000000000000x';
$calculated->answer = $sign
- .substr($answer, 0, $answerlength +1).$eX.$unit;
+ .substr($answer, 0, $answerlength +1).$eX.$unit;
}
} else {
// Stick to plain numeric format
@@ -2349,7 +2407,7 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
} else {
// Could be an idea to add some zeros here
$answer .= (preg_match('~^[0-9]*$~', $answer) ? '.' : '')
- . '00000000000000000000000000000000000000000x';
+ . '00000000000000000000000000000000000000000x';
$oklen = $answerlength + ($p10 < 1 ? 2-$p10 : 1);
$calculated->answer = $sign.substr($answer, 0, $oklen).$unit;
}
@@ -2365,9 +2423,9 @@ function qtype_calculated_calculate_answer($formula, $individualdata,
function qtype_calculated_find_formula_errors($formula) {
-/// Validates the formula submitted from the question edit page.
-/// Returns false if everything is alright.
-/// Otherwise it constructs an error message
+ /// Validates the formula submitted from the question edit page.
+ /// Returns false if everything is alright.
+ /// Otherwise it constructs an error message
// Strip away dataset names
while (preg_match('~\\{[[:alpha:]][^>} <{"\']*\\}~', $formula, $regs)) {
$formula = str_replace($regs[0], '1', $formula);
@@ -2380,59 +2438,58 @@ function qtype_calculated_find_formula_errors($formula) {
$operatorornumber = "[$safeoperatorchar.0-9eE]";
while ( preg_match("~(^|[$safeoperatorchar,(])([a-z0-9_]*)\\(($operatorornumber+(,$operatorornumber+((,$operatorornumber+)+)?)?)?\\)~",
- $formula, $regs)) {
-
+ $formula, $regs)) {
switch ($regs[2]) {
// Simple parenthesis
- case '':
- if ($regs[4] || strlen($regs[3])==0) {
- return get_string('illegalformulasyntax', 'quiz', $regs[0]);
- }
- break;
+ case '':
+ if ($regs[4] || strlen($regs[3])==0) {
+ return get_string('illegalformulasyntax', 'quiz', $regs[0]);
+ }
+ break;
// Zero argument functions
- case 'pi':
- if ($regs[3]) {
- return get_string('functiontakesnoargs', 'quiz', $regs[2]);
- }
- break;
+ case 'pi':
+ if ($regs[3]) {
+ return get_string('functiontakesnoargs', 'quiz', $regs[2]);
+ }
+ break;
// Single argument functions (the most common case)
- case 'abs': case 'acos': case 'acosh': case 'asin': case 'asinh':
- case 'atan': case 'atanh': case 'bindec': case 'ceil': case 'cos':
- case 'cosh': case 'decbin': case 'decoct': case 'deg2rad':
- case 'exp': case 'expm1': case 'floor': case 'is_finite':
- case 'is_infinite': case 'is_nan': case 'log10': case 'log1p':
- case 'octdec': case 'rad2deg': case 'sin': case 'sinh': case 'sqrt':
- case 'tan': case 'tanh':
- if (!empty($regs[4]) || empty($regs[3])) {
- return get_string('functiontakesonearg','quiz',$regs[2]);
- }
- break;
+ case 'abs': case 'acos': case 'acosh': case 'asin': case 'asinh':
+ case 'atan': case 'atanh': case 'bindec': case 'ceil': case 'cos':
+ case 'cosh': case 'decbin': case 'decoct': case 'deg2rad':
+ case 'exp': case 'expm1': case 'floor': case 'is_finite':
+ case 'is_infinite': case 'is_nan': case 'log10': case 'log1p':
+ case 'octdec': case 'rad2deg': case 'sin': case 'sinh': case 'sqrt':
+ case 'tan': case 'tanh':
+ if (!empty($regs[4]) || empty($regs[3])) {
+ return get_string('functiontakesonearg','quiz',$regs[2]);
+ }
+ break;
// Functions that take one or two arguments
- case 'log': case 'round':
- if (!empty($regs[5]) || empty($regs[3])) {
- return get_string('functiontakesoneortwoargs','quiz',$regs[2]);
- }
- break;
+ case 'log': case 'round':
+ if (!empty($regs[5]) || empty($regs[3])) {
+ return get_string('functiontakesoneortwoargs','quiz',$regs[2]);
+ }
+ break;
// Functions that must have two arguments
- case 'atan2': case 'fmod': case 'pow':
- if (!empty($regs[5]) || empty($regs[4])) {
- return get_string('functiontakestwoargs', 'quiz', $regs[2]);
- }
- break;
+ case 'atan2': case 'fmod': case 'pow':
+ if (!empty($regs[5]) || empty($regs[4])) {
+ return get_string('functiontakestwoargs', 'quiz', $regs[2]);
+ }
+ break;
// Functions that take two or more arguments
- case 'min': case 'max':
- if (empty($regs[4])) {
- return get_string('functiontakesatleasttwo','quiz',$regs[2]);
- }
- break;
+ case 'min': case 'max':
+ if (empty($regs[4])) {
+ return get_string('functiontakesatleasttwo','quiz',$regs[2]);
+ }
+ break;
- default:
- return get_string('unsupportedformulafunction','quiz',$regs[2]);
+ default:
+ return get_string('unsupportedformulafunction','quiz',$regs[2]);
}
// Exchange the function call with '1' and then chack for
@@ -2452,13 +2509,4 @@ function qtype_calculated_find_formula_errors($formula) {
// Formula just might be valid
return false;
}
-
}
-
-function dump($obj) {
- echo "\n";
- var_dump($obj);
- echo "
\n";
-}
-
-
diff --git a/question/type/calculated/version.php b/question/type/calculated/version.php
index aa56eb9470c80..6b690e75cf360 100644
--- a/question/type/calculated/version.php
+++ b/question/type/calculated/version.php
@@ -1,5 +1,5 @@
version = 2010020800;
+$plugin->version = 2010020801;
$plugin->requires = 2007101000;
diff --git a/question/type/calculatedmulti/edit_calculatedmulti_form.php b/question/type/calculatedmulti/edit_calculatedmulti_form.php
index 3b81121d8cb0e..a2e736f21ae0f 100644
--- a/question/type/calculatedmulti/edit_calculatedmulti_form.php
+++ b/question/type/calculatedmulti/edit_calculatedmulti_form.php
@@ -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 @@ function question_edit_calculatedmulti_form(&$submiturl, &$question, &$category,
$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 @@ function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions
$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 @@ function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions
$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('...{={x}+..}...');
+ $repeated[1]->setLabel('...{={x}+..}...');
return $repeated;
}
@@ -85,79 +84,78 @@ function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions
function definition_inner(&$mform) {
global $QTYPES;
$this->qtypeobj =& $QTYPES[$this->qtype()];
- // echo code left for testing period
- // echo "";print_r($this->question);echo "
question ".optional_param('multichoice', '', PARAM_RAW)." optional
";print_r($this->question);echo ""; $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 @@ function definition_inner(&$mform) { } $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 @@ function set_data($question) { } } 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 @@ function set_data($question) { $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 @@ function qtype() { } function validation($data, $files) { - // echo code left for testing period - - // echo "
question
";print_r($this->question);echo ""; - // echo "
data
";print_r($data);echo ""; + // echo code left for testing period + // echo "
question
";print_r($this->question);echo ""; + // echo "
data
";print_r($data);echo ""; $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
";print_r($possibledatasets); - while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { + // echo "numericalquestion qtextremaining";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 @@ function validation($data, $files) { $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 @@ function validation($data, $files) { 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";print_r($possibledatasets); + // echo "numericalquestion qanswerremaining";print_r($possibledatasets); while (preg_match('~\{=([^[:space:]}]*)}~', $qanswerremaining, $regs1)) { $qanswersplits = explode($regs1[0], $qanswerremaining, 2); $qanswer =$qanswer.$qanswersplits[0]; @@ -320,16 +340,16 @@ function validation($data, $files) { } 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 @@ function validation($data, $files) { 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 @@ function validation($data, $files) { $errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction); } } - - + if ($answercount==0){ $errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated'); } if ($maxgrade == false) { $errors['fraction[0]'] = get_string('fractionsnomax', 'question'); } - + } return $errors; } } - diff --git a/question/type/calculatedmulti/lib.php b/question/type/calculatedmulti/lib.php new file mode 100644 index 0000000000000..220f828fa011a --- /dev/null +++ b/question/type/calculatedmulti/lib.php @@ -0,0 +1,31 @@ +. + +/** + * Serve question type files + * + * @since 2.0 + * @package questionbank + * @subpackage questiontypes + * @author Dongsheng Cai+ * @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); +} diff --git a/question/type/calculatedmulti/questiontype.php b/question/type/calculatedmulti/questiontype.php index 0d878988a062e..c648899e3abb1 100644 --- a/question/type/calculatedmulti/questiontype.php +++ b/question/type/calculatedmulti/questiontype.php @@ -1,17 +1,30 @@ . + ///////////////// // 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 @@ function requires_qtypes() { 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 @@ function save_question_options($question) { $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 @@ function save_question_options($question) { } // 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 @@ function save_question_options($question) { } // 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 @@ function save_question_options($question) { $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 @@ function save_question_options($question) { 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 @@ function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) } } 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 @@ function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) 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 @@ function create_runtime_question($question, $form) { 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 @@ function create_runtime_question($question, $form) { return $question; } - - - - function convert_answers (&$question, &$state){ - foreach ($question->options->answers as $key => $answer) { - $answer->answer = $this->substitute_variables($answer->answer, $state->options->dataset); - //evaluate the equations i.e {=5+4) - $qtext = ""; - $qtextremaining = $answer->answer ; - // while (preg_match('~\{(=)|%[[:digit]]\.=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { - while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { - - $qtextsplits = explode($regs1[0], $qtextremaining, 2); - $qtext =$qtext.$qtextsplits[0]; - $qtextremaining = $qtextsplits[1]; - if (empty($regs1[1])) { - $str = ''; - } else { - if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){ - $str=$formulaerrors ; - }else { - eval('$str = '.$regs1[1].';'); - $texteval= qtype_calculated_calculate_answer( - $str, $state->options->dataset, $answer->tolerance, - $answer->tolerancetype, $answer->correctanswerlength, - $answer->correctanswerformat, ''); + foreach ($question->options->answers as $key => $answer) { + $answer->answer = $this->substitute_variables($answer->answer, $state->options->dataset); + //evaluate the equations i.e {=5+4) + $qtext = ""; + $qtextremaining = $answer->answer ; + // while (preg_match('~\{(=)|%[[:digit]]\.=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { + while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { + + $qtextsplits = explode($regs1[0], $qtextremaining, 2); + $qtext = $qtext.$qtextsplits[0]; + $qtextremaining = $qtextsplits[1]; + if (empty($regs1[1])) { + $str = ''; + } else { + if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){ + $str=$formulaerrors ; + }else { + eval('$str = '.$regs1[1].';'); + $texteval= qtype_calculated_calculate_answer( + $str, $state->options->dataset, $answer->tolerance, + $answer->tolerancetype, $answer->correctanswerlength, + $answer->correctanswerformat, ''); $str = $texteval->answer; - } - } - $qtext = $qtext.$str ; + } } - $answer->answer = $qtext.$qtextremaining ; ; + $qtext = $qtext.$str ; } + $answer->answer = $qtext.$qtextremaining ; ; } + } - function get_default_numerical_unit($question,$virtualqtype){ - $unit = ''; - return $unit ; - } + function get_default_numerical_unit($question, $virtualqtype){ + $unit = ''; + return $unit ; + } function grade_responses(&$question, &$state, $cmoptions) { // Forward the grading to the virtual qtype // We modify the question to look like a multichoice question - // for grading nothing to do + // for grading nothing to do /* $numericalquestion = fullclone($question); foreach ($numericalquestion->options->answers as $key => $answer) { $answer = $numericalquestion->options->answers[$key]->answer; // for PHP 4.x $numericalquestion->options->answers[$key]->answer = $this->substitute_variables_and_eval($answer, $state->options->dataset); - }*/ - $virtualqtype = $this->get_virtual_qtype( $question); +}*/ + $virtualqtype = $this->get_virtual_qtype( $question); return $virtualqtype->grade_responses($question, $state, $cmoptions) ; } @@ -319,7 +327,7 @@ function get_actual_response(&$question, &$state) { $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 @@ function get_actual_response(&$question, &$state) { 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 @@ function comment_header($question) { } else { $strheader .= $delimiter.$answer->answer; } - $delimiter = '
'; + $delimiter = '
'; } return $strheader; } @@ -369,7 +377,7 @@ function comment_on_datasetitems($qtypeobj,$questionid,$questiontext, $answers,$ $unit = $unit->unit; } else { $unit = ''; - }*/ + }*/ $answers = fullclone($answers); $strmin = get_string('min', 'quiz'); @@ -377,29 +385,29 @@ function comment_on_datasetitems($qtypeobj,$questionid,$questiontext, $answers,$ $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 @@ function comment_on_datasetitems($qtypeobj,$questionid,$questiontext, $answers,$ }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".'
'; @@ -432,15 +440,11 @@ function comment_on_datasetitems($qtypeobj,$questionid,$questiontext, $answers,$ $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 @@ function get_correct_responses1(&$question, &$state) { return $correct; } } - }else{**/ - return $virtualqtype->get_correct_responses($question, $state) ; - // } + }else{**/ + return $virtualqtype->get_correct_responses($question, $state) ; + // } return null; } function get_virtual_qtype() { global $QTYPES; - // if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){ - $this->virtualqtype =& $QTYPES['multichoice']; - // }else { - // $this->virtualqtype =& $QTYPES['numerical']; - // } + // if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){ + $this->virtualqtype =& $QTYPES['multichoice']; + // }else { + // $this->virtualqtype =& $QTYPES['numerical']; + // } return $this->virtualqtype; } - /** - * Runs all the code required to set up and save an essay question for testing purposes. - * Alternate DB table prefix may be used to facilitate data deletion. - */ - function generate_test($name, $courseid = null) { - global $DB; - list($form, $question) = parent::generate_test($name, $courseid); - $form->feedback = 1; - $form->multiplier = array(1, 1); - $form->shuffleanswers = 1; - $form->noanswers = 1; - $form->qtype ='calculatedmulti'; - $question->qtype ='calculatedmulti'; - $form->answers = array('{a} + {b}'); - $form->fraction = array(1); - $form->tolerance = array(0.01); - $form->tolerancetype = array(1); - $form->correctanswerlength = array(2); - $form->correctanswerformat = array(1); - $form->questiontext = "What is {a} + {b}?"; - - if ($courseid) { - $course = $DB->get_record('course', array('id'=> $courseid)); - } - - $new_question = $this->save_question($question, $form, $course); - - $dataset_form = new stdClass(); - $dataset_form->nextpageparam["forceregeneration"]= 1; - $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0); - $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0); - $dataset_form->calclength = array(1 => 1, 2 => 1); - $dataset_form->number = array(1 => 5.4 , 2 => 4.9); - $dataset_form->itemid = array(1 => '' , 2 => ''); - $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform'); - $dataset_form->definition = array(1 => "1-0-a", - 2 => "1-0-b"); - $dataset_form->nextpageparam = array('forceregeneration' => false); - $dataset_form->addbutton = 1; - $dataset_form->selectadd = 1; - $dataset_form->courseid = $courseid; - $dataset_form->cmid = 0; - $dataset_form->id = $new_question->id; - $this->save_dataset_items($new_question, $dataset_form); - - return $new_question; - } + /** + * Runs all the code required to set up and save an essay question for testing purposes. + * Alternate DB table prefix may be used to facilitate data deletion. + */ + function generate_test($name, $courseid = null) { + global $DB; + list($form, $question) = parent::generate_test($name, $courseid); + $form->feedback = 1; + $form->multiplier = array(1, 1); + $form->shuffleanswers = 1; + $form->noanswers = 1; + $form->qtype ='calculatedmulti'; + $question->qtype ='calculatedmulti'; + $form->answers = array('{a} + {b}'); + $form->fraction = array(1); + $form->tolerance = array(0.01); + $form->tolerancetype = array(1); + $form->correctanswerlength = array(2); + $form->correctanswerformat = array(1); + $form->questiontext = "What is {a} + {b}?"; + + if ($courseid) { + $course = $DB->get_record('course', array('id'=> $courseid)); + } + + $new_question = $this->save_question($question, $form, $course); + + $dataset_form = new stdClass(); + $dataset_form->nextpageparam["forceregeneration"]= 1; + $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0); + $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0); + $dataset_form->calclength = array(1 => 1, 2 => 1); + $dataset_form->number = array(1 => 5.4 , 2 => 4.9); + $dataset_form->itemid = array(1 => '' , 2 => ''); + $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform'); + $dataset_form->definition = array(1 => "1-0-a", + 2 => "1-0-b"); + $dataset_form->nextpageparam = array('forceregeneration' => false); + $dataset_form->addbutton = 1; + $dataset_form->selectadd = 1; + $dataset_form->courseid = $courseid; + $dataset_form->cmid = 0; + $dataset_form->id = $new_question->id; + $this->save_dataset_items($new_question, $dataset_form); + + return $new_question; + } + + /** + * When move the category of questions, the belonging files should be moved as well + * @param object $question, question information + * @param object $newcategory, target category information + */ + function move_files($question, $newcategory) { + global $DB; + // move files belonging to question component + parent::move_files($question, $newcategory); + + // move files belonging to qtype_calculatedmulti + $fs = get_file_storage(); + // process files in answer + if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) { + $oldanswers = array(); + } + $component = 'question'; + $filearea = 'answerfeedback'; + foreach ($oldanswers as $answer) { + $files = $fs->get_area_files($question->contextid, $component, $filearea, $answer->id); + foreach ($files as $storedfile) { + if (!$storedfile->is_directory()) { + $newfile = new object(); + $newfile->contextid = (int)$newcategory->contextid; + $fs->create_file_from_storedfile($newfile, $storedfile); + $storedfile->delete(); + } + } + } + + $component = 'qtype_calculatedmulti'; + foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $filearea) { + $files = $fs->get_area_files($question->contextid, $component, $filearea, $question->id); + foreach ($files as $storedfile) { + if (!$storedfile->is_directory()) { + $newfile = new object(); + $newfile->contextid = (int)$newcategory->contextid; + $fs->create_file_from_storedfile($newfile, $storedfile); + $storedfile->delete(); + } + } + } + } + + function check_file_access($question, $state, $options, $contextid, $component, + $filearea, $args) { + $itemid = reset($args); + + if (empty($question->maxgrade)) { + $question->maxgrade = $question->defaultgrade; + } + + if (in_array($filearea, array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'))) { + $result = $options->feedback && ($itemid == $question->id); + if (!$result) { + return false; + } + if ($state->raw_grade >= $question->maxgrade/1.01) { + $feedbacktype = 'correctfeedback'; + } else if ($state->raw_grade > 0) { + $feedbacktype = 'partiallycorrectfeedback'; + } else { + $feedbacktype = 'incorrectfeedback'; + } + if ($feedbacktype != $filearea) { + return false; + } + return true; + } else if ($component == 'question' && $filearea == 'answerfeedback') { + return $options->feedback && (array_key_exists($itemid, $question->options->answers)); + } else { + return parent::check_file_access($question, $state, $options, $contextid, $component, + $filearea, $args); + } + } } + //// END OF CLASS //// ////////////////////////////////////////////////////////////////////////// diff --git a/question/type/calculatedsimple/edit_calculatedsimple_form.php b/question/type/calculatedsimple/edit_calculatedsimple_form.php index f63b72cdc9de5..8c84517e4b7a3 100644 --- a/question/type/calculatedsimple/edit_calculatedsimple_form.php +++ b/question/type/calculatedsimple/edit_calculatedsimple_form.php @@ -1,4 +1,20 @@ . + /** * 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 @@ function question_edit_calculatedsimple_form(&$submiturl, &$question, &$category //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 "question
";print_r($question);echo ""; - if ( !empty($question->id)) { - + if (!empty($question->id)) { + /* if (empty($question->options)) { $this->get_question_options($question); }*/ @@ -127,7 +137,7 @@ function question_edit_calculatedsimple_form(&$submiturl, &$question, &$category $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 @@ function definition_inner(&$mform) { $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 @@ function definition_inner(&$mform) { $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 @@ function definition_inner(&$mform) { } //this should be done before the elements are created and stored as $this->formdata ; //fill out all data sets and also the fields for the next item to add. - /*Here we do already the values error analysis so that - * we could force all wild cards values display if there is an error in values. - * as using a , in a number */ - $this->numbererrors = array(); + /*Here we do already the values error analysis so that + * we could force all wild cards values display if there is an error in values. + * as using a , in a number */ + $this->numbererrors = array(); if(!empty($this->datasetdefs)){ - $j = $this->noofitems * count($this->datasetdefs); - for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){ - $data = array(); - $numbererrors = array() ; - $comment = new stdClass; - $comment->stranswers = array(); - $comment->outsidelimit = false ; - $comment->answers = array(); - - foreach ($this->datasetdefs as $defid => $datasetdef){ - if (isset($datasetdef->items[$itemnumber])){ - $this->formdata["definition[$j]"] = $defid; - $this->formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id; - $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value; - $this->formdata["number[$j]"] = $number = $datasetdef->items[$itemnumber]->value; - if(! is_numeric($number)){ - $a = new stdClass; - $a->name = '{'.$datasetdef->name.'}' ; - $a->value = $datasetdef->items[$itemnumber]->value ; - if (stristr($number,',')){ + $j = $this->noofitems * count($this->datasetdefs); + for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){ + $data = array(); + $numbererrors = array() ; + $comment = new stdClass; + $comment->stranswers = array(); + $comment->outsidelimit = false ; + $comment->answers = array(); + + foreach ($this->datasetdefs as $defid => $datasetdef){ + if (isset($datasetdef->items[$itemnumber])){ + $this->formdata["definition[$j]"] = $defid; + $this->formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id; + $data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value; + $this->formdata["number[$j]"] = $number = $datasetdef->items[$itemnumber]->value; + if(! is_numeric($number)){ + $a = new stdClass; + $a->name = '{'.$datasetdef->name.'}' ; + $a->value = $datasetdef->items[$itemnumber]->value ; + if (stristr($number,',')){ $this->numbererrors["number[$j]"]=get_string('nocommaallowed', 'qtype_calculated'); - $numbererrors .= $this->numbererrors['number['.$j.']']."
"; + $numbererrors .= $this->numbererrors['number['.$j.']']."
"; - }else { + }else { $this->numbererrors["number[$j]"]= get_string('notvalidnumber','qtype_calculated',$a); $numbererrors .= $this->numbererrors['number['.$j.']']."
"; //$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.']']."
"; - } 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.']']."
"; - // $val = 1.0 ; - } + } + }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.']']."
"; + } 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.']']."
"; + // $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].'
'; - } - - $this->formdata['answercomment['.$itemnumber.']'] = $totalcomment ; - } + $j--; + } + 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].'
'; + } + + $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 + } + $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','',''.get_string('youmustaddatleastonevalue', 'qtype_calculatedsimple').''); + $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", '', ''.get_string('useadvance', 'qtype_calculatedsimple').''); } + $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','',''.get_string('youmustaddatleastonevalue', 'qtype_calculatedsimple').''); - $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", '', ''.get_string('useadvance', 'qtype_calculatedsimple').''); - } + //------------------------------------------------------------------------------------------------------------------------------ + $j = $this->noofitems * count($this->datasetdefs); + $k = 1 ; + if ("" != optional_param('selectshow')){ + $k = optional_param('selectshow', '', PARAM_INT); + } - $mform->addElement('submit', 'updatedatasets', get_string('updatewildcardvalues', 'qtype_calculatedsimple')); - $mform->registerNoSubmitButton('updatedatasets'); - $mform->setAdvanced("updatedatasets",true); + 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]",'',''.$this->numbererrors['number['.$j.']'].''); + $mform->setAdvanced("numbercomment[$j]",true); + } + }else { + $mform->addElement('hidden',"number[$j]" , get_string('wildcard', 'qtype_calculatedsimple', $datasetdef->name)); + } + $mform->setType("number[$j]", PARAM_NUMBER); -//------------------------------------------------------------------------------------------------------------------------------ - $j = $this->noofitems * count($this->datasetdefs); - $k = 1 ; - if ("" != optional_param('selectshow')){ - $k = optional_param('selectshow', '', PARAM_INT); - } + $mform->addElement('hidden', "itemid[$j]"); + $mform->setType("itemid[$j]", 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]",'',''.$this->numbererrors['number['.$j.']'].''); - $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); + $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]", "".get_string('setno', 'qtype_calculatedsimple', $i)." ".$strquestionlabel); + $j--; + } + if (!empty( $strquestionlabel) && ($k > 0 || $this->outsidelimit || !empty($this->numbererrors ) ) ){ + // $repeated[] =& $mform->addElement('static', "answercomment[$i]", $strquestionlabel); + $mform->addElement('static', "answercomment[$i]", "".get_string('setno', 'qtype_calculatedsimple', $i)." ".$strquestionlabel); - } - if($k > 0 || $this->outsidelimit || !empty($this->numbererrors )){ - $mform->addElement('static', "divider1[$j]", '', '
'); + } + if($k > 0 || $this->outsidelimit || !empty($this->numbererrors )){ + $mform->addElement('static', "divider1[$j]", '', '
'); - } - $k-- ; + } + $k-- ; + } + } + // if ($this->outsidelimit){ + // $mform->addElement('static','outsidelimit','',''); + // } + }else { + $mform->addElement('static','warningnowildcards','',''.get_string('atleastonewildcard', 'qtype_calculatedsimple').''); + $mform->closeHeaderBefore('warningnowildcards'); } - } - // if ($this->outsidelimit){ - // $mform->addElement('static','outsidelimit','',''); - // } - }else { - $mform->addElement('static','warningnowildcards','',''.get_string('atleastonewildcard', 'qtype_calculatedsimple').''); - $mform->closeHeaderBefore('warningnowildcards'); - } -//------------------------------------------------------------------------------------------------------------------------------ + //------------------------------------------------------------------------------------------------------------------------------ //non standard name for button element needed so not using add_action_buttons //hidden elements @@ -536,50 +549,64 @@ function definition_inner(&$mform) { $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; - $key++; - } + $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; + + // 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 @@ function set_data($question) { } } } - */ - $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 @@ function set_data($question) { } $j--; } - // echo "answers avant comment
";print_r($answer);echo""; - // echo "data avant comment
";print_r($data);echo""; + // echo "answers avant comment
";print_r($answer);echo""; + // echo "data avant comment
";print_r($data);echo""; 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 "comment
";print_r($comment);echo""; + $comment = $this->qtypeobj->comment_on_datasetitems($question->id,$this->nonemptyanswer, $data, $itemnumber);//$this-> + if ($comment->outsidelimit) { + $this->outsidelimit=$comment->outsidelimit ; + } + $totalcomment=''; + // echo "comment
";print_r($comment);echo""; - foreach ($this->nonemptyanswer as $key => $answer) { - $totalcomment .= $comment->stranswers[$key].'
'; - } + foreach ($this->nonemptyanswer as $key => $answer) { + $totalcomment .= $comment->stranswers[$key].'
'; + } - $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 @@ function set_data($question) { $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 @@ function validation($data, $files) { $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 @@ function validation($data, $files) { $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 @@ function validation($data, $files) { return $errors; } } - diff --git a/question/type/calculatedsimple/lib.php b/question/type/calculatedsimple/lib.php new file mode 100644 index 0000000000000..23a1770a58346 --- /dev/null +++ b/question/type/calculatedsimple/lib.php @@ -0,0 +1,31 @@ +. + +/** + * Serve question type files + * + * @since 2.0 + * @package questionbank + * @subpackage questiontypes + * @author Dongsheng Cai+ * @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); +} diff --git a/question/type/calculatedsimple/questiontype.php b/question/type/calculatedsimple/questiontype.php index 91bcd9a40bf6f..bb6845e8ebb9a 100644 --- a/question/type/calculatedsimple/questiontype.php +++ b/question/type/calculatedsimple/questiontype.php @@ -1,30 +1,41 @@ . + ///////////////// // 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 @@ function save_question_options($question) { $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 @@ function save_question_options($question) { } } - - 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); + $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++; } - $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); + $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); + } } } } - } // Report any problems. //convert to calculated if(!empty($question->makecopy) && !empty($question->convert)) { @@ -190,39 +205,34 @@ function save_question_options($question) { } 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 @@ function comment_header($answers) { $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 = '
'; } return $strheader; @@ -262,18 +272,18 @@ function comment_header($answers) { 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 @@ function dataset_options($form, $name, $mandatory=true,$renameabledatasets=false } if (!$selected) { if ($mandatory){ - $selected = "1-0-$name"; // Default + $selected = "1-0-$name"; // Default }else { $selected = "0"; // Default } @@ -291,53 +301,123 @@ function dataset_options($form, $name, $mandatory=true,$renameabledatasets=false } -/** - * 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)); - } - - $new_question = $this->save_question($question, $form, $course); - - $dataset_form = new stdClass(); - $dataset_form->nextpageparam["forceregeneration"]= 1; - $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0); - $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0); - $dataset_form->calclength = array(1 => 1, 2 => 1); - $dataset_form->number = array(1 => 5.4 , 2 => 4.9); - $dataset_form->itemid = array(1 => '' , 2 => ''); - $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform'); - $dataset_form->definition = array(1 => "1-0-a", - 2 => "1-0-b"); - $dataset_form->nextpageparam = array('forceregeneration' => false); - $dataset_form->addbutton = 1; - $dataset_form->selectadd = 1; - $dataset_form->courseid = $courseid; - $dataset_form->cmid = 0; - $dataset_form->id = $new_question->id; - $this->save_dataset_items($new_question, $dataset_form); - - return $new_question; - } + /** + * Runs all the code required to set up and save an essay question for testing purposes. + * Alternate DB table prefix may be used to facilitate data deletion. + */ + function generate_test($name, $courseid = null) { + global $DB; + list($form, $question) = parent::generate_test($name, $courseid); + $form->feedback = 1; + $form->multiplier = array(1, 1); + $form->shuffleanswers = 1; + $form->noanswers = 1; + $form->qtype ='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)); + } + + $new_question = $this->save_question($question, $form, $course); + + $dataset_form = new stdClass(); + $dataset_form->nextpageparam["forceregeneration"]= 1; + $dataset_form->calcmin = array(1 => 1.0, 2 => 1.0); + $dataset_form->calcmax = array(1 => 10.0, 2 => 10.0); + $dataset_form->calclength = array(1 => 1, 2 => 1); + $dataset_form->number = array(1 => 5.4 , 2 => 4.9); + $dataset_form->itemid = array(1 => '' , 2 => ''); + $dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform'); + $dataset_form->definition = array(1 => "1-0-a", + 2 => "1-0-b"); + $dataset_form->nextpageparam = array('forceregeneration' => false); + $dataset_form->addbutton = 1; + $dataset_form->selectadd = 1; + $dataset_form->courseid = $courseid; + $dataset_form->cmid = 0; + $dataset_form->id = $new_question->id; + $this->save_dataset_items($new_question, $dataset_form); + + return $new_question; + } + /** + * When move the category of questions, the belonging files should be moved as well + * @param object $question, question information + * @param object $newcategory, target category information + */ + function move_files($question, $newcategory) { + global $DB; + parent::move_files($question, $newcategory); + + $fs = get_file_storage(); + // process files in answer + if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) { + $oldanswers = array(); + } + $component = 'question'; + $filearea = '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 @@ function generate_test($name, $courseid = null) { if ( ! defined ("CALCULATEDSIMPLE")) { define("CALCULATEDSIMPLE", "calculatedsimple"); } - - diff --git a/question/type/description/edit_description_form.php b/question/type/description/edit_description_form.php index 3170740f00f72..b13858f80596a 100644 --- a/question/type/description/edit_description_form.php +++ b/question/type/description/edit_description_form.php @@ -1,4 +1,20 @@ . + /** * Defines the editing form for the description question type. * diff --git a/question/type/description/question.html b/question/type/description/question.html index c55db3daf6e32..78f525e8ba292 100644 --- a/question/type/description/question.html +++ b/question/type/description/question.html @@ -6,10 +6,6 @@- - - -diff --git a/question/type/description/questiontype.php b/question/type/description/questiontype.php index 2d4221e70ee58..c3e8fb812c02f 100644 --- a/question/type/description/questiontype.php +++ b/question/type/description/questiontype.php @@ -68,7 +68,6 @@ function print_question(&$question, &$state, $number, $cmoptions, $options) { $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 @@ function grade_responses(&$question, &$state, $cmoptions) { } // Register this question type with questionlib.php. question_register_questiontype(new description_qtype()); - diff --git a/question/type/edit_question_form.php b/question/type/edit_question_form.php index 21b0ed260341f..a0a5977070426 100644 --- a/question/type/edit_question_form.php +++ b/question/type/edit_question_form.php @@ -1,4 +1,20 @@ . + /** * 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 @@ function definition() { $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 @@ function definition() { $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 @@ function validation($fromform, $files) { $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 @@ function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions $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 @@ function add_per_answer_fields(&$mform, $label, $gradeoptions, $minoptions = QUE 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 @@ function set_data($question) { } } } - + // 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 @@ function qtype() { return ''; } } - - diff --git a/question/type/essay/display.html b/question/type/essay/display.html index 2acb0a2f5b032..89c91d8014cfc 100644 --- a/question/type/essay/display.html +++ b/question/type/essay/display.html @@ -2,10 +2,6 @@- - - -diff --git a/question/type/essay/edit_essay_form.php b/question/type/essay/edit_essay_form.php index 5b90371d72d74..b238842892747 100644 --- a/question/type/essay/edit_essay_form.php +++ b/question/type/essay/edit_essay_form.php @@ -1,4 +1,20 @@ . + /** * 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 @@ function definition_inner(&$mform) { $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'; } } - diff --git a/question/type/essay/lib.php b/question/type/essay/lib.php new file mode 100644 index 0000000000000..dc1fe09d5caa9 --- /dev/null +++ b/question/type/essay/lib.php @@ -0,0 +1,31 @@ +. + +/** + * Serve question type files + * + * @since 2.0 + * @package questionbank + * @subpackage questiontypes + * @author Dongsheng Cai- - - -+ * @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); +} diff --git a/question/type/essay/questiontype.php b/question/type/essay/questiontype.php index 12acc8d629ef7..67bafcd99f142 100644 --- a/question/type/essay/questiontype.php +++ b/question/type/essay/questiontype.php @@ -1,5 +1,20 @@ . + ////////////////// /// ESSAY /// ///////////////// @@ -21,6 +36,7 @@ function is_manual_graded() { 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 @@ function save_question_options($question) { $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 @@ function save_question_options($question) { 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 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions $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 @@ function generate_test($name, $courseid = null) { 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 @@ function generate_test($name, $courseid = null) { //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// question_register_questiontype(new question_essay_qtype()); - diff --git a/question/type/match/db/install.xml b/question/type/match/db/install.xml index 78cb64986a08d..cb0ba92ac319f 100644 --- a/question/type/match/db/install.xml +++ b/question/type/match/db/install.xml @@ -1,5 +1,8 @@ - + \ No newline at end of file diff --git a/question/type/match/db/upgrade.php b/question/type/match/db/upgrade.php new file mode 100644 index 0000000000000..f634514b4555c --- /dev/null +++ b/question/type/match/db/upgrade.php @@ -0,0 +1,58 @@ +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; +} diff --git a/question/type/match/display.html b/question/type/match/display.html index 828262a740649..c55cfd60c8e96 100644 --- a/question/type/match/display.html +++ b/question/type/match/display.html @@ -2,10 +2,6 @@+ -
@@ -18,8 +21,9 @@ - - + + + @@ -27,4 +31,4 @@ - \ No newline at end of file + diff --git a/question/type/missingtype/questiontype.php b/question/type/missingtype/questiontype.php index e0422d474bac4..d0d450a153862 100644 --- a/question/type/missingtype/questiontype.php +++ b/question/type/missingtype/questiontype.php @@ -42,7 +42,6 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions $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(); diff --git a/question/type/multianswer/edit_multianswer_form.php b/question/type/multianswer/edit_multianswer_form.php index 151f745dc4dd2..fd88cb74c1674 100644 --- a/question/type/multianswer/edit_multianswer_form.php +++ b/question/type/multianswer/edit_multianswer_form.php @@ -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 @@ function question_edit_multianswer_form(&$submiturl, &$question, &$category, &$c }else { $this->reload = false ; } - // $this->question = $question; - $this->used_in_quiz =false; - // echo "diff --git a/question/type/match/edit_match_form.php b/question/type/match/edit_match_form.php index 880bc5966d6a6..75f27739dbdcb 100644 --- a/question/type/match/edit_match_form.php +++ b/question/type/match/edit_match_form.php @@ -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 @@ function definition_inner(&$mform) { $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 @@ function validation($data, $files) { $questioncount = 0; $answercount = 0; foreach ($questions as $key => $question){ - $trimmedquestion = trim($question); + $trimmedquestion = trim($question['text']); $trimmedanswer = trim($answers[$key]); if ($trimmedquestion != ''){ $questioncount++; diff --git a/question/type/match/lib.php b/question/type/match/lib.php new file mode 100644 index 0000000000000..e51349e159c6f --- /dev/null +++ b/question/type/match/lib.php @@ -0,0 +1,31 @@ +. + +/** + * Serve question type files + * + * @since 2.0 + * @package questionbank + * @subpackage questiontypes + * @author Dongsheng Cai
+ * @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); +} diff --git a/question/type/match/questiontype.php b/question/type/match/questiontype.php index f1bbe5ed38587..be01f1c539ebd 100644 --- a/question/type/match/questiontype.php +++ b/question/type/match/questiontype.php @@ -1,5 +1,20 @@ . + ///////////// /// MATCH /// ///////////// @@ -24,6 +39,7 @@ function get_question_options(&$question) { 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 @@ function save_question_options($question) { // 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 @@ function get_correct_responses(&$question, &$state) { 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 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions // 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 @@ function generate_test($name, $courseid = null) { 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 @@ function generate_test($name, $courseid = null) { //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// question_register_questiontype(new question_match_qtype()); - diff --git a/question/type/match/version.php b/question/type/match/version.php index 1950094efc88b..5b5cd5e043746 100644 --- a/question/type/match/version.php +++ b/question/type/match/version.php @@ -1,6 +1,6 @@ version = 2006032200; +$plugin->version = 2009072100; $plugin->requires = 2007101000; diff --git a/question/type/missingtype/display.html b/question/type/missingtype/display.html index e4ebf65893508..01d65085e2e52 100644 --- a/question/type/missingtype/display.html +++ b/question/type/missingtype/display.html @@ -2,10 +2,6 @@ - - - - @@ -21,4 +17,4 @@question
";print_r($question);echo ""; + // $this->question = $question; + $this->used_in_quiz =false; + // echo "question
";print_r($question);echo ""; 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 @@ function question_edit_multianswer_form(&$submiturl, &$question, &$category, &$c } } - - - - 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 @@ function definition_inner(&$mform) { $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 @@ function definition_inner(&$mform) { // 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 @@ function definition_inner(&$mform) { $countsavedsubquestions =0; foreach($this->savedquestiondisplay->options->questions as $subquestion){ if (!empty($subquestion)){ - $countsavedsubquestions++; + $countsavedsubquestions++; } } } else { @@ -108,24 +102,24 @@ function definition_inner(&$mform) { $countsubquestions =0; foreach($this->questiondisplay->options->questions as $subquestion){ if (!empty($subquestion)){ - $countsubquestions++; + $countsubquestions++; } } - } else { - $countsubquestions =0; - } - }else{ - $countsubquestions =$countsavedsubquestions ; + } else { + $countsubquestions =0; } - // echo "saved question $countsavedsubquestions
";print_r($this->savedquestiondisplay);echo ""; - // echo "saved question $countsubquestions
";print_r($this->questiondisplay);echo ""; + }else{ + $countsubquestions =$countsavedsubquestions ; + } + // echo "saved question $countsavedsubquestions
";print_r($this->savedquestiondisplay);echo ""; + // echo "saved question $countsubquestions
";print_r($this->questiondisplay);echo ""; - $mform->addElement('submit', 'analyzequestion', get_string('decodeverifyquestiontext','qtype_multianswer')); - $mform->registerNoSubmitButton('analyzequestion'); - echo ''; - echo ''; - if ( $this->reload ){ + $mform->addElement('submit', 'analyzequestion', get_string('decodeverifyquestiontext','qtype_multianswer')); + $mform->registerNoSubmitButton('analyzequestion'); + echo ''; + echo ''; } @@ -219,11 +211,11 @@ function set_data($question) { 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 @@ function set_data($question) { break; default: print_error('unknownquestiontype', 'question', '', $wrapped->qtype); - } - $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); } - if ($subanswer->feedback) { - $parsableanswerdef .= "#$subanswer->feedback"; + $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); + } + if ($subanswer->feedback) { + $parsableanswerdef .= "#$subanswer->feedback"; + } + $separator = '~'; } - $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.'_' ; - - // validate parameters - $answercount = 0; - $maxgrade = false; - $maxfraction = -1; - if ($subquestion->qtype =='shortanswer' ) { - switch ($subquestion->usecase) { + 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) { 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 @@ function set_data($question) { 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')); + foreach ($subquestion->answer as $key=>$answer) { + if ( $subquestion->qtype == 'numerical' && $key == 0 ) { + $default_values[$prefix.'tolerance['.$key.']'] = $subquestion->tolerance[0] ; } - if ($subquestion->fraction[$key] == 1) { - $maxgrade = true; + $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] ; + } } - 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 ($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')); } - } - if ($maxgrade == false) { - $this->_form->setElementError($prefix.'fraction[0]' ,get_string('fractionsnomax', 'question')); - } - foreach ($subquestion->feedback as $key=>$answer) { + 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']= "".get_string('questioninquiz','qtype_multianswer').""; + $default_values['alertas']= "".get_string('questioninquiz','qtype_multianswer').""; if( $default_values != "") { $question = (object)((array)$question + $default_values); @@ -358,14 +350,11 @@ function set_data($question) { 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 @@ function validation($data, $files) { $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 @@ function validation($data, $files) { $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'; } } - diff --git a/question/type/multianswer/questiontype.php b/question/type/multianswer/questiontype.php index e2786b4f39f2d..35f51e88d0937 100644 --- a/question/type/multianswer/questiontype.php +++ b/question/type/multianswer/questiontype.php @@ -307,10 +307,6 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions } echo ''; + if ( $this->reload ){ for ($sub =1;$sub <=$countsubquestions ;$sub++) { $this->editas[$sub] = 'unknown type'; @@ -135,14 +129,14 @@ function definition_inner(&$mform) { $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 = " 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 != $this->questiondisplay->options->questions[$sub]->qtype ){ + $this->qtype_change = true ; + $storemess = " STORED QTYPE ".$question_type_names[$this->savedquestiondisplay->options->questions[$sub]->qtype].""; + } $mform->addElement('header', 'subhdr'.$sub, get_string('questionno', 'quiz', - '{#'.$sub.'}').' '.$question_type_names[$this->questiondisplay->options->questions[$sub]->qtype].$storemess); + '{#'.$sub.'}').' '.$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 @@ function definition_inner(&$mform) { $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 @@ function definition_inner(&$mform) { echo ''; $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', "".get_string('questiondeleted','qtype_multianswer')."",get_string('questionsless','qtype_multianswer',$this->negative_diff)); + $mform->addElement('static', 'alert1', "".get_string('questiondeleted','qtype_multianswer')."",get_string('questionsless','qtype_multianswer',$this->negative_diff)); } - if($this->qtype_change ) - { - $mform->addElement('static', 'alert1', "".get_string('questiontypechanged','qtype_multianswer')."",get_string('questiontypechangedcomment','qtype_multianswer')); + if($this->qtype_change ) { + $mform->addElement('static', 'alert1', "".get_string('questiontypechanged','qtype_multianswer')."",get_string('questiontypechangedcomment','qtype_multianswer')); } echo ''; - // For this question type, we better print the image on top: - if ($image = get_question_image($question)) { - echo('- - - -
'); - } $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; } -?> diff --git a/question/type/multichoice/db/install.xml b/question/type/multichoice/db/install.xml index 8a50ff9d03637..da93831b65c7f 100644 --- a/question/type/multichoice/db/install.xml +++ b/question/type/multichoice/db/install.xml @@ -1,5 +1,5 @@ -@@ -12,10 +12,13 @@ + \ No newline at end of file diff --git a/question/type/multichoice/db/upgrade.php b/question/type/multichoice/db/upgrade.php index bbd4dcfea6316..04e31965c86aa 100644 --- a/question/type/multichoice/db/upgrade.php +++ b/question/type/multichoice/db/upgrade.php @@ -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; } diff --git a/question/type/multichoice/display.html b/question/type/multichoice/display.html index 5e4f66d6cd9ff..9acf11fd91489 100644 --- a/question/type/multichoice/display.html +++ b/question/type/multichoice/display.html @@ -2,10 +2,6 @@- - - - + + + + + + + - @@ -23,4 +26,4 @@ - - - -diff --git a/question/type/multichoice/edit_multichoice_form.php b/question/type/multichoice/edit_multichoice_form.php index f25a4eeec9e71..e062fc6812698 100644 --- a/question/type/multichoice/edit_multichoice_form.php +++ b/question/type/multichoice/edit_multichoice_form.php @@ -1,4 +1,22 @@ . + +defined('MOODLE_INTERNAL') || die(); + /** * Defines the editing form for the multichoice question type. * @@ -47,14 +65,14 @@ function definition_inner(&$mform) { $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 @@ function set_data($question) { 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() { diff --git a/question/type/multichoice/lib.php b/question/type/multichoice/lib.php new file mode 100644 index 0000000000000..5852ecaeef8ff --- /dev/null +++ b/question/type/multichoice/lib.php @@ -0,0 +1,31 @@ +. + +/** + * Serve question type files + * + * @since 2.0 + * @package questionbank + * @subpackage questiontypes + * @author Dongsheng Cai- - - -+ * @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); +} diff --git a/question/type/multichoice/questiontype.php b/question/type/multichoice/questiontype.php index 856b59af9a8a8..c38437769878f 100644 --- a/question/type/multichoice/questiontype.php +++ b/question/type/multichoice/questiontype.php @@ -29,8 +29,8 @@ function get_question_options(&$question) { 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 @@ function get_question_options(&$question) { 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 @@ function save_question_options($question) { 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 @@ function save_question_options($question) { $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 @@ function save_question_options($question) { } /** - * Deletes question from the question-type specific tables - * - * @return boolean Success/Failure - * @param object $question The question being deleted - */ + * Deletes question from the question-type specific tables + * + * @return boolean Success/Failure + * @param object $question The question being deleted + */ function delete_question($questionid) { global $DB; $DB->delete_records("question_multichoice", array("question" => $questionid)); @@ -153,10 +165,10 @@ function delete_question($questionid) { 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 @@ function restore_session_and_responses(&$question, &$state) { $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 @@ function get_correct_responses(&$question, &$state) { 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 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions $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 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions // 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 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions 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 @@ function grade_responses(&$question, &$state, $cmoptions) { // 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 @@ function get_random_guess_score($question) { } return $totalfraction / count($question->options->answers); } -/// BACKUP FUNCTIONS //////////////////////////// + /// BACKUP FUNCTIONS //////////////////////////// /* * Backup the data in the question @@ -436,7 +456,7 @@ function backup($bf,$preferences,$question,$level=6) { return $status; } -/// RESTORE FUNCTIONS ///////////////// + /// RESTORE FUNCTIONS ///////////////// /* * Restores the data in the question @@ -575,31 +595,31 @@ function decode_content_links_caller($questionids, $restore, &$i) { // 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); - } - - // Do some output. - if (++$i % 5 == 0 && !defined('RESTORE_SILENTLY')) { - echo "."; - if ($i % 100 == 0) { - echo "
"; + $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 "
"; + } + backup_flush(300); } - backup_flush(300); } } - } return $status; } @@ -625,16 +645,16 @@ function number_html($qnum) { */ 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 @@ function generate_test($name, $courseid = null) { 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()); - diff --git a/question/type/multichoice/version.php b/question/type/multichoice/version.php index 0d354e821d2bd..c0b4d8672c8d6 100644 --- a/question/type/multichoice/version.php +++ b/question/type/multichoice/version.php @@ -1,6 +1,6 @@ version = 2009021800; +$plugin->version = 2009021801; $plugin->requires = 2007101000; diff --git a/question/type/numerical/db/install.xml b/question/type/numerical/db/install.xml index 735fc4b466068..c4f3ee44a0521 100644 --- a/question/type/numerical/db/install.xml +++ b/question/type/numerical/db/install.xml @@ -1,5 +1,5 @@ -@@ -23,8 +23,9 @@ + \ No newline at end of file diff --git a/question/type/numerical/db/upgrade.php b/question/type/numerical/db/upgrade.php index d2ff825cbd55a..96b547a5993ba 100644 --- a/question/type/numerical/db/upgrade.php +++ b/question/type/numerical/db/upgrade.php @@ -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; } diff --git a/question/type/numerical/display.html b/question/type/numerical/display.html index 4262283926d39..c81bd1e7a64a5 100644 --- a/question/type/numerical/display.html +++ b/question/type/numerical/display.html @@ -2,10 +2,6 @@- - + + + @@ -50,4 +51,4 @@ - - - diff --git a/question/type/numerical/edit_numerical_form.php b/question/type/numerical/edit_numerical_form.php index 598e0c79b01bc..8ed3aff89eabb 100644 --- a/question/type/numerical/edit_numerical_form.php +++ b/question/type/numerical/edit_numerical_form.php @@ -1,4 +1,20 @@ . + /** * Defines the editing form for the numerical question type. * @@ -39,30 +55,36 @@ function definition_inner(&$mform) { $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 @@ function set_data($question) { }*/ $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 @@ function validation($data, $files) { 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 @@ function validation($data, $files) { return $errors; } + function qtype() { return 'numerical'; } diff --git a/question/type/numerical/lib.php b/question/type/numerical/lib.php new file mode 100644 index 0000000000000..0b83d90cba108 --- /dev/null +++ b/question/type/numerical/lib.php @@ -0,0 +1,31 @@ +. + +/** + * Serve question type files + * + * @since 2.0 + * @package questionbank + * @subpackage questiontypes + * @author Dongsheng Cai@@ -237,7 +233,7 @@options->instructions)){?>- options->instructions, true, $formatoptions, $cmoptions->course);?> + options->instructions, $question->options->instructionsformat, $formatoptions, $cmoptions->course);?>@@ -250,5 +246,3 @@ print_question_submit_buttons($question, $state, $cmoptions, $options); ?>+ * @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); +} diff --git a/question/type/numerical/questiontype.php b/question/type/numerical/questiontype.php index 33062593a5624..acb91008030e6 100644 --- a/question/type/numerical/questiontype.php +++ b/question/type/numerical/questiontype.php @@ -1,10 +1,26 @@ . + /** * @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 @@ * @package questionbank * @subpackage questiontypes */ + class question_numerical_qtype extends question_shortanswer_qtype { public $virtualqtype = false; @@ -52,7 +69,7 @@ function get_question_options(&$question) { } $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 @@ function get_numerical_options(&$question) { 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 @@ function get_numerical_options(&$question) { $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 @@ function get_default_numerical_unit(&$question) { */ 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 @@ function save_question_options($question) { 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 @@ function save_question_options($question) { } } $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 @@ function save_question_options($question) { 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 @@ function save_numerical_options(&$question) { }else { $options->showunits = $question->showunits1; } - }else { + }else { if(isset($question->showunits)){ $options->showunits = $question->showunits; }else { @@ -264,22 +296,27 @@ function save_numerical_options(&$question) { }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 @@ function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) $state->responses = array(); $state->responses['answer'] = ''; $state->responses['unit'] = ''; - + return true; } function restore_session_and_responses(&$question, &$state) { @@ -356,10 +393,10 @@ function split_old_answer($rawresponse, $units, &$answer ,&$unit ) { $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 @@ function delete_question($questionid) { */ 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 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions } 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 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions //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 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions }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 @@ function compare_responses(&$question, $state, $teststate) { * 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 @@ function test_response(&$question, &$state, $answer ) { } /* 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 @@ function test_response(&$question, &$state, $answer ) { // 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 @@ function test_response(&$question, &$state, $answer ) { } /** - * 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 @@ function grade_responses(&$question, &$state, $cmoptions) { 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 @@ function grade_responses(&$question, &$state, $cmoptions) { $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 @@ function grade_responses(&$question, &$state, $cmoptions) { } 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 @@ function grade_responses(&$question, &$state, $cmoptions) { $valid_numerical_unit = true ; } } - + // apply unit penalty $raw_unitpenalty = 0 ; if(!empty($question->options->unitpenalty)&& $valid_numerical_unit != true ){ @@ -725,7 +767,7 @@ function grade_responses(&$question, &$state, $cmoptions) { } $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 @@ function extract_numerical_response($rawresponse) { // 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 @@ function apply_unit_old($rawresponse, $units) { // 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 @@ function apply_unit_old($rawresponse, $units) { } /** - * 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 @@ function add_units_options(&$mform, &$that){ $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', '
', '
'); // Units are not graded $mform->addElement('radio', 'unitrole', get_string('unitnotgraded', 'qtype_numerical'), get_string('onlynumerical', 'qtype_numerical'),1); @@ -967,6 +1008,8 @@ function add_units_options(&$mform, &$that){ $mform->disabledIf('unitsleft', 'showunits1','eq','3'); $mform->disabledIf('showunits1','unitrole','eq','0'); $mform->disabledIf('showunits0','unitrole','eq','1'); + + } /** @@ -974,7 +1017,7 @@ function add_units_options(&$mform, &$that){ * 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 @@ function add_units_elements(& $mform,& $that) { } /** - * 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 @@ function set_numerical_unit_data(&$question,&$default_values){ } } -/** - * 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 @@ function validate_numerical_options(& $data, & $errors){ }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 @@ function validate_numerical_options(& $data, & $errors){ } } } - - + + // Check double units. $alreadyseenunits = array(); if (isset($data['unit'])) { @@ -1106,7 +1166,7 @@ function validate_numerical_options(& $data, & $errors){ } } - } + } } } @@ -1284,6 +1344,71 @@ function generate_test($name, $courseid = null) { 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. diff --git a/question/type/numerical/version.php b/question/type/numerical/version.php index 168f4ff99cc71..ca04978a12415 100644 --- a/question/type/numerical/version.php +++ b/question/type/numerical/version.php @@ -1,6 +1,6 @@ version = 2009100100; +$plugin->version = 2009100101; $plugin->requires = 2007101000; diff --git a/question/type/questiontype.php b/question/type/questiontype.php index 54e44956e42fe..e5767afa6b59c 100644 --- a/question/type/questiontype.php +++ b/question/type/questiontype.php @@ -1,4 +1,20 @@ . + /** * The default questiontype class. * @@ -30,6 +46,7 @@ * @subpackage questiontypes */ class default_questiontype { + public static $fileoptions = array('subdirs'=>false, 'maxfiles'=>-1, 'maxbytes'=>0); /** * Name of the question type @@ -294,31 +311,35 @@ function get_heading($adding = false){ */ 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 @@ function save_question($question, $form, $course) { $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 @@ function save_question($question, $form, $course) { $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 @@ function save_question($question, $form, $course) { $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 @@ protected function find_standard_scripts() { * @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 @@ function print_question(&$question, &$state, $number, $cmoptions, $options) { $generalfeedback = ''; if ($isgraded && $options->generalfeedback) { $generalfeedback = $this->format_text($question->generalfeedback, - $question->questiontextformat, $cmoptions); + $question->generalfeedbackformat, $cmoptions); } $grade = ''; @@ -1246,6 +1284,22 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions .' 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 @@ function format_text($text, $textformat, $cmoptions = NULL) { */ 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 @@ function find_file_links($question, $courseid){ 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 @@ function generate_test($name, $courseid=null) { $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(); + } + } + } + } +} diff --git a/question/type/random/edit_random_form.php b/question/type/random/edit_random_form.php index 35eddb91b71fd..0572d14f1f95d 100644 --- a/question/type/random/edit_random_form.php +++ b/question/type/random/edit_random_form.php @@ -79,11 +79,13 @@ function definition() { $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'; } diff --git a/question/type/random/questiontype.php b/question/type/random/questiontype.php index 958501fbcd4e5..f1acc34c2f822 100644 --- a/question/type/random/questiontype.php +++ b/question/type/random/questiontype.php @@ -88,6 +88,7 @@ function init_qtype_lists() { } 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 @@ function get_random_guess_score($question) { //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// question_register_questiontype(new random_qtype()); - - diff --git a/question/type/randomsamatch/edit_randomsamatch_form.php b/question/type/randomsamatch/edit_randomsamatch_form.php index 24b9502e175aa..4cdf576850132 100644 --- a/question/type/randomsamatch/edit_randomsamatch_form.php +++ b/question/type/randomsamatch/edit_randomsamatch_form.php @@ -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 @@ function definition_inner(&$mform) { $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 @@ function validation($data, $files) { $errors['choose'] = get_string('notenoughsaincategory', 'qtype_randomsamatch', $a); } return $errors; - } } - diff --git a/question/type/shortanswer/display.html b/question/type/shortanswer/display.html index ad263640a8b34..d29fe5d97c4a1 100644 --- a/question/type/shortanswer/display.html +++ b/question/type/shortanswer/display.html @@ -2,10 +2,6 @@diff --git a/question/type/truefalse/edit_truefalse_form.php b/question/type/truefalse/edit_truefalse_form.php index ad70e66a838b9..4057d886fca29 100644 --- a/question/type/truefalse/edit_truefalse_form.php +++ b/question/type/truefalse/edit_truefalse_form.php @@ -1,9 +1,26 @@ . + 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 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @package questionbank * @subpackage questiontypes - *//** */ + */ /** * truefalse editing form definition. @@ -27,12 +44,10 @@ function definition_inner(&$mform) { $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 @@ function definition_inner(&$mform) { 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); } diff --git a/question/type/truefalse/lib.php b/question/type/truefalse/lib.php new file mode 100644 index 0000000000000..6b8e005719530 --- /dev/null +++ b/question/type/truefalse/lib.php @@ -0,0 +1,31 @@ +. + +/** + * 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); +} diff --git a/question/type/truefalse/questiontype.php b/question/type/truefalse/questiontype.php index 9c2b895e0c072..58c78a1330235 100644 --- a/question/type/truefalse/questiontype.php +++ b/question/type/truefalse/questiontype.php @@ -1,5 +1,22 @@ . + +defined('MOODLE_INTERNAL') || die(); + ///////////////// /// TRUEFALSE /// ///////////////// @@ -24,34 +41,52 @@ function save_question_options($question) { $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 @@ function get_correct_responses(&$question, &$state) { /** * 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 @@ function print_question_formulation_and_controls(&$question, &$state, $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 @@ function print_question_formulation_and_controls(&$question, &$state, $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 @@ function generate_test($name, $courseid = null) { 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 @@ function generate_test($name, $courseid = null) { //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// question_register_questiontype(new question_truefalse_qtype()); - diff --git a/version.php b/version.php index c07041033937f..ae20a02f0c55d 100644 --- a/version.php +++ b/version.php @@ -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 namediff --git a/question/type/shortanswer/edit_shortanswer_form.php b/question/type/shortanswer/edit_shortanswer_form.php index fae996e4642fe..a43f3c2fac64a 100644 --- a/question/type/shortanswer/edit_shortanswer_form.php +++ b/question/type/shortanswer/edit_shortanswer_form.php @@ -1,4 +1,22 @@ . + +defined('MOODLE_INTERNAL') || die(); + /** * Defines the editing form for the shortanswer question type. * @@ -30,22 +48,40 @@ function definition_inner(&$mform) { $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 @@ function validation($data, $files) { 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++; } diff --git a/question/type/shortanswer/lib.php b/question/type/shortanswer/lib.php new file mode 100644 index 0000000000000..82757e70d7383 --- /dev/null +++ b/question/type/shortanswer/lib.php @@ -0,0 +1,31 @@ +. + +/** + * Serve question type files + * + * @since 2.0 + * @package questionbank + * @subpackage questiontypes + * @author Dongsheng Cai- - - -+ * @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); +} diff --git a/question/type/shortanswer/questiontype.php b/question/type/shortanswer/questiontype.php index a9a0d22eb4d2f..d285f45e93eae 100644 --- a/question/type/shortanswer/questiontype.php +++ b/question/type/shortanswer/questiontype.php @@ -1,5 +1,20 @@ . + /////////////////// /// SHORTANSWER /// /////////////////// @@ -27,17 +42,48 @@ function has_wildcards_in_responses($question, $subqid) { } 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 @@ function save_question_options($question) { 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 @@ function save_question_options($question) { } 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 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions $questiontext = format_text($question->questiontext, $question->questiontextformat, $formatoptions, $cmoptions->course); - $image = get_question_image($question); /// Print input controls @@ -135,6 +195,7 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions $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 @@ function print_question_formulation_and_controls(&$question, &$state, $cmoptions /// 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 @@ function print_question_grading_details(&$question, &$state, $cmoptions, $option // 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 @@ function generate_test($name, $courseid = null) { 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 //// diff --git a/question/type/truefalse/display.html b/question/type/truefalse/display.html index 2bd99a5702c42..344f3d830db43 100644 --- a/question/type/truefalse/display.html +++ b/question/type/truefalse/display.html @@ -2,10 +2,6 @@ \ No newline at end of file +: @@ -27,4 +23,4 @@print_question_submit_buttons($question, $state, $cmoptions, $options); ?> -