diff --git a/lib/questionlib.php b/lib/questionlib.php index 9b47a4b3929..8371f3bc3e3 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -210,56 +210,16 @@ function question_context_has_any_questions($context) { /** * Returns list of 'allowed' grades for grade selection * formatted suitably for dropdown box function + * + * @deprecated since 2.1. Use {@link question_bank::fraction_options()} or + * {@link question_bank::fraction_options_full()} instead. + * * @return object ->gradeoptionsfull full array ->gradeoptions +ve only */ function get_grade_options() { - // define basic array of grades. This list comprises all fractions of the form: - // a. p/q for q <= 6, 0 <= p <= q - // b. p/10 for 0 <= p <= 10 - // c. 1/q for 1 <= q <= 10 - // d. 1/20 - $grades = array( - 1.0000000, - 0.9000000, - 0.8333333, - 0.8000000, - 0.7500000, - 0.7000000, - 0.6666667, - 0.6000000, - 0.5000000, - 0.4000000, - 0.3333333, - 0.3000000, - 0.2500000, - 0.2000000, - 0.1666667, - 0.1428571, - 0.1250000, - 0.1111111, - 0.1000000, - 0.0500000, - 0.0000000); - - // iterate through grades generating full range of options - $gradeoptionsfull = array(); - $gradeoptions = array(); - foreach ($grades as $grade) { - $percentage = 100 * $grade; - $gradeoptions["$grade"] = $percentage . '%'; - $gradeoptionsfull["$grade"] = $percentage . '%'; - $gradeoptionsfull['' . (-$grade)] = (-$percentage) . '%'; - } - $gradeoptionsfull['0'] = $gradeoptions['0'] = get_string('none'); - - // sort lists - arsort($gradeoptions, SORT_NUMERIC); - arsort($gradeoptionsfull, SORT_NUMERIC); - - // construct return object $grades = new stdClass(); - $grades->gradeoptions = $gradeoptions; - $grades->gradeoptionsfull = $gradeoptionsfull; + $grades->gradeoptions = question_bank::fraction_options(); + $grades->gradeoptionsfull = question_bank::fraction_options_full(); return $grades; } diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php index 85386e750e0..2ed1246ac96 100644 --- a/mod/quiz/attemptlib.php +++ b/mod/quiz/attemptlib.php @@ -756,6 +756,20 @@ class quiz_attempt { return $this->quba->get_question_state_string($slot, $showcorrectness); } + /** + * Return the grade obtained on a particular question, if the user is permitted + * to see it. You must previously have called load_question_states to load the + * state data about this question. + * + * @param int $slot the number used to identify this question within this attempt. + * @param bool $showcorrectness Whether right/partial/wrong states should + * be distinguised. + * @return string class name for this state. + */ + public function get_question_state_class($slot, $showcorrectness) { + return $this->quba->get_question_state_class($slot, $showcorrectness); + } + /** * Return the grade obtained on a particular question. * You must previously have called load_question_states to load the state @@ -1167,7 +1181,7 @@ abstract class quiz_nav_panel_base { $button = new quiz_nav_question_button(); $button->id = 'quiznavbutton' . $slot; $button->number = $qa->get_question()->_number; - $button->stateclass = $qa->get_state()->get_state_class($showcorrectness); + $button->stateclass = $qa->get_state_class($showcorrectness); if (!$showcorrectness && $button->stateclass == 'notanswered') { $button->stateclass = 'complete'; } @@ -1226,7 +1240,7 @@ class quiz_attempt_nav_panel extends quiz_nav_panel_base { public function render_end_bits(mod_quiz_renderer $output) { return html_writer::link($this->attemptobj->summary_url(), - get_string('endtest', 'quiz'), array('id' => 'endtestlink')) . + get_string('endtest', 'quiz'), array('class' => 'endtestlink')) . $output->countdown_timer() . $this->render_restart_preview_link($output); } diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index 9dfbc6bebfa..f9f57c989a8 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -481,43 +481,38 @@ function quiz_set_grade($newgrade, $quiz) { // Use a transaction, so that on those databases that support it, this is safer. $transaction = $DB->start_delegated_transaction(); - try { - // Update the quiz table. - $DB->set_field('quiz', 'grade', $newgrade, array('id' => $quiz->instance)); + // Update the quiz table. + $DB->set_field('quiz', 'grade', $newgrade, array('id' => $quiz->instance)); - // Rescaling the other data is only possible if the old grade was non-zero. - if ($quiz->grade > 1e-7) { - global $CFG; + // Rescaling the other data is only possible if the old grade was non-zero. + if ($quiz->grade > 1e-7) { + global $CFG; - $factor = $newgrade/$quiz->grade; - $quiz->grade = $newgrade; + $factor = $newgrade/$quiz->grade; + $quiz->grade = $newgrade; - // Update the quiz_grades table. - $timemodified = time(); - $DB->execute(" - UPDATE {quiz_grades} - SET grade = ? * grade, timemodified = ? - WHERE quiz = ? - ", array($factor, $timemodified, $quiz->id)); + // Update the quiz_grades table. + $timemodified = time(); + $DB->execute(" + UPDATE {quiz_grades} + SET grade = ? * grade, timemodified = ? + WHERE quiz = ? + ", array($factor, $timemodified, $quiz->id)); - // Update the quiz_feedback table. - $DB->execute(" - UPDATE {quiz_feedback} - SET mingrade = ? * mingrade, maxgrade = ? * maxgrade - WHERE quizid = ? - ", array($factor, $factor, $quiz->id)); - } - - // update grade item and send all grades to gradebook - quiz_grade_item_update($quiz); - quiz_update_grades($quiz); - - $transaction->allow_commit(); - return true; - - } catch (Exception $e) { - $transaction->rollback($e); + // Update the quiz_feedback table. + $DB->execute(" + UPDATE {quiz_feedback} + SET mingrade = ? * mingrade, maxgrade = ? * maxgrade + WHERE quizid = ? + ", array($factor, $factor, $quiz->id)); } + + // update grade item and send all grades to gradebook + quiz_grade_item_update($quiz); + quiz_update_grades($quiz); + + $transaction->allow_commit(); + return true; } /** @@ -869,6 +864,9 @@ function quiz_question_edit_button($cmid, $question, $returnurl, $contentafteric // Build the icon. if ($action) { + if ($returnurl instanceof moodle_url) { + $returnurl = str_replace($CFG->wwwroot, '', $returnurl->out(false)); + } $questionparams = array('returnurl' => $returnurl, 'cmid' => $cmid, 'id' => $question->id); $questionurl = new moodle_url("$CFG->wwwroot/question/question.php", $questionparams); return ' string The standard set of grade options (fractions) + * to use when editing questions, in the range 0 to 1 inclusive. Array keys + * are string becuase: a) we want grades to exactly 7 d.p., and b. you can't + * have float array keys in PHP. + * Initialised by {@link ensure_grade_options_initialised()}. + */ + public static function fraction_options() { + self::ensure_fraction_options_initialised(); + return self::$fractionoptions; + } + + /** @return array string => string The full standard set of (fractions) -1 to 1 inclusive. */ + public static function fraction_options_full() { + self::ensure_fraction_options_initialised(); + return self::$fractionoptionsfull; + } } diff --git a/question/engine/datalib.php b/question/engine/datalib.php index 3190a5c9db7..8702895f7b4 100644 --- a/question/engine/datalib.php +++ b/question/engine/datalib.php @@ -688,7 +688,7 @@ ORDER BY $this->delete_response_files($context->id, "IN ( SELECT id - FROM question_attempt_step + FROM {question_attempt_steps} WHERE questionattemptid $test)", $params); $this->db->delete_records_select('question_attempt_step_data', "attemptstepid IN ( diff --git a/question/engine/questionattempt.php b/question/engine/questionattempt.php index 083bb062a2d..7f9a268200e 100644 --- a/question/engine/questionattempt.php +++ b/question/engine/questionattempt.php @@ -517,6 +517,15 @@ class question_attempt { return $this->behaviour->get_state_string($showcorrectness); } + /** + * @param bool $showcorrectness Whether right/partial/wrong states should + * be distinguised. + * @return string a CSS class name for the current state. + */ + public function get_state_class($showcorrectness) { + return $this->get_state()->get_state_class($showcorrectness); + } + /** * @return int the timestamp of the most recent step in this question attempt. */ diff --git a/question/engine/questionusage.php b/question/engine/questionusage.php index 5811c8981ea..6583552d97a 100644 --- a/question/engine/questionusage.php +++ b/question/engine/questionusage.php @@ -246,6 +246,16 @@ class question_usage_by_activity { return $this->get_question_attempt($slot)->get_state_string($showcorrectness); } + /** + * @param int $slot the number used to identify this question within this usage. + * @param bool $showcorrectness Whether right/partial/wrong states should + * be distinguised. + * @return string a CSS class name for the current state. + */ + public function get_question_state_class($slot, $showcorrectness) { + return $this->get_question_attempt($slot)->get_state_class($showcorrectness); + } + /** * Get the time of the most recent action performed on a question. * @param int $slot the number used to identify this question within this usage. diff --git a/question/engine/simpletest/testquestionbank.php b/question/engine/simpletest/testquestionbank.php index 4610f90d93c..697a7a69381 100644 --- a/question/engine/simpletest/testquestionbank.php +++ b/question/engine/simpletest/testquestionbank.php @@ -58,4 +58,34 @@ class question_bank_test extends UnitTestCase { 'frog' => 'toad', )); } -} \ No newline at end of file + + public function test_fraction_options() { + $fractions = question_bank::fraction_options(); + $this->assertIdentical(get_string('none'), reset($fractions)); + $this->assertIdentical('0.0', key($fractions)); + $this->assertIdentical('5%', end($fractions)); + $this->assertIdentical('0.05', key($fractions)); + array_shift($fractions); + array_pop($fractions); + array_pop($fractions); + $this->assertIdentical('100%', reset($fractions)); + $this->assertIdentical('1.0', key($fractions)); + $this->assertIdentical('11.11111%', end($fractions)); + $this->assertIdentical('0.1111111', key($fractions)); + } + + public function test_fraction_options_full() { + $fractions = question_bank::fraction_options_full(); + $this->assertIdentical(get_string('none'), reset($fractions)); + $this->assertIdentical('0.0', key($fractions)); + $this->assertIdentical('-100%', end($fractions)); + $this->assertIdentical('-1.0', key($fractions)); + array_shift($fractions); + array_pop($fractions); + array_pop($fractions); + $this->assertIdentical('100%', reset($fractions)); + $this->assertIdentical('1.0', key($fractions)); + $this->assertIdentical('-83.33333%', end($fractions)); + $this->assertIdentical('-0.8333333', key($fractions)); + } +} diff --git a/question/format.php b/question/format.php index 23fbdcaaf2d..663e9176baf 100644 --- a/question/format.php +++ b/question/format.php @@ -54,8 +54,8 @@ class qformat_default { protected $importcontext = null; -// functions to indicate import/export functionality -// override to return true if implemented + // functions to indicate import/export functionality + // override to return true if implemented /** @return bool whether this plugin provides import functionality. */ public function provide_import() { @@ -80,7 +80,7 @@ class qformat_default { return '.txt'; } -// Accessor methods + // Accessor methods /** * set the category @@ -196,9 +196,9 @@ class qformat_default { $this->canaccessbackupdata = $canaccess; } -/*********************** - * IMPORTING FUNCTIONS - ***********************/ + /*********************** + * IMPORTING FUNCTIONS + ***********************/ /** * Handle parsing error @@ -304,8 +304,7 @@ class qformat_default { } // get list of valid answer grades - $grades = get_grade_options(); - $gradeoptionsfull = $grades->gradeoptionsfull; + $gradeoptionsfull = question_bank::fraction_options_full(); // check answer grades are valid // (now need to do this here because of 'stop on error': MDL-10689) @@ -316,11 +315,11 @@ class qformat_default { $fractions = $question->fraction; $answersvalid = true; // in case they are! foreach ($fractions as $key => $fraction) { - $newfraction = match_grade_options($gradeoptionsfull, $fraction, $this->matchgrades); - if ($newfraction===false) { + $newfraction = match_grade_options($gradeoptionsfull, $fraction, + $this->matchgrades); + if ($newfraction === false) { $answersvalid = false; - } - else { + } else { $fractions[$key] = $newfraction; } } @@ -328,8 +327,7 @@ class qformat_default { echo $OUTPUT->notification(get_string('invalidgrade', 'question')); ++$gradeerrors; continue; - } - else { + } else { $question->fraction = $fractions; } } @@ -338,7 +336,7 @@ class qformat_default { $questions = $goodquestions; // check for errors before we continue - if ($this->stoponerror and ($gradeerrors>0)) { + if ($this->stoponerror && $gradeerrors > 0) { return false; } @@ -410,7 +408,8 @@ class qformat_default { } // Give the question a unique version stamp determined by question_hash() - $DB->set_field('question', 'version', question_hash($question), array('id'=>$question->id)); + $DB->set_field('question', 'version', question_hash($question), + array('id' => $question->id)); } return true; } @@ -428,7 +427,8 @@ class qformat_default { return $count; } foreach ($questions as $question) { - if (!is_object($question) || !isset($question->qtype) || ($question->qtype == 'category')) { + if (!is_object($question) || !isset($question->qtype) || + ($question->qtype == 'category')) { continue; } $count++; @@ -471,7 +471,8 @@ class qformat_default { // Now create any categories that need to be created. foreach ($catnames as $catname) { - if ($category = $DB->get_record('question_categories', array('name' => $catname, 'contextid' => $context->id, 'parent' => $parent))) { + if ($category = $DB->get_record('question_categories', + array('name' => $catname, 'contextid' => $context->id, 'parent' => $parent))) { $parent = $category->id; } else { require_capability('moodle/question:managecategory', $context); @@ -613,10 +614,9 @@ class qformat_default { return true; } - -/******************* - * EXPORT FUNCTIONS - *******************/ + /******************* + * EXPORT FUNCTIONS + *******************/ /** * Provide export functionality for plugin questiontypes @@ -685,9 +685,10 @@ class qformat_default { $trackcategory = 0; // iterate through questions - foreach($questions as $question) { + foreach ($questions as $question) { // used by file api - $contextid = $DB->get_field('question_categories', 'contextid', array('id'=>$question->category)); + $contextid = $DB->get_field('question_categories', 'contextid', + array('id' => $question->category)); $question->contextid = $contextid; // do not export hidden questions @@ -748,7 +749,7 @@ class qformat_default { protected function get_category_path($id, $includecontext = true) { global $DB; - if (!$category = $DB->get_record('question_categories',array('id' =>$id))) { + if (!$category = $DB->get_record('question_categories', array('id' => $id))) { print_error('cannotfindcategory', 'error', '', $id); } $contextstring = $this->translator->context_to_string($category->contextid); diff --git a/question/type/calculated/edit_calculated_form.php b/question/type/calculated/edit_calculated_form.php index ee4c8aefe68..cd79a8f9ebf 100644 --- a/question/type/calculated/edit_calculated_form.php +++ b/question/type/calculated/edit_calculated_form.php @@ -146,10 +146,8 @@ class qtype_calculated_edit_form extends qtype_numerical_edit_form { $mform->addElement('hidden', 'answernumbering', 'abc'); $mform->setType('answernumbering', PARAM_SAFEDIR); - $creategrades = get_grade_options(); - $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'), - $creategrades->gradeoptions, 1, 1); + question_bank::fraction_options(), 1, 1); $repeated = array(); diff --git a/question/type/calculatedmulti/edit_calculatedmulti_form.php b/question/type/calculatedmulti/edit_calculatedmulti_form.php index c8f93ab7f0f..d2d93f5051a 100644 --- a/question/type/calculatedmulti/edit_calculatedmulti_form.php +++ b/question/type/calculatedmulti/edit_calculatedmulti_form.php @@ -147,9 +147,8 @@ class qtype_calculatedmulti_edit_form extends question_edit_form { get_string('answernumbering', 'qtype_multichoice'), $numberingoptions); $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)); + question_bank::fraction_options_full(), max(5, QUESTION_NUMANS_START)); $repeated = array(); // if ($this->editasmultichoice == 1) { diff --git a/question/type/calculatedsimple/edit_calculatedsimple_form.php b/question/type/calculatedsimple/edit_calculatedsimple_form.php index 132e7ed79b9..ad18e37cee1 100644 --- a/question/type/calculatedsimple/edit_calculatedsimple_form.php +++ b/question/type/calculatedsimple/edit_calculatedsimple_form.php @@ -331,9 +331,8 @@ class qtype_calculatedsimple_edit_form extends qtype_calculated_edit_form { $addstring = get_string("updatecategory", "qtype_calculated"); $mform->registerNoSubmitButton($addfieldsname); - $creategrades = get_grade_options(); $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'), - $creategrades->gradeoptions, 1, 1); + question_bank::fraction_options(), 1, 1); $this->add_unit_options($mform, $this); $this->add_unit_fields($mform, $this); diff --git a/question/type/essay/question.php b/question/type/essay/question.php index f03ad155e72..3f3c2c4e284 100644 --- a/question/type/essay/question.php +++ b/question/type/essay/question.php @@ -70,8 +70,8 @@ class qtype_essay_question extends question_with_responses { if (isset($response['answer'])) { $formatoptions = new stdClass(); $formatoptions->para = false; - return shorten_text(html_to_text(format_text( - $response['answer'], FORMAT_HTML, $formatoptions), 0, false), 200); + return html_to_text(format_text( + $response['answer'], FORMAT_HTML, $formatoptions), 0, false); } else { return null; } diff --git a/question/type/essay/simpletest/testquestion.php b/question/type/essay/simpletest/testquestion.php index 1b2fe5f552b..bd85d1f0c61 100644 --- a/question/type/essay/simpletest/testquestion.php +++ b/question/type/essay/simpletest/testquestion.php @@ -45,8 +45,7 @@ class qtype_essay_question_test extends UnitTestCase { public function test_summarise_response() { $longstring = str_repeat('0123456789', 50); $essay = test_question_maker::make_an_essay_question(); - $summary = $essay->summarise_response(array('answer' => $longstring)); - $this->assertTrue(strlen($summary) < 250); - $this->assertEqual(substr($longstring, 0, 100), substr($summary, 0, 100)); + $this->assertEqual($longstring, + $essay->summarise_response(array('answer' => $longstring))); } } diff --git a/question/type/missingtype/edit_missingtype_form.php b/question/type/missingtype/edit_missingtype_form.php index 1905238605b..128f3ababee 100644 --- a/question/type/missingtype/edit_missingtype_form.php +++ b/question/type/missingtype/edit_missingtype_form.php @@ -45,9 +45,8 @@ class qtype_missingtype_edit_form extends question_edit_form { * @param object $mform the form being built. */ protected function definition_inner($mform) { - $creategrades = get_grade_options(); $this->add_per_answer_fields($mform, get_string('answerno', 'qtype_missingtype', '{no}'), - $creategrades->gradeoptionsfull); + question_bank::fraction_options_full()); } public function set_data($question) { diff --git a/question/type/multichoice/edit_multichoice_form.php b/question/type/multichoice/edit_multichoice_form.php index 453bff1c7bc..3a46415b535 100644 --- a/question/type/multichoice/edit_multichoice_form.php +++ b/question/type/multichoice/edit_multichoice_form.php @@ -58,9 +58,8 @@ class qtype_multichoice_edit_form extends question_edit_form { qtype_multichoice::get_numbering_styles()); $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)); + question_bank::fraction_options_full(), max(5, QUESTION_NUMANS_START)); $this->add_combined_feedback_fields(true); $mform->disabledIf('shownumcorrect', 'single', 'eq', 1); diff --git a/question/type/numerical/edit_numerical_form.php b/question/type/numerical/edit_numerical_form.php index 17f32dcc1f8..38922a8b70e 100644 --- a/question/type/numerical/edit_numerical_form.php +++ b/question/type/numerical/edit_numerical_form.php @@ -38,9 +38,8 @@ require_once($CFG->dirroot . '/question/type/numerical/questiontype.php'); class qtype_numerical_edit_form extends question_edit_form { protected function definition_inner($mform) { - $creategrades = get_grade_options(); $this->add_per_answer_fields($mform, get_string('answerno', 'qtype_numerical', '{no}'), - $creategrades->gradeoptions); + question_bank::fraction_options()); $this->add_unit_options($mform); $this->add_unit_fields($mform); diff --git a/question/type/shortanswer/edit_shortanswer_form.php b/question/type/shortanswer/edit_shortanswer_form.php index c6663cf4af7..49fabf2abae 100644 --- a/question/type/shortanswer/edit_shortanswer_form.php +++ b/question/type/shortanswer/edit_shortanswer_form.php @@ -48,9 +48,8 @@ class qtype_shortanswer_edit_form extends question_edit_form { get_string('filloutoneanswer', 'qtype_shortanswer')); $mform->closeHeaderBefore('answersinstruct'); - $creategrades = get_grade_options(); $this->add_per_answer_fields($mform, get_string('answerno', 'qtype_shortanswer', '{no}'), - $creategrades->gradeoptions); + question_bank::fraction_options()); $this->add_interactive_settings(); } diff --git a/question/type/truefalse/edit_truefalse_form.php b/question/type/truefalse/edit_truefalse_form.php index 599e12561ed..936035c801d 100644 --- a/question/type/truefalse/edit_truefalse_form.php +++ b/question/type/truefalse/edit_truefalse_form.php @@ -75,23 +75,19 @@ class qtype_truefalse_edit_form extends question_edit_form { $draftid = file_get_submitted_draft_itemid('trueanswer'); $answerid = $question->options->trueanswer; - $text = $trueanswer->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 + $draftid, // draftid + $this->context->id, // context + 'question', // component + 'answerfeedback', // filarea + !empty($answerid) ? (int) $answerid : null, // itemid + $this->fileoptions, // options + $trueanswer->feedback // text ); $question->feedbacktrue['itemid'] = $draftid; - - return $question; } if (!empty($question->options->falseanswer)) { @@ -99,19 +95,17 @@ class qtype_truefalse_edit_form extends question_edit_form { $draftid = file_get_submitted_draft_itemid('falseanswer'); $answerid = $question->options->falseanswer; - $text = $falseanswer->feedback; $question->feedbackfalse = array(); - $question->feedbackfalse['text'] = $falseanswer->feedback; $question->feedbackfalse['format'] = $falseanswer->feedbackformat; $question->feedbackfalse['text'] = file_prepare_draft_area( - $draftid, // draftid + $draftid, // draftid $this->context->id, // context - 'question', // component - 'answerfeedback', // filarea - !empty($answerid)?(int)$answerid:null, // itemid + 'question', // component + 'answerfeedback', // filarea + !empty($answerid) ? (int) $answerid : null, // itemid $this->fileoptions, // options - $text // text + $falseanswer->feedback // text ); $question->feedbackfalse['itemid'] = $draftid; }