diff --git a/lib/questionlib.php b/lib/questionlib.php index 9b1149e8dc1..f8f6b29e0ee 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -1833,20 +1833,18 @@ function question_pluginfile($course, $context, $component, $filearea, $args, $f send_file($content, $filename, 0, 0, true, true, $qformat->mime_type()); } - $attemptid = (int)array_shift($args); - $questionid = (int)array_shift($args); + $qubaid = (int)array_shift($args); + $slot = (int)array_shift($args); + $module = $DB->get_field('question_usages', 'component', + array('id' => $qubaid)); - if ($attemptid === 0) { - // preview + if ($module === 'core_question_preview') { require_once($CFG->dirroot . '/question/previewlib.php'); return question_preview_question_pluginfile($course, $context, - $component, $filearea, $attemptid, $questionid, $args, $forcedownload); + $component, $filearea, $qubaid, $slot, $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(); @@ -1858,7 +1856,7 @@ function question_pluginfile($course, $context, $component, $filearea, $args, $f send_file_not_found(); } - $filefunction($course, $context, $component, $filearea, $attemptid, $questionid, + $filefunction($course, $context, $component, $filearea, $qubaid, $slot, $args, $forcedownload); send_file_not_found(); @@ -1866,6 +1864,8 @@ function question_pluginfile($course, $context, $component, $filearea, $args, $f } /** + * TODO delete this. Replaced by $quba->check_file_access. + * * Final test for whether a studnet should be allowed to see a particular file. * This delegates the decision to the question type plugin. * diff --git a/local/qedatabase/db/install.php b/local/qedatabase/db/install.php index 84eb3c3bc95..1ced515055a 100755 --- a/local/qedatabase/db/install.php +++ b/local/qedatabase/db/install.php @@ -673,6 +673,7 @@ function xmldb_local_qedatabase_install() { $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('questionid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); $table->add_field('hint', XMLDB_TYPE_TEXT, 'small', null, XMLDB_NOTNULL, null, null); + $table->add_field('hintformat', XMLDB_TYPE_INTEGER, '4', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0'); $table->add_field('shownumcorrect', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, null, null, null); $table->add_field('clearwrong', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, null, null, null); $table->add_field('options', XMLDB_TYPE_CHAR, '255', null, null, null, null); diff --git a/local/qedatabase/db/upgrade.php b/local/qedatabase/db/upgrade.php new file mode 100755 index 00000000000..1bfac5da6ad --- /dev/null +++ b/local/qedatabase/db/upgrade.php @@ -0,0 +1,21 @@ +get_manager(); + + if ($oldversion < 2008000700) { + + // Define field hintformat to be added to question_hints table. + $table = new xmldb_table('question_hints'); + $field = new xmldb_field('hintformat', XMLDB_TYPE_INTEGER, '4', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0'); + + // Conditionally launch add field partiallycorrectfeedbackformat + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + upgrade_plugin_savepoint(true, 2008000700, 'local', 'qedatabase'); + } +} diff --git a/local/qedatabase/version.php b/local/qedatabase/version.php index e1ac2dbfd0f..10ccc937218 100755 --- a/local/qedatabase/version.php +++ b/local/qedatabase/version.php @@ -26,5 +26,5 @@ defined('MOODLE_INTERNAL') || die; -$plugin->version = 2008000602; +$plugin->version = 2008000700; $plugin->requires = 2010080300; diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index db4f1f0ff37..fc657cb09ed 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -174,6 +174,7 @@ function quiz_load_attempt($attemptid) { return false; } + // TODO kill this. if (!$DB->record_exists('question_sessions', array('attemptid' => $attempt->uniqueid))) { /// this attempt has not yet been upgraded to the new model quiz_upgrade_states($attempt); diff --git a/question/behaviour/behaviourbase.php b/question/behaviour/behaviourbase.php index ccf9162eb28..15969f78a33 100644 --- a/question/behaviour/behaviourbase.php +++ b/question/behaviour/behaviourbase.php @@ -122,6 +122,20 @@ abstract class question_behaviour { return $qoutput->question($this->qa, $behaviouroutput, $qtoutput, $options, $number); } + /** + * Checks whether the users is allow to be served a particular file. + * @param question_display_options $options the options that control display of the question. + * @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 boolean $forcedownload whether the user must be forced to download the file. + * @return boolean true if the user can access this file. + */ + public function check_file_access($options, $component, $filearea, $args, $forcedownload) { + $this->adjust_display_options($options); + return $this->question->check_file_access($this->qa, $options, $component, $filearea, $args, $forcedownload); + } + /** * @return qbehaviour_renderer get the appropriate renderer to use for this model. */ @@ -432,18 +446,19 @@ abstract class question_behaviour { /** * @param $comment the comment text to format. If omitted, * $this->qa->get_manual_comment() is used. + * @param $commentformat the format of the comment, one of the FORMAT_... constants. * @return string the comment, ready to be output. */ - public function format_comment($comment = null) { + public function format_comment($comment = null, $commentformat = null) { $formatoptions = new stdClass; $formatoptions->noclean = true; $formatoptions->para = false; if (is_null($comment)) { - $comment = $this->qa->get_manual_comment(); + list($comment, $commentformat) = $this->qa->get_manual_comment(); } - return format_text($comment, FORMAT_HTML, $formatoptions); + return format_text($comment, $commentformat, $formatoptions); } /** diff --git a/question/behaviour/interactive/simpletest/testwalkthrough.php b/question/behaviour/interactive/simpletest/testwalkthrough.php index f6fc9cb43f2..c761dfc12a1 100644 --- a/question/behaviour/interactive/simpletest/testwalkthrough.php +++ b/question/behaviour/interactive/simpletest/testwalkthrough.php @@ -36,8 +36,8 @@ class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_tes // Create a multichoice single question. $mc = test_question_maker::make_a_multichoice_single_question(); $mc->hints = array( - new question_hint_with_parts('This is the first hint.', false, false), - new question_hint_with_parts('This is the second hint.', true, true), + new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, false, false), + new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), ); $this->start_attempt_at_question($mc, 'interactive'); @@ -176,7 +176,7 @@ class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_tes // Create a multichoice single question. $mc = test_question_maker::make_a_multichoice_single_question(); $mc->hints = array( - new question_hint_with_parts('This is the first hint.', false, false), + new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, false, false), ); $this->start_attempt_at_question($mc, 'interactive'); @@ -235,8 +235,8 @@ class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_tes // Create a short answer question. $sa = test_question_maker::make_a_shortanswer_question(); $sa->hints = array( - new question_hint('This is the first hint.'), - new question_hint('This is the second hint.'), + new question_hint(0, 'This is the first hint.', FORMAT_HTML), + new question_hint(0, 'This is the second hint.', FORMAT_HTML), ); $this->start_attempt_at_question($sa, 'interactive'); @@ -330,8 +330,8 @@ class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_tes // Create a multichoice multiple question. $mc = test_question_maker::make_a_multichoice_multi_question(); $mc->hints = array( - new question_hint_with_parts('This is the first hint.', true, true), - new question_hint_with_parts('This is the second hint.', true, true), + new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true), + new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), ); $this->start_attempt_at_question($mc, 'interactive', 2); @@ -403,8 +403,8 @@ class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_tes // Create a multichoice multiple question. $q = test_question_maker::make_a_shortanswer_question(); $q->hints = array( - new question_hint_with_parts('This is the first hint.', true, true), - new question_hint_with_parts('This is the second hint.', true, true), + new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true), + new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), ); $this->start_attempt_at_question($q, 'interactive', 3); @@ -436,8 +436,8 @@ class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_tes // Create a multichoice multiple question. $q = test_question_maker::make_a_shortanswer_question(); $q->hints = array( - new question_hint_with_parts('This is the first hint.', true, true), - new question_hint_with_parts('This is the second hint.', true, true), + new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true), + new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), ); $this->start_attempt_at_question($q, 'interactive', 3); diff --git a/question/behaviour/interactivecountback/simpletest/testwalkthrough.php b/question/behaviour/interactivecountback/simpletest/testwalkthrough.php index 14de088873d..815433dc932 100644 --- a/question/behaviour/interactivecountback/simpletest/testwalkthrough.php +++ b/question/behaviour/interactivecountback/simpletest/testwalkthrough.php @@ -36,8 +36,8 @@ class qbehaviour_interactivecountback_walkthrough_test extends qbehaviour_walkth $m = test_question_maker::make_a_matching_question(); $m->shufflestems = false; $m->hints = array( - new question_hint_with_parts('This is the first hint.', true, true), - new question_hint_with_parts('This is the second hint.', true, true), + new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true), + new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), ); $this->start_attempt_at_question($m, 'interactive', 12); diff --git a/question/behaviour/missing/simpletest/testmissingbehaviour.php b/question/behaviour/missing/simpletest/testmissingbehaviour.php index a4bd3df8120..0a6ee5568e2 100644 --- a/question/behaviour/missing/simpletest/testmissingbehaviour.php +++ b/question/behaviour/missing/simpletest/testmissingbehaviour.php @@ -31,21 +31,21 @@ require_once(dirname(__FILE__) . '/../behaviour.php'); class qbehaviour_missing_test extends UnitTestCase { public function test_missing_cannot_start() { - $qa = new question_attempt(test_question_maker::make_a_truefalse_question(), 0); + $qa = new question_attempt(test_question_maker::make_a_truefalse_question(), 0, 0); $behaviour = new qbehaviour_missing($qa, 'deferredfeedback'); $this->expectException(); $behaviour->init_first_step(new question_attempt_step(array())); } public function test_missing_cannot_process() { - $qa = new question_attempt(test_question_maker::make_a_truefalse_question(), 0); + $qa = new question_attempt(test_question_maker::make_a_truefalse_question(), 0, 0); $behaviour = new qbehaviour_missing($qa, 'deferredfeedback'); $this->expectException(); $behaviour->process_action(new question_attempt_pending_step(array())); } public function test_missing_cannot_get_min_grade() { - $qa = new question_attempt(test_question_maker::make_a_truefalse_question(), 0); + $qa = new question_attempt(test_question_maker::make_a_truefalse_question(), 0, 0); $behaviour = new qbehaviour_missing($qa, 'deferredfeedback'); $this->expectException(); $behaviour->get_min_fraction(); diff --git a/question/behaviour/rendererbase.php b/question/behaviour/rendererbase.php index 655c34de618..630db9f265c 100644 --- a/question/behaviour/rendererbase.php +++ b/question/behaviour/rendererbase.php @@ -71,7 +71,8 @@ abstract class qbehaviour_renderer extends plugin_renderer_base { $commentfield = $qa->get_behaviour_field_name('comment'); - $comment = print_textarea(can_use_html_editor(), 10, 80, null, null, $commentfield, $qa->get_manual_comment(), 0, true); + list($commenttext, $commentformat) = $this->qa->get_manual_comment(); + $comment = print_textarea(can_use_html_editor(), 10, 80, null, null, $commentfield, $commenttext, 0, true); $comment = html_writer::tag('div', html_writer::tag('div', html_writer::tag('label', get_string('comment', 'question'), array('for' => $commentfield)), array('class' => 'fitemtitle')) . diff --git a/question/engine/datalib.php b/question/engine/datalib.php index d4dd82aa0c2..24906194820 100644 --- a/question/engine/datalib.php +++ b/question/engine/datalib.php @@ -164,6 +164,7 @@ WHERE $records = $this->db->get_records_sql(" SELECT COALESCE(qasd.id, -1 * qas.id) AS id, + quba.contextid, quba.preferredbehaviour, qa.id AS questionattemptid, qa.questionusageid, diff --git a/question/engine/lib.php b/question/engine/lib.php index 7fb92968f7e..d30535a368d 100644 --- a/question/engine/lib.php +++ b/question/engine/lib.php @@ -718,7 +718,8 @@ class question_usage_by_activity { * @return integer the number used to identify this question within this usage. */ public function add_question(question_definition $question, $maxmark = null) { - $qa = new question_attempt($question, $this->get_id(), $this->observer, $maxmark); + $qa = new question_attempt($question, $this->get_id(), + $this->context->id, $this->observer, $maxmark); if (count($this->questionattempts) == 0) { $this->questionattempts[1] = $qa; } else { @@ -925,6 +926,20 @@ class question_usage_by_activity { return $this->get_question_attempt($slot)->render_at_step($seq, $options, $number, $this->preferredbehaviour); } + /** + * Checks whether the users is allow to be served a particular file. + * @param integer $slot the number used to identify this question within this usage. + * @param question_display_options $options the options that control display of the question. + * @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 boolean $forcedownload whether the user must be forced to download the file. + * @return boolean true if the user can access this file. + */ + public function check_file_access($slot, $options, $component, $filearea, $args, $forcedownload) { + return $this->get_question_attempt($slot)->check_file_access($options, $component, $filearea, $args, $forcedownload); + } + /** * Replace a particular question_attempt with a different one. * @@ -1141,7 +1156,7 @@ class question_usage_by_activity { $this->observer->notify_delete_attempt_steps($oldqa); $newqa = new question_attempt($oldqa->get_question(), $oldqa->get_usage_id(), - $this->observer, $newmaxmark); + $this->context->id, $this->observer, $newmaxmark); $newqa->set_database_id($oldqa->get_database_id()); $newqa->regrade($oldqa, $finished); @@ -1297,7 +1312,7 @@ class question_attempt { /** @var integer|string the id of the question_usage_by_activity we belong to. */ protected $usageid; - // TODO + /** @var integer the id of the context this question_attempt belongs to. */ protected $owningcontextid = null; /** @var integer the number used to identify this question_attempt within the usage. */ @@ -1370,10 +1385,11 @@ class question_attempt { * @param number $maxmark the maximum grade for this question_attempt. If not * passed, $question->defaultmark is used. */ - public function __construct(question_definition $question, $usageid, + public function __construct(question_definition $question, $usageid, $owningcontextid, question_usage_observer $observer = null, $maxmark = null) { $this->question = $question; $this->usageid = $usageid; + $this->owningcontextid = $owningcontextid; if (is_null($observer)) { $observer = new question_usage_null_observer(); } @@ -1782,10 +1798,18 @@ class question_attempt { return $this->behaviour->summarise_action($step); } - public function rewrite_pluginfile_urls($text, $component, $filearea) { + /** + * Calls {@link question_rewrite_question_urls()} with appropriate parameters + * for content belonging to this question. + * @param string $text the content to output. + * @param string $component the component name (normally 'question' or 'qtype_...') + * @param string $filearea the name of the file area. + * @param integer $itemid the item id. + */ + public function rewrite_pluginfile_urls($text, $component, $filearea, $itemid) { return question_rewrite_question_urls($text, 'pluginfile.php', $this->owningcontextid, $component, $filearea, - array($this->get_usage_id(), $this->get_slot()), $this->get_question()->id); + array($this->get_usage_id(), $this->get_slot()), $itemid); } /** @@ -1828,6 +1852,19 @@ class question_attempt { return $restrictedqa->render($options, $number); } + /** + * Checks whether the users is allow to be served a particular file. + * @param question_display_options $options the options that control display of the question. + * @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 boolean $forcedownload whether the user must be forced to download the file. + * @return boolean true if the user can access this file. + */ + public function check_file_access($options, $component, $filearea, $args, $forcedownload) { + return $this->behaviour->check_file_access($options, $component, $filearea, $args, $forcedownload); + } + /** * Add a step to this question attempt. * @param question_attempt_step $step the new step. @@ -2121,16 +2158,17 @@ class question_attempt { } /** - * @return string the most recent manual comment that was added to this question. - * null, if none. + * @return array(string, int) the most recent manual comment that was added + * to this question, and the FORMAT_... it is. */ public function get_manual_comment() { foreach ($this->get_reverse_step_iterator() as $step) { if ($step->has_behaviour_var('comment')) { - return $step->get_behaviour_var('comment'); + return array($step->get_behaviour_var('comment'), + $step->get_behaviour_var('commentformat')); } } - return null; + return array(null, null); } /** @@ -2172,7 +2210,8 @@ class question_attempt { $record->questionid, $record->maxmark + 0); } - $qa = new question_attempt($question, $record->questionusageid, null, $record->maxmark + 0); + $qa = new question_attempt($question, $record->questionusageid, + $record->contextid, null, $record->maxmark + 0); $qa->set_database_id($record->questionattemptid); $qa->set_number_in_usage($record->slot); $qa->minfraction = $record->minfraction + 0; diff --git a/question/engine/simpletest/helpers.php b/question/engine/simpletest/helpers.php index 8641bbddeb7..fcea16620bc 100644 --- a/question/engine/simpletest/helpers.php +++ b/question/engine/simpletest/helpers.php @@ -69,7 +69,7 @@ class test_question_maker { * @return question_attempt the question attempt. */ public function get_a_qa($question, $maxmark = 3) { - return new question_attempt($question, 13, null, $maxmark); + return new question_attempt($question, 13, 0, null, $maxmark); } /** @@ -111,6 +111,8 @@ class test_question_maker { $tf->rightanswer = true; $tf->truefeedback = 'This is the right answer.'; $tf->falsefeedback = 'This is the wrong answer.'; + $tf->trueanswerid = 13; + $tf->falseanswerid = 14; return $tf; } diff --git a/question/engine/simpletest/testquestionattempt.php b/question/engine/simpletest/testquestionattempt.php index d13f339a79f..a5f76fe5044 100644 --- a/question/engine/simpletest/testquestionattempt.php +++ b/question/engine/simpletest/testquestionattempt.php @@ -41,7 +41,7 @@ class question_attempt_test extends UnitTestCase { $this->question = test_question_maker::make_a_description_question(); $this->question->defaultmark = 3; $this->usageid = 13; - $this->qa = new question_attempt($this->question, $this->usageid); + $this->qa = new question_attempt($this->question, $this->usageid, 0); } public function tearDown() { @@ -51,13 +51,13 @@ class question_attempt_test extends UnitTestCase { } public function test_constructor_sets_maxmark() { - $qa = new question_attempt($this->question, $this->usageid); + $qa = new question_attempt($this->question, $this->usageid, 0); $this->assertIdentical($this->question, $qa->get_question()); $this->assertEqual(3, $qa->get_max_mark()); } public function test_maxmark_beats_default_mark() { - $qa = new question_attempt($this->question, $this->usageid, null, 2); + $qa = new question_attempt($this->question, $this->usageid, 0, null, 2); $this->assertEqual(2, $qa->get_max_mark()); } @@ -133,7 +133,7 @@ class question_attempt_with_steps_test extends UnitTestCase { public function setUp() { $this->question = test_question_maker::make_a_description_question(); - $this->qa = new testable_question_attempt($this->question, 0, null, 2); + $this->qa = new testable_question_attempt($this->question, 0, 0, null, 2); for ($i = 0; $i < 3; $i++) { $step = new question_attempt_step(array('i' => $i)); $this->qa->add_step($step); @@ -237,7 +237,7 @@ class question_attempt_with_steps_test extends UnitTestCase { } public function test_cannot_get_min_fraction_before_start() { - $qa = new question_attempt($this->question, null); + $qa = new question_attempt($this->question, null, 0); $this->expectException(); $qa->get_min_fraction(); } @@ -247,19 +247,19 @@ class question_attempt_with_steps_test extends UnitTestCase { class question_attempt_db_test extends data_loading_method_test_base { public function test_load() { $records = testing_db_record_builder::build_db_records(array( - array('id', 'questionattemptid', 'questionusageid', 'slot', + array('id', 'questionattemptid', 'contextid', 'questionusageid', 'slot', 'behaviour', 'questionid', 'maxmark', 'minfraction', 'flagged', 'questionsummary', 'rightanswer', 'responsesummary', 'timemodified', 'attemptstepid', 'sequencenumber', 'state', 'fraction', 'timecreated', 'userid', 'name', 'value'), - array(1, 1, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 1, 0, 'todo', null, 1256233700, 1, null, null), - array(2, 1, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 2, 1, 'complete', null, 1256233705, 1, 'answer', '1'), - array(3, 1, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 1, '', '', '', 1256233790, 3, 2, 'complete', null, 1256233710, 1, 'answer', '0'), - array(4, 1, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 4, 3, 'complete', null, 1256233715, 1, 'answer', '1'), - array(5, 1, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 5, 4, 'gradedright', 1.0000000, 1256233720, 1, '-finish', '1'), - array(6, 1, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1, '-comment', 'Not good enough!'), - array(7, 1, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1, '-mark', '1'), - array(8, 1, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1, '-maxmark', '2'), + array(1, 1, 123, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 1, 0, 'todo', null, 1256233700, 1, null, null), + array(2, 1, 123, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 2, 1, 'complete', null, 1256233705, 1, 'answer', '1'), + array(3, 1, 123, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 1, '', '', '', 1256233790, 3, 2, 'complete', null, 1256233710, 1, 'answer', '0'), + array(4, 1, 123, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 4, 3, 'complete', null, 1256233715, 1, 'answer', '1'), + array(5, 1, 123, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 5, 4, 'gradedright', 1.0000000, 1256233720, 1, '-finish', '1'), + array(6, 1, 123, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1, '-comment', 'Not good enough!'), + array(7, 1, 123, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1, '-mark', '1'), + array(8, 1, 123, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 6, 5, 'mangrpartial', 0.5000000, 1256233790, 1, '-maxmark', '2'), )); $question = test_question_maker::make_a_truefalse_question(); @@ -320,12 +320,12 @@ class question_attempt_db_test extends data_loading_method_test_base { public function test_load_missing_question() { $records = testing_db_record_builder::build_db_records(array( - array('id', 'questionattemptid', 'questionusageid', 'slot', + array('id', 'questionattemptid', 'contextid', 'questionusageid', 'slot', 'behaviour', 'questionid', 'maxmark', 'minfraction', 'flagged', 'questionsummary', 'rightanswer', 'responsesummary', 'timemodified', 'attemptstepid', 'sequencenumber', 'state', 'fraction', 'timecreated', 'userid', 'name', 'value'), - array(1, 1, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 1, 0, 'todo', null, 1256233700, 1, null, null), + array(1, 1, 123, 1, 1, 'deferredfeedback', -1, 2.0000000, 0.0000000, 0, '', '', '', 1256233790, 1, 0, 'todo', null, 1256233700, 1, null, null), )); question_bank::start_unit_test(); diff --git a/question/engine/simpletest/testquestionattemptstepiterator.php b/question/engine/simpletest/testquestionattemptstepiterator.php index 224510ec399..a3e2f0dea44 100644 --- a/question/engine/simpletest/testquestionattemptstepiterator.php +++ b/question/engine/simpletest/testquestionattemptstepiterator.php @@ -36,7 +36,7 @@ class question_attempt_step_iterator_test extends UnitTestCase { public function setUp() { $question = test_question_maker::make_a_description_question(); - $this->qa = new testable_question_attempt($question, 0); + $this->qa = new testable_question_attempt($question, 0, 0); for ($i = 0; $i < 3; $i++) { $step = new question_attempt_step(array('i' => $i)); $this->qa->add_step($step); diff --git a/question/preview.php b/question/preview.php index f8d65f1281e..4376b1d787a 100644 --- a/question/preview.php +++ b/question/preview.php @@ -33,7 +33,6 @@ require_once(dirname(__FILE__) . '/../config.php'); require_once($CFG->libdir . '/questionlib.php'); -require_once($CFG->libdir . '/formslib.php'); require_once(dirname(__FILE__) . '/previewlib.php'); // Get and validate question id. diff --git a/question/previewlib.php b/question/previewlib.php index 273ad58fc1f..d75ee7c6530 100644 --- a/question/previewlib.php +++ b/question/previewlib.php @@ -26,6 +26,9 @@ */ +require_once($CFG->libdir . '/formslib.php'); + + /** * Settings form for the preview options. * @@ -194,67 +197,34 @@ class question_preview_options extends question_display_options { * @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 integer $qubaid the question_usage this image belongs to. + * @param integer $slot the relevant slot within the usage. * @param array $args the remaining bits of the file path. * @param bool $forcedownload whether the user must be forced to download the file. * @return bool false if file not found, does not return if found - justsend the file */ function question_preview_question_pluginfile($course, $context, $component, - $filearea, $attemptid, $questionid, $args, $forcedownload) { - global $USER, $SESSION, $DB, $CFG; - require_once($CFG->dirroot . '/mod/quiz/locallib.php'); + $filearea, $qubaid, $slot, $args, $forcedownload) { + global $USER, $DB, $CFG; - if (!$question = $DB->get_record('question', array('id' => $questionid))) { - return send_file_not_found(); - } + $quba = question_engine::load_questions_usage_by_activity($qubaid); - if (!question_has_capability_on($question, 'use', $question->category)) { + if ($quba->get_owning_context()->id != $context->id) { send_file_not_found(); } - if (!isset($SESSION->quizpreview->states) || $SESSION->quizpreview->questionid != $questionid) { + if (!question_has_capability_on($quba->get_question($slot), 'use')) { send_file_not_found(); } - $states = end($SESSION->quizpreview->states); - if (!array_key_exists($question->id, $states)) { - send_file_not_found(); - } - $state = $states[$question->id]; - - // Build fake cmoptions - $quiz = new cmoptions; - $quiz->id = 0; - $quiz->review = get_config('quiz', 'review'); - if (empty($course->id)) { - $quiz->course = SITEID; - } else { - $quiz->course = $course->id; - } - $quiz->decimalpoints = get_config('quiz', 'decimalpoints'); - - $questions[$question->id] = $question; - get_question_options($questions); - - // Build fake attempt - $timenow = time(); - $attempt = new stdClass(); - $attempt->quiz = $quiz->id; - $attempt->userid = $USER->id; - $attempt->attempt = 0; - $attempt->sumgrades = 0; - $attempt->timestart = $timenow; - $attempt->timefinish = 0; - $attempt->timemodified = $timenow; - $attempt->uniqueid = 0; - $attempt->id = 0; - $attempt->layout = $question->id; - - $options = quiz_get_renderoptions($quiz, $attempt, $context, $state); - $options->noeditlink = true; - // TODO: mulitichoice type needs quiz id to get maxgrade - $options->quizid = 0; - - if (!question_check_file_access($question, $state, $options, $context->id, $component, + $options = new question_display_options(); + $options->feedback = question_display_options::VISIBLE; + $options->numpartscorrect = question_display_options::VISIBLE; + $options->generalfeedback = question_display_options::VISIBLE; + $options->rightanswer = question_display_options::VISIBLE; + $options->manualcomment = question_display_options::VISIBLE; + $options->history = question_display_options::VISIBLE; + if (!$quba->check_file_access($slot, $options, $component, $filearea, $args, $forcedownload)) { send_file_not_found(); } diff --git a/question/type/description/renderer.php b/question/type/description/renderer.php index 70eb9933f4e..701fd8faee9 100644 --- a/question/type/description/renderer.php +++ b/question/type/description/renderer.php @@ -35,7 +35,7 @@ class qtype_description_renderer extends qtype_renderer { public function formulation_and_controls(question_attempt $qa, question_display_options $options) { - return html_writer::tag('div', $qa->get_question()->format_questiontext(), + return html_writer::tag('div', $qa->get_question()->format_questiontext($qa), array('class' => 'qtext')); } } diff --git a/question/type/edit_question_form.php b/question/type/edit_question_form.php index 98140597680..5419fb48afa 100644 --- a/question/type/edit_question_form.php +++ b/question/type/edit_question_form.php @@ -315,7 +315,8 @@ abstract class question_edit_form extends moodleform { $repeated = array(); $repeated[] = $mform->createElement('header', 'answerhdr', get_string('hintn', 'question')); - $repeated[] = $mform->createElement('htmleditor', 'hint', get_string('hinttext', 'question'), array('size' => 50)); + $repeated[] = $mform->createElement('editor', 'hint', get_string('hinttext', 'question'), + array('rows' => 5), $this->editoroptions); $repeatedoptions['hint']['type'] = PARAM_RAW; if ($withclearwrong) { @@ -427,16 +428,6 @@ abstract class question_edit_form extends moodleform { // subclass adds data_preprocessing code here $question = $this->data_preprocessing($question); - if (!empty($question->hints)) { - $i = 0; - foreach ($question->hints as $hint) { - $question->hint[$i] = $hint->hint; - $question->hintclearwrong[$i] = !empty($hint->clearwrong); - $question->hintshownumcorrect[$i] = !empty($hint->shownumcorrect); - $i += 1; - } - } - parent::set_data($question); } @@ -446,7 +437,74 @@ abstract class question_edit_form extends moodleform { * @param array $question - array to fill in with the default values */ function data_preprocessing($question) { - // TODO is this really necessary? + return $question; + } + + protected function data_preprocessing_answers($question) { + if (empty($question->options->answers)) { + return $question; + } + + $key = 0; + foreach ($question->options->answers as $answer){ + $question->answer[$key] = $answer->answer; + $question->fraction[$key] = 0 + $answer->fraction; + $question->feedback[$key] = array(); + + // Evil hack alert. Formslib can store defaults in two ways for + // repeat elements: ->_defaultValues['fraction[0]'] and + // ->_defaultValues['fraction'][0]. The $repeatedoptions['fraction']['default'] = 0; + // bit above means that ->_defaultValues['fraction[0]'] has already + // been set, but we are using object notation here, so we will be setting + // ->_defaultValues['fraction'][0]. That does not work, so we have to unset + // ->_defaultValues['fraction[0]'] + unset($this->_form->_defaultValues["fraction[$key]"]); + + // Prepare the feedback editor to display files in draft area + $draftitemid = file_get_submitted_draft_itemid('feedback['.$key.']'); + $question->feedback[$key]['text'] = file_prepare_draft_area( + $draftitemid, // 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[$key]['itemid'] = $draftitemid; + $question->feedback[$key]['format'] = $answer->feedbackformat; + $key++; + } + return $question; + } + + protected function data_preprocessing_hints($question, $withclearwrong = false, $withshownumpartscorrect = false) { + if (empty($question->hints)) { + return $question; + } + + $key = 0; + foreach ($question->hints as $hint){ + $question->hint[$key] = array(); + + // prepare feedback editor to display files in draft area + $draftitemid = file_get_submitted_draft_itemid('hint['.$key.']'); + $question->hint[$key]['text'] = file_prepare_draft_area( + $draftitemid, // draftid + $this->context->id, // context + 'question', // component + 'hint', // filarea + !empty($hint->id) ? (int) $hint->id : null, // itemid + $this->fileoptions, // options + $hint->hint // text + ); + $question->hint[$key]['itemid'] = $draftitemid; + $question->hint[$key]['format'] = $hint->hintformat; + $key++; + } + + // TODO $withclearwrong, $withshownumpartscorrect + return $question; } diff --git a/question/type/questionbase.php b/question/type/questionbase.php index 870aba08259..71513a88513 100644 --- a/question/type/questionbase.php +++ b/question/type/questionbase.php @@ -235,23 +235,48 @@ abstract class question_definition { * parts of the question do not need to be cleaned, and student input does. * @return string the text formatted for output by format_text. */ - public function format_text($text, $qa, $component, $filearea, $clean = false) { + public function format_text($text, $qa, $component, $filearea, $itemid, $clean = false) { $formatoptions = new stdClass; $formatoptions->noclean = !$clean; $formatoptions->para = false; -// TODO $itemid needs to be an argument too. - $text = $qa->rewrite_pluginfile_urls($text, $component, $filearea); + $text = $qa->rewrite_pluginfile_urls($text, $component, $filearea, $itemid); return format_text($text, $this->questiontextformat, $formatoptions); } /** @return the result of applying {@link format_text()} to the question text. */ public function format_questiontext($qa) { - return $this->format_text($this->questiontext, $qa, 'question', 'questiontext'); + return $this->format_text($this->questiontext, $qa, + 'question', 'questiontext', $this->id); } /** @return the result of applying {@link format_text()} to the general feedback. */ public function format_generalfeedback($qa) { - return $this->format_text($this->generalfeedback, $qa, 'question', 'generalfeedback'); + return $this->format_text($this->generalfeedback, $qa, + 'question', 'generalfeedback', $this->id); + } + + /** + * Checks whether the users is allow to be served a particular file. + * @param question_attempt the question attempt being displayed. + * @param question_display_options $options the options that control display of the question. + * @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 boolean $forcedownload whether the user must be forced to download the file. + * @return boolean true if the user can access this file. + */ + public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) { + if ($component == 'question' && $filearea == 'questiontext') { + // Question text always visible. + return true; + + } else if ($component == 'question' && $filearea == 'generalfeedback') { + return $options->generalfeedback; + + } else { + // Unrecognised component or filearea. + return false; + } } } @@ -495,6 +520,10 @@ abstract class question_graded_automatically extends question_with_responses } return $this->hints[$hintnumber]; } + + public function format_hint(question_hint $hint, question_attempt $qa) { + return $this->format_text($hint->hint, $qa, 'question', 'hint', $hint->id); + } } @@ -625,15 +654,23 @@ class question_answer { * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class question_hint { - /** @var The feedback hint to be shown. */ + /** @var integer The hint id. */ + public $id; + /** @var string The feedback hint to be shown. */ public $hint; + /** @var integer The corresponding text FORMAT_... type. */ + public $hintformat; /** * Constructor. + * @param integer the hint id from the database. * @param string $hint The hint text + * @param integer the corresponding text FORMAT_... type. */ - public function __construct($hint) { + public function __construct($id, $hint, $hintformat) { + $this->id = $id; $this->hint = $hint; + $this->hintformat = $hintformat; } /** @@ -642,7 +679,7 @@ class question_hint { * @return question_hint */ public static function load_from_record($row) { - return new question_hint($row->hint); + return new question_hint($row->id, $row->hint, $row->hintformat); } /** @@ -672,12 +709,14 @@ class question_hint_with_parts extends question_hint { /** * Constructor. + * @param integer the hint id from the database. * @param string $hint The hint text + * @param integer the corresponding text FORMAT_... type. * @param boolean $shownumcorrect whether the number of right parts should be shown * @param boolean $clearwrong whether the wrong parts should be reset. */ - public function __construct($hint, $shownumcorrect, $clearwrong) { - parent::__construct($hint); + public function __construct($id, $hint, $hintformat, $shownumcorrect, $clearwrong) { + parent::__construct($id, $hint, $hintformat); $this->shownumcorrect = $shownumcorrect; $this->clearwrong = $clearwrong; } @@ -688,7 +727,8 @@ class question_hint_with_parts extends question_hint { * @return question_hint_with_parts */ public static function load_from_record($row) { - return new question_hint_with_parts($row->hint, $row->shownumcorrect, $row->clearwrong); + return new question_hint_with_parts($row->id, $row->hint, $row->hintformat, + $row->shownumcorrect, $row->clearwrong); } public function adjust_display_options(question_display_options $options) { diff --git a/question/type/questiontype.php b/question/type/questiontype.php index 92eb7ec3373..865198c85fd 100644 --- a/question/type/questiontype.php +++ b/question/type/questiontype.php @@ -462,7 +462,9 @@ class question_type { public function save_hints($formdata, $withparts = false) { global $DB; - $DB->delete_records('question_hints', array('questionid' => $formdata->id)); + $context = $formdata->context; + + $oldhints = $DB->get_records('question_hints', array('questionid' => $formdata->id)); if (!empty($formdata->hint)) { $numhints = max(array_keys($formdata->hint)) + 1; @@ -485,24 +487,44 @@ class question_type { } for ($i = 0; $i < $numhints; $i += 1) { - $hint = new stdClass; - $hint->hint = $formdata->hint[$i]; - $hint->questionid = $formdata->id; - - if (html_is_blank($hint->hint)) { - $hint->hint = ''; + if (html_is_blank($formdata->hint[$i]['text'])) { + $formdata->hint[$i]['text'] = ''; } if ($withparts) { - $hint->clearwrong = !empty($formdata->hintclearwrong[$i]); - $hint->shownumcorrect = !empty($formdata->hintshownumcorrect[$i]); + $clearwrong = !empty($formdata->hintclearwrong[$i]); + $shownumcorrect = !empty($formdata->hintshownumcorrect[$i]); } - if (empty($hint->hint) && empty($hint->clearwrong) && empty($hint->shownumcorrect)) { + if (empty($formdata->hint[$i]['text']) && empty($clearwrong) && empty($shownumcorrect)) { continue; } - $DB->insert_record('question_hints', $hint); + // Update an existing answer if possible. + $hint = array_shift($oldhints); + if (!$hint) { + $hint = new stdClass(); + $hint->questionid = $formdata->id; + $hint->hint = ''; + $hint->id = $DB->insert_record('question_hints', $hint); + } + + $hint->hint = ''; + $hint->hint = $this->import_or_save_files($formdata->hint[$i], + $context, 'question', 'hint', $hint->id); + $hint->hintformat = $formdata->hint[$i]['format']; + if ($withparts) { + $hint->clearwrong = $clearwrong; + $hint->shownumcorrect = $shownumcorrect; + } + $DB->update_record('question_hints', $hint); + } + + // Delete any remaining old hints. + $fs = get_file_storage(); + foreach($oldhints as $oldhint) { + $fs->delete_area_files($context->id, 'question', 'hint', $oldhint->id); + $DB->delete_records('question_hints', array('id' => $oldhint->id)); } } diff --git a/question/type/rendererbase.php b/question/type/rendererbase.php index 7fd9874cfd2..9834f6404ef 100644 --- a/question/type/rendererbase.php +++ b/question/type/rendererbase.php @@ -152,8 +152,8 @@ abstract class qtype_renderer extends plugin_renderer_base { * @return string HTML fragment. */ protected function hint(question_attempt $qa, question_hint $hint) { - return html_writer::nonempty_tag('div', $qa->get_question()->format_text( - $hint->hint, $qa, 'question', 'hint'), array('class' => 'hint')); + return html_writer::nonempty_tag('div', + $qa->get_question()->format_hint($hint, $qa), array('class' => 'hint')); } /** @@ -256,7 +256,8 @@ abstract class qtype_with_combined_feedback_renderer extends qtype_renderer { } if ($question->$feedbackfield) { - $feedback .= $question->format_text($question->$feedbackfield, $qa, 'question', $feedbackfield); + $feedback .= $question->format_text($question->$feedbackfield, $qa, + 'question', $feedbackfield, $question->id); } return $feedback; diff --git a/question/type/shortanswer/edit_shortanswer_form.php b/question/type/shortanswer/edit_shortanswer_form.php index b8349c71fdc..f4046955ba3 100644 --- a/question/type/shortanswer/edit_shortanswer_form.php +++ b/question/type/shortanswer/edit_shortanswer_form.php @@ -56,36 +56,10 @@ class question_edit_shortanswer_form extends question_edit_form { } function data_preprocessing($question) { - if (isset($question->options)){ - $answers = $question->options->answers; - if (count($answers)) { - $key = 0; - foreach ($answers as $answer){ - $default_values['answer['.$key.']'] = $answer->answer; - $default_values['fraction['.$key.']'] = $answer->fraction; - $default_values['feedback['.$key.']'] = array(); - - // prepare feedback editor to display files in draft area - $draftid_editor = file_get_submitted_draft_itemid('feedback['.$key.']'); - $default_values['feedback['.$key.']']['text'] = file_prepare_draft_area( - $draftid_editor, // draftid - $this->context->id, // context - 'question', // component - 'answerfeedback', // filarea - !empty($answer->id)?(int)$answer->id:null, // itemid - $this->fileoptions, // options - $answer->feedback // text - ); - $default_values['feedback['.$key.']']['itemid'] = $draftid_editor; - // prepare files code block ends - - $default_values['feedback['.$key.']']['format'] = $answer->feedbackformat; - $key++; - } - } - $default_values['usecase'] = $question->options->usecase; - $question = (object)((array)$question + $default_values); - } + $question = parent::data_preprocessing($question); + $question = $this->data_preprocessing_answers($question); + $question = $this->data_preprocessing_hints($question); + $question->usecase = $question->options->usecase; return $question; } @@ -114,6 +88,7 @@ class question_edit_shortanswer_form extends question_edit_form { } return $errors; } + public function qtype() { return 'shortanswer'; } diff --git a/question/type/shortanswer/question.php b/question/type/shortanswer/question.php index 51c044b2cce..e5406471806 100644 --- a/question/type/shortanswer/question.php +++ b/question/type/shortanswer/question.php @@ -97,4 +97,24 @@ class qtype_shortanswer_question extends question_graded_by_strategy return preg_match($regexp, trim($string)); } + + function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) { + if ($component == 'question' && $filearea == 'answerfeedback') { + $currentanswer = $qa->get_last_qt_var('answer'); + $answer = $qa->get_question()->get_matching_answer(array('answer' => $currentanswer)); + $answerid = reset($args); // itemid is answer id. + return $options->feedback && $answerid == $answer->id; + + } else if ($component == 'question' && $filearea == 'hint') { + if (!$options->feedback) { + return false; + } + $hint = $qa->get_applicable_hint(); + $hintid = reset($args); // itemid is hint id. + return $hintid == $hint->id; + + } else { + return parent::check_file_access($qa, $options, $component, $filearea, $args, $forcedownload); + } + } } diff --git a/question/type/shortanswer/questiontype.php b/question/type/shortanswer/questiontype.php index 3ac8a993c11..e29379c4507 100644 --- a/question/type/shortanswer/questiontype.php +++ b/question/type/shortanswer/questiontype.php @@ -147,30 +147,4 @@ class qtype_shortanswer extends question_type { return array($questiondata->id => $responses); } - - public 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); - } - } } diff --git a/question/type/shortanswer/renderer.php b/question/type/shortanswer/renderer.php index bb051520923..4d88de54792 100644 --- a/question/type/shortanswer/renderer.php +++ b/question/type/shortanswer/renderer.php @@ -103,7 +103,8 @@ class qtype_shortanswer_renderer extends qtype_renderer { return ''; } - return $question->format_text($answer->feedback, $qa, 'question', 'answerfeedback'); + return $question->format_text($answer->feedback, $qa, + 'question', 'answerfeedback', $answer->id); } public function correct_response(question_attempt $qa) { diff --git a/question/type/simpletest/testquestionbase.php b/question/type/simpletest/testquestionbase.php index 156d81dade7..4d48b0b1f98 100644 --- a/question/type/simpletest/testquestionbase.php +++ b/question/type/simpletest/testquestionbase.php @@ -97,19 +97,27 @@ class question_first_matching_answer_grading_strategy_test extends UnitTestCase class question_hint_test extends UnitTestCase { public function test_basic() { $row = new stdClass; + $row->id = 123; $row->hint = 'A hint'; + $row->hintformat = FORMAT_HTML; $hint = question_hint::load_from_record($row); + $this->assertEqual($row->id, $hint->id); $this->assertEqual($row->hint, $hint->hint); + $this->assertEqual($row->hintformat, $hint->hintformat); } public function test_with_parts() { $row = new stdClass; + $row->id = 123; $row->hint = 'A hint'; + $row->hintformat = FORMAT_HTML; $row->shownumcorrect = 1; $row->clearwrong = 1; $hint = question_hint_with_parts::load_from_record($row); + $this->assertEqual($row->id, $hint->id); $this->assertEqual($row->hint, $hint->hint); + $this->assertEqual($row->hintformat, $hint->hintformat); $this->assertTrue($hint->shownumcorrect); $this->assertTrue($hint->clearwrong); } diff --git a/question/type/truefalse/question.php b/question/type/truefalse/question.php index 7e1a9b8f008..fae901267f5 100644 --- a/question/type/truefalse/question.php +++ b/question/type/truefalse/question.php @@ -35,6 +35,8 @@ class qtype_truefalse_question extends question_graded_automatically { public $rightanswer; public $truefeedback; public $falsefeedback; + public $trueanswerid; + public $falseanswerid; public function get_expected_data() { return array('answer' => PARAM_INTEGER); @@ -94,4 +96,17 @@ class qtype_truefalse_question extends question_graded_automatically { } return array($fraction, question_state::graded_state_for_fraction($fraction)); } + + function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) { + if ($component == 'question' && $filearea == 'answerfeedback') { + $answerid = reset($args); // itemid is answer id. + $response = $qa->get_last_qt_var('answer', ''); + return $options->feedback && ( + ($answerid == $this->trueanswerid && $response) || + ($answerid == $this->falseanswerid && $response !== '')); + + } else { + return parent::check_file_access($qa, $options, $component, $filearea, $args, $forcedownload); + } + } } diff --git a/question/type/truefalse/questiontype.php b/question/type/truefalse/questiontype.php index b10a0996d26..a2c3a1a4217 100644 --- a/question/type/truefalse/questiontype.php +++ b/question/type/truefalse/questiontype.php @@ -134,6 +134,8 @@ class qtype_truefalse extends question_type { } $question->truefeedback = $answers[$questiondata->options->trueanswer]->feedback; $question->falsefeedback = $answers[$questiondata->options->falseanswer]->feedback; + $question->trueanswerid = $questiondata->options->trueanswer; + $question->falseanswerid = $questiondata->options->falseanswer; } function delete_question($questionid, $contextid) { @@ -153,26 +155,6 @@ class qtype_truefalse extends question_type { $this->delete_files_in_answers($questionid, $contextid); } - 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 get_random_guess_score($questiondata) { return 0.5; } diff --git a/question/type/truefalse/renderer.php b/question/type/truefalse/renderer.php index 17a99b4556c..7f2f2cdb370 100644 --- a/question/type/truefalse/renderer.php +++ b/question/type/truefalse/renderer.php @@ -124,9 +124,11 @@ class qtype_truefalse_renderer extends qtype_renderer { $response = $qa->get_last_qt_var('answer', ''); if ($response) { - return $question->format_text($question->truefeedback, $qa, 'question', 'answerfeedback'); + return $question->format_text($question->truefeedback, $qa, + 'question', 'answerfeedback', $question->trueanswerid); } else { - return $question->format_text($question->falsefeedback, $qa, 'question', 'answerfeedback'); + return $question->format_text($question->falsefeedback, $qa, + 'question', 'answerfeedback', $question->falseanswerid); } } diff --git a/theme/base/style/question.css b/theme/base/style/question.css index 72074d84bb4..8c1f7f75288 100644 --- a/theme/base/style/question.css +++ b/theme/base/style/question.css @@ -64,9 +64,7 @@ body.jsenabled #qtypechoicecontainer {display: block;} .que .im-feedback, .que .feedback, .que p {margin: 0 0 0.5em;} -.que .prompt {float: left;width: 15%;height: 2em;padding-top: 0.3em;} .que .qtext {margin-bottom: 1.5em;} -.que .submit {position: relative;clear: both;float: left;} .que .correct {background-color: #afa;} .que .notanswered, @@ -82,11 +80,6 @@ body.jsenabled #qtypechoicecontainer {display: block;} .que .history table {width: 100%;margin: 0;} .que .history .current {font-weight: bold;} -.que.description .info {margin: 0.5em 0 0.5em 0.2em;width: 1em;} -.que.description .content, -.que.description .generalfeedback {margin-left: 1.2em;margin-bottom: 0.5em;float: none;width: auto;} -.que.description .qtext {margin-bottom: 0;} - .multianswer .submit {margin-top: 1em;} .multichoice td.feedback {width: auto;vertical-align: top;padding-top: 0.3em;border-style: none;} @@ -108,10 +101,6 @@ body.jsenabled #qtypechoicecontainer {display: block;} .importerror {margin-top: 10px;border-bottom: 1px solid #555;} .mform .que.comment .fitemtitle {width: 20%;} -/**** GRRRRR ***/ -/* MSIE Hack */ -* html .que .submit {float: none;} - /** Overide for RTL layout **/ .dir-rtl #qtypechoicecontainer #chooseqtype .instruction, .dir-rtl #qtypechoicecontainer #chooseqtype .qtypesummary {right: 60%;left: 0%; border-left:0;border-right: 1px solid grey;}