MDL-20636 Now the quiz review page works.

This commit is contained in:
Tim Hunt 2011-02-09 16:56:44 +00:00
parent 167f1562f8
commit b2607ccc19
11 changed files with 280 additions and 255 deletions

View File

@ -166,8 +166,8 @@ class quiz_access_manager {
/**
* Do any of the rules mean that this student will no be allowed any further attempts at this
* quiz. Used, for example, to change the label by the grade displayed on the view page from
* 'your current score is' to 'your final score is'.
* quiz. Used, for example, to change the label by the grade displayed on the view page from
* 'your current grade is' to 'your final grade is'.
*
* @param integer $numattempts the number of previous attempts this user has made.
* @param object $lastattempt information about the user's last completed attempt.
@ -484,7 +484,7 @@ abstract class quiz_access_rule_base {
/**
* If this rule can determine that this user will never be allowed another attempt at
* this quiz, then return true. This is used so we can know whether to display a
* final score on the view page. This will only be called if there is not a currently
* final grade on the view page. This will only be called if there is not a currently
* active attempt for this user.
* @param integer $numattempts the number of previous attempts this user has made.
* @param object $lastattempt information about the user's last completed attempt.

View File

@ -33,9 +33,6 @@ if (!defined('MOODLE_INTERNAL')) {
}
// TODO get_question_score -> mark
/**
* Class for quiz exceptions. Just saves a couple of arguments on the
* constructor for a moodle_exception.
@ -557,6 +554,15 @@ class quiz_attempt {
(!$this->is_preview_user() || $this->attempt->preview);
}
/**
* Get the overall feedback corresponding to a particular mark.
* @param $grade a particular grade.
*/
public function get_overall_feedback($grade) {
return quiz_feedback_for_grade($grade, $this->get_quiz(),
$this->quizobj->get_context(), $this->get_cm());
}
/**
* Wrapper round the has_capability funciton that automatically passes in the quiz context.
*/
@ -713,7 +719,7 @@ class quiz_attempt {
* @param integer $slot the number used to identify this question within this attempt.
* @return string the formatted grade, to the number of decimal places specified by the quiz.
*/
public function get_question_score($slot) {
public function get_question_mark($slot) {
return quiz_format_question_grade($this->get_quiz(), $this->quba->get_question_mark($slot));
}

View File

@ -17,7 +17,7 @@
<FIELD NAME="attemptonlast" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="preferredbehaviour" NEXT="attempts"/>
<FIELD NAME="attempts" TYPE="int" LENGTH="6" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="The maximum number of attempts that a student is allowed at this quiz. 0 mean no limit." PREVIOUS="attemptonlast" NEXT="grademethod"/>
<FIELD NAME="grademethod" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="1" SEQUENCE="false" COMMENT="How individual attempt grades are combined to get the overall grade. From the top of mod/quiz/lib.php: 1 = QUIZ_GRADEHIGHEST, 2 = QUIZ_GRADEAVERAGE, 3 = QUIZ_ATTEMPTFIRST, 4 = QUIZ_ATTEMPTLAST." PREVIOUS="attempts" NEXT="decimalpoints"/>
<FIELD NAME="decimalpoints" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="2" SEQUENCE="false" COMMENT="Number of decimal points to display when printing scores belonging to this quiz." PREVIOUS="grademethod" NEXT="questiondecimalpoints"/>
<FIELD NAME="decimalpoints" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="2" SEQUENCE="false" COMMENT="Number of decimal points to display when printing grades belonging to this quiz." PREVIOUS="grademethod" NEXT="questiondecimalpoints"/>
<FIELD NAME="questiondecimalpoints" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="-2" SEQUENCE="false" COMMENT="The number of decimal digits to use when displaying question grades. -1 = use decimalpoints, otherwise a separate setting." PREVIOUS="decimalpoints" NEXT="reviewattempt"/>
<FIELD NAME="reviewattempt" TYPE="int" LENGTH="6" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether users are allowed to review their quiz attempts at various times. This is a bit field, decoded by the mod_quiz_display_options class. It is formed by ORing together the constants defined there." PREVIOUS="questiondecimalpoints" NEXT="reviewcorrectness"/>
<FIELD NAME="reviewcorrectness" TYPE="int" LENGTH="6" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether users are allowed to review their quiz attempts at various times. A bit field, like reviewattempt." PREVIOUS="reviewattempt" NEXT="reviewmarks"/>
@ -102,7 +102,7 @@
<KEY NAME="question" TYPE="foreign" FIELDS="question" REFTABLE="question" REFFIELDS="id" PREVIOUS="quiz"/>
</KEYS>
</TABLE>
<TABLE NAME="quiz_feedback" COMMENT="Feedback given to students based on which grade band their overall score lies." PREVIOUS="quiz_question_instances" NEXT="quiz_reports">
<TABLE NAME="quiz_feedback" COMMENT="Feedback given to students based on which grade band their overall grade lies." PREVIOUS="quiz_question_instances" NEXT="quiz_reports">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="quizid"/>
<FIELD NAME="quizid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references quiz.id." PREVIOUS="id" NEXT="feedbacktext"/>

View File

@ -86,9 +86,9 @@
array_push($headings, get_string('feedback', 'quiz'));
array_push($align, 'left');
}
$showing = 'scores'; // default
$showing = 'grades'; // default
$scores = $DB->get_records_sql_menu('
$grades = $DB->get_records_sql_menu('
SELECT qg.quiz, qg.grade
FROM {quiz_grades} qg
JOIN {quiz} q ON q.id = qg.quiz
@ -140,22 +140,22 @@
// fields set to make the following call work.
$data[] = quiz_attempt_summary_link_to_reports($quiz, $cm, $context);
} else if ($showing == 'scores') {
} else if ($showing == 'grades') {
// Grade and feedback.
$attempts = quiz_get_user_attempts($quiz->id, $USER->id, 'all');
list($someoptions, $alloptions) = quiz_get_combined_reviewoptions($quiz, $attempts, $context);
$grade = '';
$feedback = '';
if ($quiz->grade && array_key_exists($quiz->id, $scores)) {
if ($alloptions->scores) {
if ($quiz->grade && array_key_exists($quiz->id, grades)) {
if ($alloptions->marks) {
$a = new stdClass;
$a->grade = quiz_format_grade($quiz, $scores[$quiz->id]);
$a->grade = quiz_format_grade($quiz, grades[$quiz->id]);
$a->maxgrade = quiz_format_grade($quiz, $quiz->grade);
$grade = get_string('outofshort', 'quiz', $a);
}
if ($alloptions->overallfeedback) {
$feedback = quiz_feedback_for_grade($scores[$quiz->id], $quiz, $context, $cm);
$feedback = quiz_feedback_for_grade(grades[$quiz->id], $quiz, $context, $cm);
}
}
$data[] = $grade;

View File

@ -218,7 +218,7 @@ $string['daysavailable'] = 'Days available';
$string['decimaldigits'] = 'Decimal digits in grades';
$string['decimalformat'] = 'decimals';
$string['decimalplaces'] = 'Decimal places in grades';
$string['decimalplaces_help'] = 'This setting specifies the number of digits shown after the decimal point when displaying scores or grades. It only effects the display of grades, not the grades stored in the database, nor the internal calculations, which are carried out to full accuracy.';
$string['decimalplaces_help'] = 'This setting specifies the number of digits shown after the decimal point when displaying grades or grades. It only effects the display of grades, not the grades stored in the database, nor the internal calculations, which are carried out to full accuracy.';
$string['decimalplacesquestion'] = 'Decimal places in question grades';
$string['decimalplacesquestion_help'] = 'This setting specifies the number of digits shown after the decimal point when displaying the grades for individual questions.';
$string['decimalpoints'] = 'Decimal points';
@ -314,8 +314,8 @@ $string['feedbackerrororder'] = 'Feedback grade boundaries must be in order, hig
$string['file'] = 'File';
$string['fileformat'] = 'File format';
$string['fillcorrect'] = 'Fill with correct';
$string['filloutnumericalanswer'] = 'You provide at least one possible answer and tolerance. The first matching answer will be used to determine the score and feedback. If you supply some feedback with no answer at the end, that will be shown to students whose response is not matched by any of the other answers.';
$string['filloutoneanswer'] = 'You must provide at least one possible answer. Answers left blank will not be used. \'*\' can be used as a wildcard to match any characters. The first matching answer will be used to determine the score and feedback.';
$string['filloutnumericalanswer'] = 'You provide at least one possible answer and tolerance. The first matching answer will be used to determine the grade and feedback. If you supply some feedback with no answer at the end, that will be shown to students whose response is not matched by any of the other answers.';
$string['filloutoneanswer'] = 'You must provide at least one possible answer. Answers left blank will not be used. \'*\' can be used as a wildcard to match any characters. The first matching answer will be used to determine the grade and feedback.';
$string['filloutthreequestions'] = 'You must provide at least three questions with matching answers. You can provide extra wrong answers by giving an answer with a blank question. Entries where both the question and the answer are blank will be ignored.';
$string['fillouttwochoices'] = 'You must fill out at least two choices. Choices left blank will not be used.';
$string['finishattemptdots'] = 'Finish attempt...';

View File

@ -326,7 +326,7 @@ function quiz_get_best_grade($quiz, $userid) {
global $DB;
$grade = $DB->get_field('quiz_grades', 'grade', array('quiz' => $quiz->id, 'userid' => $userid));
// Need to detect errors/no result, without catching 0 scores.
// Need to detect errors/no result, without catching 0 grades.
if ($grade === false) {
return null;
}
@ -599,10 +599,10 @@ function quiz_grade_item_update($quiz, $grades = NULL) {
}
/* description by TJ:
1/ If the quiz is set to not show scores while the quiz is still open, and is set to show scores after
1/ If the quiz is set to not show grades while the quiz is still open, and is set to show grades after
the quiz is closed, then create the grade_item with a show-after date that is the quiz close date.
2/ If the quiz is set to not show scores at either of those times, create the grade_item as hidden.
3/ If the quiz is set to show scores, create the grade_item visible.
2/ If the quiz is set to not show grades at either of those times, create the grade_item as hidden.
3/ If the quiz is set to show grades, create the grade_item visible.
*/
$openreviewoptions = mod_quiz_display_options::make_from_quiz($quiz,
mod_quiz_display_options::LATER_WHILE_OPEN);
@ -812,7 +812,7 @@ function quiz_get_recent_mod_activity(&$activities, &$index, $timestart,
$tmpactivity->content->attemptid = $attempt->id;
$tmpactivity->content->attempt = $attempt->attempt;
if (quiz_has_grades($quiz) && $options->scores) {
if (quiz_has_grades($quiz) && $options->marks) {
$tmpactivity->content->sumgrades = quiz_format_grade($quiz, $attempt->sumgrades);
$tmpactivity->content->maxgrade = quiz_format_grade($quiz, $quiz->sumgrades);
} else {

View File

@ -508,7 +508,7 @@ function quiz_update_all_attempt_sumgrades($quiz) {
}
/**
* The quiz grade is the score that student's results are marked out of. When it
* The quiz grade is the maximum that student's results are marked out of. When it
* changes, the corresponding data in quiz_grades and quiz_feedback needs to be
* rescaled. After calling this function, you probably need to call
* quiz_update_all_attempt_sumgrades, quiz_update_all_final_grades and

View File

@ -21,7 +21,7 @@ class quiz_overview_report extends quiz_default_report {
$this->context = get_context_instance(CONTEXT_MODULE, $cm->id);
// Work out some display options - whether there is feedback, and whether scores should be shown.
// Work out some display options - whether there is feedback, and whether grades should be shown.
$hasfeedback = quiz_has_feedback($quiz);
$fakeattempt = new stdClass();
$fakeattempt->preview = false;

View File

@ -1,257 +1,275 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This page prints a review of a particular quiz attempt
*
* @author Martin Dougiamas and many others.
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package quiz
* It is used either by the student whose attempts this is, after the attempt,
* or by a teacher reviewing another's attempt during or afterwards.
*
* @package mod
* @subpackage quiz
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(dirname(__FILE__) . '/../../config.php');
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
require_once(dirname(__FILE__) . '/../../config.php');
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
$attemptid = required_param('attempt', PARAM_INT);
$page = optional_param('page', 0, PARAM_INT);
$showall = optional_param('showall', 0, PARAM_BOOL);
$attemptid = required_param('attempt', PARAM_INT);
$page = optional_param('page', 0, PARAM_INT);
$showall = optional_param('showall', 0, PARAM_BOOL);
$url = new moodle_url('/mod/quiz/review.php', array('attempt'=>$attemptid));
if ($page !== 0) {
$url->param('page', $page);
$url = new moodle_url('/mod/quiz/review.php', array('attempt'=>$attemptid));
if ($page !== 0) {
$url->param('page', $page);
}
if ($showall !== 0) {
$url->param('showall', $showall);
}
$PAGE->set_url($url);
$attemptobj = quiz_attempt::create($attemptid);
// Check login.
require_login($attemptobj->get_course(), false, $attemptobj->get_cm());
$attemptobj->check_review_capability();
// Create an object to manage all the other (non-roles) access rules.
$accessmanager = $attemptobj->get_access_manager(time());
$options = $attemptobj->get_display_options(true);
// Permissions checks for normal users who do not have quiz:viewreports capability.
if (!$attemptobj->has_capability('mod/quiz:viewreports')) {
// Can't review other users' attempts.
if (!$attemptobj->is_own_attempt()) {
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'notyourattempt');
}
if ($showall !== 0) {
$url->param('showall', $showall);
// Can't review during the attempt - send them back to the attempt page.
if (!$attemptobj->is_finished()) {
redirect($attemptobj->attempt_url(0, $page));
}
$PAGE->set_url($url);
$attemptobj = quiz_attempt::create($attemptid);
/// Check login.
require_login($attemptobj->get_course(), false, $attemptobj->get_cm());
$attemptobj->check_review_capability();
/// Create an object to manage all the other (non-roles) access rules.
$accessmanager = $attemptobj->get_access_manager(time());
$options = $attemptobj->get_review_options();
/// Permissions checks for normal users who do not have quiz:viewreports capability.
if (!$attemptobj->has_capability('mod/quiz:viewreports')) {
/// Can't review other users' attempts.
if (!$attemptobj->is_own_attempt()) {
quiz_error($attemptobj->get_quiz(), 'notyourattempt');
}
/// Can't review during the attempt - send them back to the attempt page.
if (!$attemptobj->is_finished()) {
redirect($attemptobj->attempt_url(0, $page));
}
/// Can't review unless Students may review -> Responses option is turned on.
if (!$options->responses) {
$accessmanager->back_to_view_page($attemptobj->is_preview_user(),
$accessmanager->cannot_review_message($options));
}
// Can't review unless Students may review -> Responses option is turned on.
if (!$options->attempt) {
$accessmanager->back_to_view_page($attemptobj->is_preview_user(),
$accessmanager->cannot_review_message($attemptobj->get_attempt_state()));
}
}
/// Load the questions and states needed by this page.
if ($showall) {
$questionids = $attemptobj->get_question_ids();
// Load the questions and states needed by this page.
if ($showall) {
$questionids = $attemptobj->get_slots();
} else {
$questionids = $attemptobj->get_slots($page);
}
// Save the flag states, if they are being changed.
if ($options->flags == question_display_options::EDITABLE && optional_param('savingflags', false, PARAM_BOOL)) {
require_sesskey();
$attemptobj->save_question_flags();
redirect($attemptobj->review_url(0, $page, $showall));
}
// Log this review.
add_to_log($attemptobj->get_courseid(), 'quiz', 'review', 'review.php?attempt=' .
$attemptobj->get_attemptid(), $attemptobj->get_quizid(), $attemptobj->get_cmid());
// Work out appropriate title and whether blocks should be shown
if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
$strreviewtitle = get_string('reviewofpreview', 'quiz');
navigation_node::override_active_url($attemptobj->start_attempt_url());
} else {
$strreviewtitle = get_string('reviewofattempt', 'quiz', $attemptobj->get_attempt_number());
if (empty($attemptobj->get_quiz()->showblocks) && !$attemptobj->is_preview_user()) {
$PAGE->blocks->show_only_fake_blocks();
}
}
// Arrange for the navigation to be displayed.
$navbc = $attemptobj->get_navigation_panel('quiz_review_nav_panel', $page, $showall);
$firstregion = reset($PAGE->blocks->get_regions());
$PAGE->blocks->add_fake_block($navbc, $firstregion);
// Print the page header
$headtags = $attemptobj->get_html_head_contributions($page, $showall);
if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
$accessmanager->setup_secure_page($attemptobj->get_course()->shortname.': '.format_string($attemptobj->get_quiz_name()), $headtags);
} elseif ($accessmanager->safebrowser_required($attemptobj->is_preview_user())) {
$PAGE->set_title($attemptobj->get_course()->shortname . ': '.format_string($attemptobj->get_quiz_name()));
$PAGE->set_heading($attemptobj->get_course()->fullname);
$PAGE->set_cacheable(false);
echo $OUTPUT->header();
} else {
$PAGE->navbar->add($strreviewtitle);
$PAGE->set_title(format_string($attemptobj->get_quiz_name()));
$PAGE->set_heading($attemptobj->get_course()->fullname);
echo $OUTPUT->header();
}
// Print heading.
if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
$attemptobj->print_restart_preview_button();
}
echo $OUTPUT->heading($strreviewtitle);
// Summary table start ============================================================================
// Work out some time-related things.
$attempt = $attemptobj->get_attempt();
$quiz = $attemptobj->get_quiz();
$overtime = 0;
if ($attempt->timefinish) {
if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
if($quiz->timelimit && $timetaken > ($quiz->timelimit + 60)) {
$overtime = $timetaken - $quiz->timelimit;
$overtime = format_time($overtime);
}
$timetaken = format_time($timetaken);
} else {
$questionids = $attemptobj->get_question_ids($page);
$timetaken = "-";
}
$attemptobj->load_questions($questionids);
$attemptobj->load_question_states($questionids);
} else {
$timetaken = get_string('unfinished', 'quiz');
}
/// Save the flag states, if they are being changed.
if ($options->flags == QUESTION_FLAGSEDITABLE && optional_param('savingflags', false, PARAM_BOOL)) {
require_sesskey();
$formdata = data_submitted();
question_save_flags($formdata, $attemptid, $questionids);
redirect($attemptobj->review_url(0, $page, $showall));
// Print summary table about the whole attempt.
// First we assemble all the rows that are appopriate to the current situation in
// an array, then later we only output the table if there are any rows to show.
$rows = array();
if (!$attemptobj->get_quiz()->showuserpicture && $attemptobj->get_userid() != $USER->id) {
// If showuserpicture is true, the picture is shown elsewhere, so don't repeat it.
$student = $DB->get_record('user', array('id' => $attemptobj->get_userid()));
$picture = $OUTPUT->user_picture($student, array('courseid'=>$attemptobj->get_courseid()));
$rows[] = '<tr><th scope="row" class="cell">' . $picture . '</th><td class="cell"><a href="' .
$CFG->wwwroot . '/user/view.php?id=' . $student->id . '&amp;course=' . $attemptobj->get_courseid() . '">' .
fullname($student, true) . '</a></td></tr>';
}
if ($attemptobj->has_capability('mod/quiz:viewreports')) {
$attemptlist = $attemptobj->links_to_other_attempts($attemptobj->review_url(0, $page, $showall));
if ($attemptlist) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('attempts', 'quiz') .
'</th><td class="cell">' . $attemptlist . '</td></tr>';
}
}
/// Log this review.
add_to_log($attemptobj->get_courseid(), 'quiz', 'review', 'review.php?attempt=' .
$attemptobj->get_attemptid(), $attemptobj->get_quizid(), $attemptobj->get_cmid());
// Timing information.
$rows[] = '<tr><th scope="row" class="cell">' . get_string('startedon', 'quiz') .
'</th><td class="cell">' . userdate($attempt->timestart) . '</td></tr>';
if ($attempt->timefinish) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('completedon', 'quiz') . '</th><td class="cell">' .
userdate($attempt->timefinish) . '</td></tr>';
$rows[] = '<tr><th scope="row" class="cell">' . get_string('timetaken', 'quiz') . '</th><td class="cell">' .
$timetaken . '</td></tr>';
}
if (!empty($overtime)) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('overdue', 'quiz') . '</th><td class="cell">' . $overtime . '</td></tr>';
}
/// Work out appropriate title and whether blocks should be shown
if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
// Normal blocks
$strreviewtitle = get_string('reviewofpreview', 'quiz');
navigation_node::override_active_url($attemptobj->start_attempt_url());
// Show marks (if the user is allowed to see marks at the moment).
$grade = quiz_rescale_grade($attempt->sumgrades, $quiz, false);
if ($options->marks && quiz_has_grades($quiz)) {
if (!$attempt->timefinish) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('grade') . '</th><td class="cell">' .
get_string('attemptstillinprogress', 'quiz') . '</td></tr>';
} else if (is_null($grade)) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('grade') . '</th><td class="cell">' .
quiz_format_grade($quiz, $grade) . '</td></tr>';
} else {
$strreviewtitle = get_string('reviewofattempt', 'quiz', $attemptobj->get_attempt_number());
if (empty($attemptobj->get_quiz()->showblocks) && !$attemptobj->is_preview_user()) {
// Only show pretend blocks
$PAGE->blocks->show_only_fake_blocks();
}
}
// Initialise the JavaScript.
$headtags = $attemptobj->get_html_head_contributions($page);
// Arrange for the navigation to be displayed.
$navbc = $attemptobj->get_navigation_panel('quiz_review_nav_panel', $page, $showall);
$firstregion = reset($PAGE->blocks->get_regions());
$PAGE->blocks->add_fake_block($navbc, $firstregion);
/// Print the page header
$headtags = $attemptobj->get_html_head_contributions($page);
if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
$accessmanager->setup_secure_page($attemptobj->get_course()->shortname.': '.format_string($attemptobj->get_quiz_name()), $headtags);
} elseif ($accessmanager->safebrowser_required($attemptobj->is_preview_user())) {
$PAGE->set_title($attemptobj->get_course()->shortname . ': '.format_string($attemptobj->get_quiz_name()));
$PAGE->set_heading($attemptobj->get_course()->fullname);
$PAGE->set_cacheable(false);
echo $OUTPUT->header();
} else {
$PAGE->navbar->add($strreviewtitle);
$PAGE->set_title(format_string($attemptobj->get_quiz_name()));
$PAGE->set_heading($attemptobj->get_course()->fullname);
echo $OUTPUT->header();
}
/// Print heading.
if ($attemptobj->is_preview_user() && $attemptobj->is_own_attempt()) {
$attemptobj->print_restart_preview_button();
}
echo $OUTPUT->heading($strreviewtitle);
/// Summary table start ============================================================================
/// Work out some time-related things.
$attempt = $attemptobj->get_attempt();
$quiz = $attemptobj->get_quiz();
$overtime = 0;
if ($attempt->timefinish) {
if ($timetaken = ($attempt->timefinish - $attempt->timestart)) {
if($quiz->timelimit && $timetaken > ($quiz->timelimit + 60)) {
$overtime = $timetaken - $quiz->timelimit;
$overtime = format_time($overtime);
}
$timetaken = format_time($timetaken);
} else {
$timetaken = "-";
}
} else {
$timetaken = get_string('unfinished', 'quiz');
}
/// Print summary table about the whole attempt.
/// First we assemble all the rows that are appopriate to the current situation in
/// an array, then later we only output the table if there are any rows to show.
$rows = array();
if (!$attemptobj->get_quiz()->showuserpicture && $attemptobj->get_userid() <> $USER->id) {
/// If showuserpicture is true, the picture is shown elsewhere, so don't repeat it.
$student = $DB->get_record('user', array('id' => $attemptobj->get_userid()));
$picture = $OUTPUT->user_picture($student, array('courseid'=>$attemptobj->get_courseid()));
$rows[] = '<tr><th scope="row" class="cell">' . $picture . '</th><td class="cell"><a href="' .
$CFG->wwwroot . '/user/view.php?id=' . $student->id . '&amp;course=' . $attemptobj->get_courseid() . '">' .
fullname($student, true) . '</a></td></tr>';
}
if ($attemptobj->has_capability('mod/quiz:viewreports')) {
$attemptlist = $attemptobj->links_to_other_attempts($attemptobj->review_url(0, $page, $showall));
if ($attemptlist) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('attempts', 'quiz') .
'</th><td class="cell">' . $attemptlist . '</td></tr>';
}
}
/// Timing information.
$rows[] = '<tr><th scope="row" class="cell">' . get_string('startedon', 'quiz') .
'</th><td class="cell">' . userdate($attempt->timestart) . '</td></tr>';
if ($attempt->timefinish) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('completedon', 'quiz') . '</th><td class="cell">' .
userdate($attempt->timefinish) . '</td></tr>';
$rows[] = '<tr><th scope="row" class="cell">' . get_string('timetaken', 'quiz') . '</th><td class="cell">' .
$timetaken . '</td></tr>';
}
if (!empty($overtime)) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('overdue', 'quiz') . '</th><td class="cell">' . $overtime . '</td></tr>';
}
/// Show scores (if the user is allowed to see scores at the moment).
$grade = quiz_rescale_grade($attempt->sumgrades, $quiz, false);
if ($options->scores) {
if (quiz_has_grades($quiz)) {
if($overtime) {
$result->sumgrades = "0";
$result->grade = "0.0";
}
/// Show raw marks only if they are different from the grade (like on the view page.
if ($quiz->grade != $quiz->sumgrades) {
$a = new stdClass;
$a->grade = quiz_format_grade($quiz, $attempt->sumgrades);
$a->maxgrade = quiz_format_grade($quiz, $quiz->sumgrades);
$rows[] = '<tr><th scope="row" class="cell">' . get_string('marks', 'quiz') . '</th><td class="cell">' .
get_string('outofshort', 'quiz', $a) . '</td></tr>';
}
/// Now the scaled grade.
// Show raw marks only if they are different from the grade (like on the view page).
if ($quiz->grade != $quiz->sumgrades) {
$a = new stdClass;
$a->grade = '<b>' . quiz_format_grade($quiz, $grade) . '</b>';
$a->maxgrade = quiz_format_grade($quiz, $quiz->grade);
$a->percent = '<b>' . round(($attempt->sumgrades/$quiz->sumgrades)*100, 0) . '</b>';
$rows[] = '<tr><th scope="row" class="cell">' . get_string('grade') . '</th><td class="cell">' .
get_string('outofpercent', 'quiz', $a) . '</td></tr>';
$a->grade = quiz_format_grade($quiz, $attempt->sumgrades);
$a->maxgrade = quiz_format_grade($quiz, $quiz->sumgrades);
$rows[] = '<tr><th scope="row" class="cell">' . get_string('marks', 'quiz') . '</th><td class="cell">' .
get_string('outofshort', 'quiz', $a) . '</td></tr>';
}
// Now the scaled grade.
$a = new stdClass;
$a->grade = '<b>' . quiz_format_grade($quiz, $grade) . '</b>';
$a->maxgrade = quiz_format_grade($quiz, $quiz->grade);
if ($quiz->grade != 100) {
$a->percent = '<b>' . round($attempt->sumgrades * 100 / $quiz->sumgrades, 0) . '</b>';
$formattedgrade = get_string('outofpercent', 'quiz', $a);
} else {
$formattedgrade = get_string('outof', 'quiz', $a);
}
$rows[] = '<tr><th scope="row" class="cell">' . get_string('grade') . '</th><td class="cell">' .
$formattedgrade . '</td></tr>';
}
}
/// Feedback if there is any, and the user is allowed to see it now.
$feedback = $attemptobj->get_overall_feedback($grade);
if ($options->overallfeedback && $feedback) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('feedback', 'quiz') .
'</th><td class="cell">' . $feedback . '</td></tr>';
}
// Feedback if there is any, and the user is allowed to see it now.
$feedback = $attemptobj->get_overall_feedback($grade);
if ($options->overallfeedback && $feedback) {
$rows[] = '<tr><th scope="row" class="cell">' . get_string('feedback', 'quiz') .
'</th><td class="cell">' . $feedback . '</td></tr>';
}
/// Now output the summary table, if there are any rows to be shown.
if (!empty($rows)) {
echo '<table class="generaltable generalbox quizreviewsummary"><tbody>', "\n";
echo implode("\n", $rows);
echo "\n</tbody></table>\n";
}
// Now output the summary table, if there are any rows to be shown.
if (!empty($rows)) {
echo '<table class="generaltable generalbox quizreviewsummary"><tbody>', "\n";
echo implode("\n", $rows);
echo "\n</tbody></table>\n";
}
/// Summary table end ==============================================================================
// Summary table end ==============================================================================
/// Form for saving flags if necessary.
if ($options->flags == QUESTION_FLAGSEDITABLE) {
echo '<form action="' . s($attemptobj->review_url(0, $page, $showall)) .
'" method="post" class="questionflagsaveform"><div>';
echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
}
// Form for saving flags if necessary.
if ($options->flags == question_display_options::EDITABLE) {
echo '<form action="' . $attemptobj->review_url(0, $page, $showall) .
'" method="post"><div>';
echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
}
/// Print all the questions.
if ($showall) {
$thispage = 'all';
$lastpage = true;
} else {
$thispage = $page;
$lastpage = $attemptobj->is_last_page($page);
}
foreach ($attemptobj->get_question_ids($thispage) as $id) {
$attemptobj->print_question($id, true, $attemptobj->review_url($id, $page, $showall));
}
// Print all the questions.
if ($showall) {
$thispage = 'all';
$lastpage = true;
} else {
$thispage = $page;
$lastpage = $attemptobj->is_last_page($page);
}
foreach ($attemptobj->get_slots($thispage) as $slot) {
echo $attemptobj->render_question($slot, true, $attemptobj->review_url($slot, $page, $showall));
}
/// Close form if we opened it.
if ($options->flags == QUESTION_FLAGSEDITABLE) {
echo '<div class="submitbtns">' . "\n" .
'<input type="submit" class="questionflagsavebutton" name="savingflags" value="' .
get_string('saveflags', 'question') . '" />' .
"</div>\n" .
"\n</div></form>\n";
$PAGE->requires->js_init_call('M.mod_quiz.init_review_form', null, false, quiz_get_js_module());
}
/// Print a link to the next page.
echo '<div class="submitbtns">';
if ($lastpage) {
$accessmanager->print_finish_review_link($attemptobj->is_preview_user());
} else {
echo link_arrow_right(get_string('next'), s($attemptobj->review_url(0, $page + 1)));
}
echo "</div>";
echo $OUTPUT->footer();
// Close form if we opened it.
if ($options->flags == question_display_options::EDITABLE) {
echo '<div class="submitbtns">' . "\n" .
'<input type="submit" class="questionflagsavebutton" name="savingflags" value="' .
get_string('saveflags', 'question') . '" />' .
"</div>\n" .
"\n</div></form>\n";
$PAGE->requires->js_init_call('M.mod_quiz.init_review_form', null, false, quiz_get_js_module());
}
// Print a link to the next page.
echo '<div class="submitbtns">';
if ($lastpage) {
$accessmanager->print_finish_review_link($attemptobj->is_preview_user());
} else {
echo link_arrow_right(get_string('next'), $attemptobj->review_url(0, $page + 1));
}
echo '</div>';
echo $OUTPUT->footer();

View File

@ -127,7 +127,7 @@ foreach ($slots as $slot) {
$attemptobj->get_question_number($slot) . $flag . '</a>',
$attemptobj->get_question_status($slot, $displayoptions->correctness));
if ($markscolumn) {
$row[] = $attemptobj->get_question_score($slot);
$row[] = $attemptobj->get_question_mark($slot);
}
$table->data[] = $row;
}

View File

@ -100,9 +100,10 @@ DONE mod/quiz/protect_js.php | 56 -
mod/quiz/quiz.js | 355 ++-
mod/quiz/restorelib.php | 113 +-
mod/quiz/restorelibpre15.php | 41 +-
mod/quiz/review.php | 520 ++--
DONE mod/quiz/review.php | 520 ++--
DONE mod/quiz/reviewoptions.html | 124 +-
mod/quiz/reviewquestion.php | 267 +-
mod/quiz/settingslib.php
DONE mod/quiz/simpletest/testaccessrules.php | 518 ++++
DONE mod/quiz/simpletest/testlib.php | 58 +
DONE mod/quiz/simpletest/testlocallib.php | 76 +