}
/**
- * Do the printheader call, etc. required for a secure page, including the necessary JS.
+ * Sets up the attempt (review or summary) page with any properties required
+ * by the access rules.
*
- * @param string $title HTML title tag content, passed to printheader.
- * @param string $headtags extra stuff to go in the HTML head tag, passed to printheader.
+ * @param moodle_page $page the page object to initialise.
*/
- public function setup_secure_page($title, $headtags = null) {
- $this->securewindowrule->setup_secure_page($title, $headtags);
+ public function setup_attempt_page($page) {
+ foreach ($this->rules as $rule) {
+ $rule->setup_attempt_page($page);
+ }
}
- public function show_attempt_timer_if_needed($attempt, $timenow) {
- global $PAGE;
+ /**
+ * Will cause the attempt time to start counting down after the page has loaded,
+ * if that is necessary.
+ *
+ * @param object $attempt the data from the relevant quiz_attempts row.
+ * @param int $timenow the time to consider as 'now'.
+ * @param mod_quiz_renderer $output the quiz renderer.
+ */
+ public function show_attempt_timer_if_needed($attempt, $timenow, $output) {
+
$timeleft = false;
foreach ($this->rules as $rule) {
$ruletimeleft = $rule->time_left($attempt, $timenow);
$timeleft = $ruletimeleft;
}
}
+
if ($timeleft !== false) {
// Make sure the timer starts just above zero. If $timeleft was <= 0, then
// this will just have the effect of causing the quiz to be submitted immediately.
$timerstartvalue = max($timeleft, 1);
- $PAGE->requires->js_init_call('M.mod_quiz.timer.init',
- array($timerstartvalue), false, quiz_get_js_module());
+ $output->initialise_timer($timerstartvalue);
}
}
/**
- * @return bolean if this quiz should only be shown to students in a secure window.
+ * @return bolean if this quiz should only be shown to students in a popup window.
*/
- public function securewindow_required($canpreview) {
- return !$canpreview && !is_null($this->securewindowrule);
- }
-
- /**
- * @return bolean if this quiz should only be shown to students with safe browser.
- */
- public function safebrowser_required($canpreview) {
- return !$canpreview && !is_null($this->safebrowserrule);
+ public function attempt_must_be_in_popup() {
+ foreach ($this->rules as $rule) {
+ if ($rule->attempt_must_be_in_popup()) {
+ return true;
+ }
+ }
+ return false;
}
/**
- * Print a button to start a quiz attempt, with an appropriate javascript warning,
- * depending on the access restrictions. The link will pop up a 'secure' window, if
- * necessary.
- *
- * @param bool $canpreview whether this user can preview. This affects whether they must
- * use a secure window.
- * @param string $buttontext the label to put on the button.
- * @param bool $unfinished whether the button is to continue an existing attempt,
- * or start a new one. This affects whether a javascript alert is shown.
+ * @return array any options that are required for showing the attempt page
+ * in a popup window.
*/
- //TODO: Add this function to renderer
- public function print_start_attempt_button($canpreview, $buttontext, $unfinished) {
- global $OUTPUT;
-
- $url = $this->quizobj->start_attempt_url();
- $button = new single_button($url, $buttontext);
- $button->class .= ' quizstartbuttondiv';
-
- if (!$unfinished) {
- $strconfirmstartattempt = $this->confirm_start_attempt_message();
- if ($strconfirmstartattempt) {
- $button->add_action(new confirm_action($strconfirmstartattempt, null,
- get_string('startattempt', 'quiz')));
- }
- }
-
- $warning = '';
-
- if ($this->securewindow_required($canpreview)) {
- $button->class .= ' quizsecuremoderequired';
-
- $button->add_action(new popup_action('click', $url, 'quizpopup',
- securewindow_access_rule::$popupoptions));
-
- $warning = html_writer::tag('noscript',
- $OUTPUT->heading(get_string('noscript', 'quiz')));
+ public function get_popup_options() {
+ $options = array();
+ foreach ($this->rules as $rule) {
+ $options += $rule->get_popup_options();
}
-
- return $OUTPUT->render($button) . $warning;
+ return $options;
}
/**
* Send the user back to the quiz view page. Normally this is just a redirect, but
* If we were in a secure window, we close this window, and reload the view window we came from.
*
- * @param bool $canpreview This affects whether we have to worry about secure window stuff.
- */
- public function back_to_view_page($canpreview, $message = '') {
- global $CFG, $OUTPUT, $PAGE;
- $url = $this->quizobj->view_url();
- if ($this->securewindow_required($canpreview)) {
- $PAGE->set_pagelayout('popup');
- echo $OUTPUT->header();
- echo $OUTPUT->box_start();
- if ($message) {
- echo '<p>' . $message . '</p><p>' . get_string('windowclosing', 'quiz') . '</p>';
- $delay = 5;
- } else {
- echo '<p>' . get_string('pleaseclose', 'quiz') . '</p>';
- $delay = 0;
- }
- $PAGE->requires->js_function_call('M.mod_quiz.secure_window.close',
- array($url, $delay));
- echo $OUTPUT->box_end();
- echo $OUTPUT->footer();
- die();
- } else {
- redirect($url, $message);
- }
- }
-
- /**
- * Print a control to finish the review. Normally this is just a link, but if we are
- * in a secure window, it needs to be a button that does M.mod_quiz.secure_window.close.
+ * This method does not return;
*
- * @param bool $canpreview This affects whether we have to worry about secure window stuff.
+ * @param mod_quiz_renderer $output the quiz renderer.
+ * @param string $message optional message to output while redirecting.
*/
- public function print_finish_review_link($canpreview, $return = false) {
- global $CFG;
- $output = '';
- $url = $this->quizobj->view_url();
- $output .= '<div class="finishreview">';
- if ($this->securewindow_required($canpreview)) {
- $url = addslashes_js(htmlspecialchars($url));
- $output .= '<input type="button" value="' . get_string('finishreview', 'quiz') . '" ' .
- "onclick=\"M.mod_quiz.secure_window.close('$url', 0)\" />\n";
- } else {
- $output .= '<a href="' . $url . '">' . get_string('finishreview', 'quiz') . "</a>\n";
- }
- $output .= "</div>\n";
- if ($return) {
- return $output;
+ public function back_to_view_page($output, $message = '') {
+ if ($this->attempt_must_be_in_popup()) {
+ echo $output->close_attempt_popup($message, $this->quizobj->view_url());
+ die();
} else {
- echo $output;
+ redirect($this->quizobj->view_url(), $message);
}
}
- /**
- * @return bolean if this quiz is password protected.
- */
- public function password_required() {
- return !is_null($this->passwordrule);
- }
-
/**
* Clear the flag in the session that says that the current user is allowed to do this quiz.
*/
}
}
- /**
- * @return string if the quiz policies merit it, return a warning string to be displayed
- * in a javascript alert on the start attempt button.
- */
- public function confirm_start_attempt_message() {
- $quiz = $this->quizobj->get_quiz();
- if ($quiz->timelimit && $quiz->attempts) {
- return get_string('confirmstartattempttimelimit', 'quiz', $quiz->attempts);
- } else if ($quiz->timelimit) {
- return get_string('confirmstarttimelimit', 'quiz');
- } else if ($quiz->attempts) {
- return get_string('confirmstartattemptlimit', 'quiz', $quiz->attempts);
- }
- return '';
- }
-
/**
* Make some text into a link to review the quiz, if that is appropriate.
*
* @return string some HTML, the $linktext either unmodified or wrapped in a
* link to the review page.
*/
- public function make_review_link($attempt, $canpreview, $reviewoptions) {
- global $CFG;
+ public function make_review_link($attempt, $reviewoptions, $output) {
// If review of responses is not allowed, or the attempt is still open, don't link.
if (!$attempt->timefinish) {
- return '';
+ return $output->no_review_message('');
}
$when = quiz_attempt_state($this->quizobj->get_quiz(), $attempt);
$this->quizobj->get_quiz(), $when);
if (!$reviewoptions->attempt) {
- $message = $this->cannot_review_message($when, true);
- if ($message) {
- return '<span class="noreviewmessage">' . $message . '</span>';
- } else {
- return '';
- }
- }
-
- $linktext = get_string('review', 'quiz');
-
- // It is OK to link, does it need to be in a secure window?
- if ($this->securewindow_required($canpreview)) {
- return $this->securewindowrule->make_review_link($linktext, $attempt->id);
- } else {
- return '<a href="' . $this->quizobj->review_url($attempt->id) . '" title="' .
- get_string('reviewthisattempt', 'quiz') . '">' . $linktext . '</a>';
- }
- }
+ return $output->no_review_message($this->quizobj->cannot_review_message($when, true));
- /**
- * If $reviewoptions->attempt is false, meaning that students can't review this
- * attempt at the moment, return an appropriate string explaining why.
- *
- * @param int $when One of the mod_quiz_display_options::DURING,
- * IMMEDIATELY_AFTER, LATER_WHILE_OPEN or AFTER_CLOSE constants.
- * @param bool $short if true, return a shorter string.
- * @return string an appropraite message.
- */
- public function cannot_review_message($when, $short = false) {
- $quiz = $this->quizobj->get_quiz();
- if ($short) {
- $langstrsuffix = 'short';
- $dateformat = get_string('strftimedatetimeshort', 'langconfig');
- } else {
- $langstrsuffix = '';
- $dateformat = '';
- }
- if ($when == mod_quiz_display_options::DURING ||
- $when == mod_quiz_display_options::IMMEDIATELY_AFTER) {
- return '';
- } else if ($when == mod_quiz_display_options::LATER_WHILE_OPEN && $quiz->timeclose &&
- $quiz->reviewattempt & mod_quiz_display_options::AFTER_CLOSE) {
- return get_string('noreviewuntil' . $langstrsuffix, 'quiz',
- userdate($quiz->timeclose, $dateformat));
} else {
- return get_string('noreview' . $langstrsuffix, 'quiz');
+ return $output->review_link($this->quizobj->review_url($attempt->id),
+ $this->attempt_must_be_in_popup(), $this->get_popup_options());
}
}
}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class quiz_access_rule_base {
+ /** @var stdClass the quiz settings. */
protected $quiz;
+ /** @var quiz the quiz object. */
protected $quizobj;
+ /** @var int the time to use as 'now'. */
protected $timenow;
/**
return false;
}
+ /**
+ * @return boolean whether this rule requires that the attemp (and review)
+ * pages must be displayed in a pop-up window.
+ */
+ public function attempt_must_be_in_popup() {
+ return false;
+ }
+
+ /**
+ * @return array any options that are required for showing the attempt page
+ * in a popup window.
+ */
+ public function get_popup_options() {
+ return array();
+ }
+
+ /**
+ * Sets up the attempt (review or summary) page with any special extra
+ * properties required by this rule. securewindow rule is an example of where
+ * this is used.
+ *
+ * @param moodle_page $page the page object to initialise.
+ */
+ public function setup_attempt_page($page) {
+ // Do nothing by default.
+ }
+
/**
* Add any fields that this rule requires to the quiz settings form. This
* method is called from {@link mod_quiz_mod_form::definition()}, while the
// If the user cancelled the password form, send them back to the view page.
if (optional_param('cancelpassword', false, PARAM_BOOL)) {
- $accessmanager->back_to_view_page($canpreview);
+ $accessmanager->back_to_view_page($output);
}
// If they entered the right password, let them in.
$output = '';
// Start the page and print the quiz intro, if any.
- if ($accessmanager->securewindow_required($canpreview)) {
- $accessmanager->setup_secure_page($this->quizobj->get_course()->shortname . ': ' .
- format_string($this->quizobj->get_quiz_name()));
- } else if ($accessmanager->safebrowser_required($canpreview)) {
- $PAGE->set_title($this->quizobj->get_course()->shortname . ': ' .
- format_string($this->quizobj->get_quiz_name()));
- $PAGE->set_cacheable(false);
- } else {
- $PAGE->set_title(format_string($this->quizobj->get_quiz_name()));
- }
+ $PAGE->set_title(format_string($this->quizobj->get_quiz_name()));
+ $accessmanager->setup_attempt_page($PAGE);
echo $OUTPUT->header();
if (trim(strip_tags($this->quiz->intro))) {
return get_string("safebrowsernotice", "quiz");
}
+ public function setup_attempt_page($page) {
+ $page->set_title($this->quizobj->get_course()->shortname . ': ' . $page->title);
+ $page->set_cacheable(false);
+ }
+
/**
* Checks if browser is safe browser
*
*/
class quizaccess_securewindow extends quiz_access_rule_base {
/** @var array options that should be used for opening the secure popup. */
- public static $popupoptions = array(
+ protected static $popupoptions = array(
'left' => 0,
'top' => 0,
'fullscreen' => true,
'menubar' => false,
);
- /**
- * Make a link to the review page for an attempt.
- *
- * @param string $linktext the desired link text.
- * @param int $attemptid the attempt id.
- * @return string HTML for the link.
- */
- public function make_review_link($linktext, $attemptid) {
- global $OUTPUT;
- $url = $this->quizobj->review_url($attemptid);
- $button = new single_button($url, $linktext);
- $button->add_action(new popup_action('click', $url, 'quizpopup', self::$popupoptions));
- return $OUTPUT->render($button);
+ public function attempt_must_be_in_popup() {
+ return true;
}
- /**
- * Do the printheader call, etc. required for a secure page, including the necessary JS.
- *
- * @param string $title HTML title tag content, passed to printheader.
- * @param string $headtags extra stuff to go in the HTML head tag, passed to printheader.
- * $headtags has been deprectaed since Moodle 2.0
- */
- public function setup_secure_page($title, $headtags=null) {
- global $PAGE;
- $PAGE->set_popup_notification_allowed(false);//prevent message notifications
- $PAGE->set_title($title);
- $PAGE->set_cacheable(false);
- $PAGE->set_pagelayout('popup');
- $PAGE->add_body_class('quiz-secure-window');
- $PAGE->requires->js_init_call('M.mod_quiz.secure_window.init',
+ public function get_popup_options() {
+ return self::$popupoptions;
+ }
+
+ public function setup_attempt_page($page) {
+ $page->set_popup_notification_allowed(false); // Prevent message notifications
+ $page->set_title($this->quizobj->get_course()->shortname . ': ' . $page->title);
+ $page->set_cacheable(false);
+ $page->set_pagelayout('popup');
+ $page->add_body_class('quiz-secure-window');
+ $page->requires->js_init_call('M.mod_quiz.secure_window.init',
null, false, quiz_get_js_module());
}
}
$title = get_string('attempt', 'quiz', $attemptobj->get_attempt_number());
$headtags = $attemptobj->get_html_head_contributions($page);
+$PAGE->set_title(format_string($attemptobj->get_quiz_name()));
$PAGE->set_heading($attemptobj->get_course()->fullname);
-if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
- $accessmanager->setup_secure_page($attemptobj->get_course()->shortname . ': ' .
- format_string($attemptobj->get_quiz_name()));
-
-} else if ($accessmanager->safebrowser_required($attemptobj->is_preview_user())) {
- $PAGE->set_title($attemptobj->get_course()->shortname . ': ' .
- format_string($attemptobj->get_quiz_name()));
- $PAGE->set_cacheable(false);
-
-} else {
- $PAGE->set_title(format_string($attemptobj->get_quiz_name()));
-}
+$accessmanager->setup_attempt_page($PAGE);
if ($attemptobj->is_last_page($page)) {
$nextpage = -1;
$nextpage = $page + 1;
}
-$accessmanager->show_attempt_timer_if_needed($attemptobj->get_attempt(), time());
+$accessmanager->show_attempt_timer_if_needed($attemptobj->get_attempt(), time(), $output);
echo $output->attempt_page($attemptobj, $page, $accessmanager, $messages, $slots, $id, $nextpage);
// Bits of content =====================================================================
+ /**
+ * @param bool $unfinished whether there is currently an unfinished attempt active.
+ * @return string if the quiz policies merit it, return a warning string to
+ * be displayed in a javascript alert on the start attempt button.
+ */
+ public function confirm_start_attempt_message($unfinished) {
+ if ($unfinished) {
+ return '';
+ }
+
+ if ($this->quiz->timelimit && $this->quiz->attempts) {
+ return get_string('confirmstartattempttimelimit', 'quiz', $this->quiz->attempts);
+ } else if ($this->quiz->timelimit) {
+ return get_string('confirmstarttimelimit', 'quiz');
+ } else if ($this->quiz->attempts) {
+ return get_string('confirmstartattemptlimit', 'quiz', $this->quiz->attempts);
+ }
+
+ return '';
+ }
+
+ /**
+ * If $reviewoptions->attempt is false, meaning that students can't review this
+ * attempt at the moment, return an appropriate string explaining why.
+ *
+ * @param int $when One of the mod_quiz_display_options::DURING,
+ * IMMEDIATELY_AFTER, LATER_WHILE_OPEN or AFTER_CLOSE constants.
+ * @param bool $short if true, return a shorter string.
+ * @return string an appropraite message.
+ */
+ public function cannot_review_message($when, $short = false) {
+
+ if ($short) {
+ $langstrsuffix = 'short';
+ $dateformat = get_string('strftimedatetimeshort', 'langconfig');
+ } else {
+ $langstrsuffix = '';
+ $dateformat = '';
+ }
+
+ if ($when == mod_quiz_display_options::DURING ||
+ $when == mod_quiz_display_options::IMMEDIATELY_AFTER) {
+ return '';
+ } else if ($when == mod_quiz_display_options::LATER_WHILE_OPEN && $this->quiz->timeclose &&
+ $this->quiz->reviewattempt & mod_quiz_display_options::AFTER_CLOSE) {
+ return get_string('noreviewuntil' . $langstrsuffix, 'quiz',
+ userdate($this->quiz->timeclose, $dateformat));
+ } else {
+ return get_string('noreview' . $langstrsuffix, 'quiz');
+ }
+ }
+
/**
* @param string $title the name of this particular quiz page.
* @return array the data that needs to be sent to print_header_simple as the $navigation
// Bits of content =====================================================================
+ /**
+ * If $reviewoptions->attempt is false, meaning that students can't review this
+ * attempt at the moment, return an appropriate string explaining why.
+ *
+ * @param bool $short if true, return a shorter string.
+ * @return string an appropraite message.
+ */
+ public function cannot_review_message($short = false) {
+ return $this->quizobj->cannot_review_message(
+ $this->get_attempt_state(), $short);
+ }
+
/**
* Initialise the JS etc. required all the questions on a page..
* @param mixed $page a page number, or 'all'.
return $cache[$quiz->id];
}
-function quiz_no_questions_message($quiz, $cm, $context) {
- global $OUTPUT;
-
- $output = '';
- $output .= $OUTPUT->notification(get_string('noquestions', 'quiz'));
- if (has_capability('mod/quiz:manage', $context)) {
- $output .= $OUTPUT->single_button(new moodle_url('/mod/quiz/edit.php',
- array('cmid' => $cm->id)), get_string('editquiz', 'quiz'), 'get');
- }
-
- return $output;
-}
-
/**
* Update the sumgrades field of the quiz. This needs to be called whenever
* the grading structure of the quiz is changed. For example if a question is
'core_question_engine'),
'strings' => array(
array('cancel', 'moodle'),
- array('timesup', 'quiz'),
- array('functiondisabledbysecuremode', 'quiz'),
array('flagged', 'question'),
+ array('functiondisabledbysecuremode', 'quiz'),
+ array('startattempt', 'quiz'),
+ array('timesup', 'quiz'),
),
);
}
e.halt();
},
+ /**
+ * Event handler for the quiz start attempt button.
+ */
+ start_attempt_action: function(e, args) {
+ if (args.startattemptwarning == '') {
+ openpopup(e, args);
+ } else {
+ M.util.show_confirm_dialog(e, {
+ message: args.startattemptwarning,
+ callback: function() {
+ openpopup(e, args);
+ },
+ continuelabel: M.util.get_string('startattempt', 'quiz')
+ });
+ }
+ },
+
init_close_button: function(Y, url) {
Y.on('click', function(e) {
M.mod_quiz.secure_window.close(url, 0)
return $output;
}
+ /**
+ * Output the JavaScript required to initialise the countdown timer.
+ * @param int $timerstartvalue time remaining, in seconds.
+ */
+ public function initialise_timer($timerstartvalue) {
+ $this->page->requires->js_init_call('M.mod_quiz.timer.init',
+ array($timerstartvalue), false, quiz_get_js_module());
+ }
+
+ /**
+ * Output a page with an optional message, and JavaScript code to close the
+ * current window and redirect the parent window to a new URL.
+ * @param moodle_url $url the URL to redirect the parent window to.
+ * @param string $message message to display before closing the window. (optional)
+ * @return string HTML to output.
+ */
+ public function close_attempt_popup($url, $message = '') {
+ $output = '';
+ $output .= $this->header();
+ $output .= $this->box_start();
+
+ if ($message) {
+ $output .= html_writer('p', $message);
+ $output .= html_writer('p', get_string('windowclosing', 'quiz'));
+ $delay = 5;
+ } else {
+ $output .= html_writer('p', get_string('pleaseclose', 'quiz'));
+ $delay = 0;
+ }
+ $this->page->requires->js_function_call('M.mod_quiz.secure_window.close',
+ array($url, $delay));
+
+ $output .= $this->box_end();
+ $output .= $this->footer();
+ return $output;
+ }
+
/**
* Print each message in an array, surrounded by <p>, </p> tags.
*
* @param array $messages the array of message strings.
* @param bool $return if true, return a string, instead of outputting.
*
- * @return mixed, if $return is true, return the string that would have been output, otherwise
- * return null.
+ * @return string HTML to output.
*/
public function access_messages($messages) {
$output = '';
* @param array $infomessages further information about why the student cannot
* attempt this quiz now, if appicable this quiz
*/
- public function view_page($course, $quiz, $cm, $context, $infomessages, $viewobj,
- $buttontext, $preventmessages) {
+ public function view_page($course, $quiz, $cm, $context, $viewobj) {
$output = '';
- $output .= $this->view_information($course, $quiz, $cm, $context, $infomessages);
+ $output .= $this->view_information($course, $quiz, $cm, $context, $viewobj->infomessages);
$output .= $this->view_table($quiz, $context, $viewobj);
$output .= $this->view_best_score($viewobj);
$output .= $this->view_result_info($quiz, $context, $cm, $viewobj);
- $output .= $this->view_attempt_button($course, $quiz, $cm, $context, $viewobj,
- $buttontext, $preventmessages);
+ $output .= $this->box($this->view_page_buttons($viewobj), 'quizattempt');
+ return $output;
+ }
+
+ /**
+ * Work out, and render, whatever buttons, and surrounding info, should appear
+ * at the end of the review page.
+ * @param mod_quiz_view_object $viewobj the information required to display
+ * the view page.
+ * @return string HTML to output.
+ */
+ public function view_page_buttons(mod_quiz_view_object $viewobj) {
+ $output = '';
+
+ if (!$viewobj->quizhasquestions) {
+ $output .= $this->no_questions_message($viewobj->canedit, $viewobj->editurl);
+ }
+
+ $output .= $this->access_messages($viewobj->preventmessages);
+
+ if ($viewobj->buttontext) {
+ $output .= $this->start_attempt_button($viewobj->buttontext,
+ $viewobj->startattempturl, $viewobj->startattemptwarning,
+ $viewobj->popuprequired, $viewobj->popupoptions);
+
+ } else if ($viewobj->buttontext === '') {
+ // We should show a 'back to the course' button.
+ $output .= $this->single_button($viewobj->backtocourseurl,
+ get_string('backtocourse', 'quiz'), 'get',
+ array('class' => 'continuebutton'));
+ }
+
+ return $output;
+ }
+
+ /**
+ * Generates the view attempt button
+ *
+ * @param int $course The course ID
+ * @param array $quiz Array containging quiz date
+ * @param int $cm The Course Module ID
+ * @param int $context The page Context ID
+ * @param mod_quiz_view_object $viewobj
+ * @param string $buttontext
+ */
+ public function start_attempt_button($buttontext, moodle_url $url,
+ $startattemptwarning, $popuprequired, $popupoptions) {
+
+ $button = new single_button($url, $buttontext);
+ $button->class .= ' quizstartbuttondiv';
+
+ $warning = '';
+ if ($popuprequired) {
+ $this->page->requires->js_module(quiz_get_js_module());
+ $this->page->requires->js('/mod/quiz/module.js');
+ $popupaction = new popup_action('click', $url, 'quizpopup', $popupoptions);
+
+ $button->class .= ' quizsecuremoderequired';
+ $button->add_action(new component_action('click',
+ 'M.mod_quiz.secure_window.start_attempt_action', array(
+ 'url' => $url->out(false),
+ 'windowname' => 'quizpopup',
+ 'popupoptions' => $popupaction->get_js_options(),
+ 'fullscreen' => true,
+ 'startattemptwarning' => $startattemptwarning,
+ )));
+
+ $warning = html_writer::tag('noscript', $this->heading(get_string('noscript', 'quiz')));
+
+ } else if ($startattemptwarning) {
+ $button->add_action(new confirm_action($startattemptwarning, null,
+ get_string('startattempt', 'quiz')));
+ }
+
+ return $this->render($button) . $warning;
+ }
+
+ /**
+ * Generate a message saying that this quiz has no questions, with a button to
+ * go to the edit page, if the user has the right capability.
+ * @param object $quiz the quiz settings.
+ * @param object $cm the course_module object.
+ * @param object $context the quiz context.
+ * @return string HTML to output.
+ */
+ function no_questions_message($canedit, $editurl) {
+ $output = '';
+ $output .= $this->notification(get_string('noquestions', 'quiz'));
+ if ($canedit) {
+ $output .= $this->single_button($editurl, get_string('editquiz', 'quiz'), 'get');
+ }
+
return $output;
}
if ($viewobj->canreviewmine) {
$row[] = $viewobj->accessmanager->make_review_link($attempt,
- $viewobj->canpreview, $attemptoptions);
+ $attemptoptions, $this);
}
if ($viewobj->feedbackcolumn && $attempt->timefinish > 0) {
}
/**
- * Generates the view attempt button
+ * Output either a link to the review page for an attempt, or a button to
+ * open the review in a popup window.
*
- * @param int $course The course ID
- * @param array $quiz Array containging quiz date
- * @param int $cm The Course Module ID
- * @param int $context The page Context ID
- * @param mod_quiz_view_object $viewobj
- * @param string $buttontext
+ * @param moodle_url $url of the target page.
+ * @param bool $reviewinpopup whether a pop-up is required.
+ * @param array $popupoptions options to pass to the popup_action constructor.
+ * @return string HTML to output.
*/
- public function view_attempt_button($course, $quiz, $cm, $context, $viewobj,
- $buttontext, $preventmessages) {
- $output = '';
- // Determine if we should be showing a start/continue attempt button,
- // or a button to go back to the course page.
- $output .= $this->box_start('quizattempt');
+ public function review_link($url, $reviewinpopup, $popupoptions) {
+ if ($reviewinpopup) {
+ $button = new single_button($url, get_string('review', 'quiz'));
+ $button->add_action(new popup_action('click', $url, 'quizpopup', $popupoptions));
+ return $this->render($button);
- // Now actually print the appropriate button.
- if (!quiz_clean_layout($quiz->questions, true)) {
- $output .= quiz_no_questions_message($quiz, $cm, $context);
- }
-
- if ($preventmessages) {
- $output .= $this->access_messages($preventmessages);
- }
-
- if ($buttontext) {
- $output .= $viewobj->accessmanager->print_start_attempt_button($viewobj->canpreview,
- $buttontext, $viewobj->unfinished);
- } else if ($buttontext === '') {
- $output .= $this->single_button(new moodle_url('/course/view.php',
- array('id' => $course->id)), get_string('backtocourse', 'quiz'), 'get',
- array('class' => 'continuebutton'));
+ } else {
+ return html_writer::link($url, get_string('review', 'quiz'),
+ array('title' => get_string('reviewthisattempt', 'quiz')));
}
- $output .= $this->box_end();
+ }
- return $output;
+ /**
+ * Displayed where there might normally be a review link, to explain why the
+ * review is not available at this time.
+ * @param string $message optional message explaining why the review is not possible.
+ * @return string HTML to output.
+ */
+ public function no_review_message($message) {
+ return html_writer::nonempty_tag('span', $message,
+ array('class' => 'noreviewmessage'));
}
/**
}
class mod_quiz_view_object {
- /**
- * @var array $attempt contains all the user's attempts at this quiz.
- */
+ /** @var array $infomessages of messages with information to display about the quiz. */
+ public $infomessages;
+ /** @var array $attempt contains all the user's attempts at this quiz. */
public $attempts;
- /**
- * @var object $accessmanager contains various access rules.
- */
+ /** @var quiz_access_manager $accessmanager contains various access rules. */
public $accessmanager;
- /**
- * @var int $canattempt determins capability for attempting a quiz.
- */
- public $canattempt;
- /**
- * @var int $canpreview determins capability for previewing a quiz.
- */
- public $canpreview;
- /**
- * @var int $canreviewmine determins capability for reviwing own quiz.
- */
+ /** @var bool $canreviewmine whether the current user has the capability to review their own attempts. */
public $canreviewmine;
- /**
- * @var int $attemptcolumn contains the number of attempts done.
- */
+ /** @var bool $canedit whether the current user has the capability to edit the quiz. */
+ public $canedit;
+ /** @var moodle_url $editurl the URL for editing this quiz. */
+ public $editurl;
+ /** @var int $attemptcolumn contains the number of attempts done. */
public $attemptcolumn;
- /**
- * @var int $gradecolumn contains the grades of any attempts.
- */
+ /** @var int $gradecolumn contains the grades of any attempts. */
public $gradecolumn;
- /**
- * @var int $markcolumn contains the marks of any attempt.
- */
+ /** @var int $markcolumn contains the marks of any attempt. */
public $markcolumn;
- /**
- * @var int $overallstats contains all marks for any attempt.
- */
+ /** @var int $overallstats contains all marks for any attempt. */
public $overallstats;
- /**
- * @var string $feedbackcolumn contains any feedback for and attempt.
- */
+ /** @var string $feedbackcolumn contains any feedback for and attempt. */
public $feedbackcolumn;
- /**
- * @var string $timenow contains a timestamp in string format.
- */
+ /** @var string $timenow contains a timestamp in string format. */
public $timenow;
- /**
- * @var int $numattempts contains the total number of attempts.
- */
+ /** @var int $numattempts contains the total number of attempts. */
public $numattempts;
- /**
- * @var int $mygrade contains the current users final grade for a quiz.
- */
+ /** @var float $mygrade contains the user's final grade for a quiz. */
public $mygrade;
- /**
- * @var int $moreattempts total attempts left.
- */
+ /** @var bool $moreattempts whether this user is allowed more attempts. */
public $moreattempts;
- /**
- * @var int $mygradeoverridden contains an overriden grade.
- */
+ /** @var int $mygradeoverridden contains an overriden grade. */
public $mygradeoverridden;
- /**
- * @var string $gradebookfeedback contains any feedback for a gradebook.
- */
+ /** @var string $gradebookfeedback contains any feedback for a gradebook. */
public $gradebookfeedback;
- /**
- * @var int $unfinished contains 1 if an attempt is unfinished.
- */
+ /** @var bool $unfinished contains 1 if an attempt is unfinished. */
public $unfinished;
- /**
- * @var int $lastfinishedattempt contains a pointer to the last attempt in the attempts array.
- */
+ /** @var object $lastfinishedattempt the last attempt from the attempts array. */
public $lastfinishedattempt;
+ /** @var array $preventmessages of messages telling the user why they can't attempt the quiz now. */
+ public $preventmessages;
+ /** @var string $buttontext caption for the start attempt button. If this is null, show no
+ * button, or if it is '' show a back to the course button. */
+ public $buttontext;
+ /** @var string $startattemptwarning alert to show the user before starting an attempt. */
+ public $startattemptwarning;
+ /** @var moodle_url $startattempturl URL to start an attempt. */
+ public $startattempturl;
+ /** @var moodle_url $startattempturl URL for any Back to the course button. */
+ public $backtocourseurl;
+ /** @var bool whether the attempt must take place in a popup window. */
+ public $popuprequired;
+ /** @var array options to use for the popup window, if required. */
+ public $popupoptions;
+ /** @var bool $quizhasquestions whether the quiz has any questions. */
+ public $quizhasquestions;
}
function quiz_report_default_report($context) {
return reset(quiz_report_list($context));
}
+
+/**
+ * Generate a message saying that this quiz has no questions, with a button to
+ * go to the edit page, if the user has the right capability.
+ * @param object $quiz the quiz settings.
+ * @param object $cm the course_module object.
+ * @param object $context the quiz context.
+ * @return string HTML to output.
+ */
+function quiz_no_questions_message($quiz, $cm, $context) {
+ global $OUTPUT;
+
+ $output = '';
+ $output .= $OUTPUT->notification(get_string('noquestions', 'quiz'));
+ if (has_capability('mod/quiz:manage', $context)) {
+ $output .= $OUTPUT->single_button(new moodle_url('/mod/quiz/edit.php',
+ array('cmid' => $cm->id)), get_string('editquiz', 'quiz'), 'get');
+ }
+
+ return $output;
+}
if ($attemptobj->is_own_attempt()) {
if (!$attemptobj->is_finished()) {
redirect($attemptobj->attempt_url(null, $page));
+
} else if (!$options->attempt) {
- $accessmanager->back_to_view_page($attemptobj->is_preview_user(),
- $accessmanager->cannot_review_message($attemptobj->get_attempt_state()));
+ $accessmanager->back_to_view_page($PAGE->get_renderer('mod_quiz'),
+ $attemptobj->cannot_review_message());
}
} else if (!$attemptobj->is_review_allowed()) {
// Set up 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);
-} else if ($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);
-} else {
- $PAGE->navbar->add($strreviewtitle);
- $PAGE->set_title(format_string($attemptobj->get_quiz_name()));
- $PAGE->set_heading($attemptobj->get_course()->fullname);
-}
+$PAGE->set_title(format_string($attemptobj->get_quiz_name()));
+$PAGE->set_heading($attemptobj->get_course()->fullname);
+$accessmanager->setup_attempt_page($PAGE);
// Summary table start ============================================================================
die();
} else if (!$options->attempt) {
echo $output->review_question_not_allowed(
- $accessmanager->cannot_review_message($attemptobj->get_review_options()));
+ $attemptobj->cannot_review_message());
die();
}
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
- * Unit tests for the access manager class.
+ * Unit tests for the quiz class.
*
* @package mod
* @subpackage quiz
/**
- * Unit tests for the access manager class
+ * Unit tests for the quiz class
*
* @copyright 2008 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class quiz_access_manager_test extends UnitTestCase {
+class quiz_class_test extends UnitTestCase {
public function test_cannot_review_message() {
$quiz = new stdClass();
$quiz->reviewattempt = 0x10010;
$quizobj = new quiz($quiz, $cm, new stdClass(), false);
- $am = new quiz_access_manager($quizobj, time(), false);
-
$this->assertEqual('',
- $am->cannot_review_message(mod_quiz_display_options::DURING));
+ $quizobj->cannot_review_message(mod_quiz_display_options::DURING));
$this->assertEqual('',
- $am->cannot_review_message(mod_quiz_display_options::IMMEDIATELY_AFTER));
+ $quizobj->cannot_review_message(mod_quiz_display_options::IMMEDIATELY_AFTER));
$this->assertEqual(get_string('noreview', 'quiz'),
- $am->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN));
+ $quizobj->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN));
$this->assertEqual(get_string('noreview', 'quiz'),
- $am->cannot_review_message(mod_quiz_display_options::AFTER_CLOSE));
+ $quizobj->cannot_review_message(mod_quiz_display_options::AFTER_CLOSE));
$closetime = time() + 10000;
$quiz->timeclose = $closetime;
$quizobj = new quiz($quiz, $cm, new stdClass(), false);
- $am = new quiz_access_manager($quizobj, time(), false);
$this->assertEqual(get_string('noreviewuntil', 'quiz', userdate($closetime)),
- $am->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN));
+ $quizobj->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN));
}
}
print_error('attempterror', 'quiz', $quizobj->view_url(),
$output->print_messages($messages));
}
+
$accessmanager->do_password_check($quizobj->is_preview_user());
// Delete any previous preview attempts belonging to this user.
$PAGE->blocks->show_only_fake_blocks();
}
-if ($accessmanager->securewindow_required($attemptobj->is_preview_user())) {
- $accessmanager->setup_secure_page($attemptobj->get_course()->shortname . ': ' .
- format_string($attemptobj->get_quiz_name()), '');
-} else if ($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);
-} else {
- $PAGE->navbar->add(get_string('summaryofattempt', 'quiz'));
- $PAGE->set_title(format_string($attemptobj->get_quiz_name()));
- $PAGE->set_heading($attemptobj->get_course()->fullname);
-}
+$PAGE->navbar->add(get_string('summaryofattempt', 'quiz'));
+$PAGE->set_title(format_string($attemptobj->get_quiz_name()));
+$PAGE->set_heading($attemptobj->get_course()->fullname);
+$accessmanager->setup_attempt_page($PAGE);
// Print heading.
-$accessmanager->show_attempt_timer_if_needed($attemptobj->get_attempt(), time());
+$accessmanager->show_attempt_timer_if_needed($attemptobj->get_attempt(), time(), $output);
echo $output->summary_page($attemptobj, $displayoptions);
$viewobj = new mod_quiz_view_object();
$viewobj->attempts = $attempts;
$viewobj->accessmanager = $accessmanager;
-$viewobj->canattempt = $canattempt;
-$viewobj->canpreview = $canpreview;
$viewobj->canreviewmine = $canreviewmine;
// Print table with existing attempts
$viewobj->attemptcolumn = 1;
}
-$moreattempts = $unfinished || !$accessmanager->is_finished($numattempts, $lastfinishedattempt);
-
$viewobj->timenow = $timenow;
$viewobj->numattempts = $numattempts;
$viewobj->mygrade = $mygrade;
-$viewobj->moreattempts = $moreattempts;
+$viewobj->moreattempts = $unfinished ||
+ !$accessmanager->is_finished($numattempts, $lastfinishedattempt);
$viewobj->mygradeoverridden = $mygradeoverridden;
$viewobj->gradebookfeedback = $gradebookfeedback;
-$viewobj->unfinished = $unfinished;
$viewobj->lastfinishedattempt = $lastfinishedattempt;
+$viewobj->canedit = has_capability('mod/quiz:manage', $context);
+$viewobj->editurl = new moodle_url('/mod/quiz/edit.php', array('cmid' => $cm->id));
+$viewobj->backtocourseurl = new moodle_url('/course/view.php', array('id' => $course->id));
+$viewobj->startattempturl = $quizobj->start_attempt_url();
+$viewobj->startattemptwarning = $quizobj->confirm_start_attempt_message($unfinished);
+$viewobj->popuprequired = $accessmanager->attempt_must_be_in_popup();
+$viewobj->popupoptions = $accessmanager->get_popup_options();
// Display information about this quiz.
-$infomessages = $viewobj->accessmanager->describe_rules();
+$viewobj->infomessages = $viewobj->accessmanager->describe_rules();
if ($quiz->attempts != 1) {
- $infomessages[] = get_string('gradingmethod', 'quiz',
+ $viewobj->infomessages[] = get_string('gradingmethod', 'quiz',
quiz_get_grading_option_name($quiz->grademethod));
}
-// This will be set something if as start/continue attempt button should appear.
-$buttontext = '';
-$preventmessages = array();
-if (!quiz_clean_layout($quiz->questions, true)) {
- $buttontext = '';
+// Determine wheter a start attempt button should be displayed.
+$viewobj->quizhasquestions = (bool) quiz_clean_layout($quiz->questions, true);
+$viewobj->preventmessages = array();
+if (!$viewobj->quizhasquestions) {
+ $viewobj->buttontext = '';
} else {
- if ($viewobj->unfinished) {
- if ($viewobj->canattempt) {
- $buttontext = get_string('continueattemptquiz', 'quiz');
- } else if ($viewobj->canpreview) {
- $buttontext = get_string('continuepreview', 'quiz');
+ if ($unfinished) {
+ if ($canattempt) {
+ $viewobj->buttontext = get_string('continueattemptquiz', 'quiz');
+ } else if ($canpreview) {
+ $viewobj->buttontext = get_string('continuepreview', 'quiz');
}
} else {
- if ($viewobj->canattempt) {
- $preventmessages = $viewobj->accessmanager->prevent_new_attempt($viewobj->numattempts,
- $viewobj->lastfinishedattempt);
- if ($preventmessages) {
- $buttontext = '';
+ if ($canattempt) {
+ $viewobj->preventmessages = $viewobj->accessmanager->prevent_new_attempt(
+ $viewobj->numattempts, $viewobj->lastfinishedattempt);
+ if ($viewobj->preventmessages) {
+ $viewobj->buttontext = '';
} else if ($viewobj->numattempts == 0) {
- $buttontext = get_string('attemptquiznow', 'quiz');
+ $viewobj->buttontext = get_string('attemptquiznow', 'quiz');
} else {
- $buttontext = get_string('reattemptquiz', 'quiz');
+ $viewobj->buttontext = get_string('reattemptquiz', 'quiz');
}
- } else if ($viewobj->canpreview) {
- $buttontext = get_string('previewquiznow', 'quiz');
+ } else if ($canpreview) {
+ $viewobj->buttontext = get_string('previewquiznow', 'quiz');
}
}
// If, so far, we think a button should be printed, so check if they will be
// allowed to access it.
- if ($buttontext) {
+ if ($viewobj->buttontext) {
if (!$viewobj->moreattempts) {
- $buttontext = '';
- } else if ($viewobj->canattempt
- && $preventmessages = $viewobj->accessmanager->prevent_access()) {
- $buttontext = '';
+ $viewobj->buttontext = '';
+ } else if ($canattempt
+ && $viewobj->preventmessages = $viewobj->accessmanager->prevent_access()) {
+ $viewobj->buttontext = '';
}
}
}
// Guests can't do a quiz, so offer them a choice of logging in or going back.
if (isguestuser()) {
echo $output->view_page_guest($course, $quiz, $cm, $context, $infomessages, $viewobj);
-} else if (!isguestuser() && !($viewobj->canattempt || $viewobj->canpreview
+} else if (!isguestuser() && !($canattempt || $canpreview
|| $viewobj->canreviewmine)) {
// If they are not enrolled in this course in a good enough role, tell them to enrol.
echo $output->view_page_notenrolled($course, $quiz, $cm, $context, $infomessages, $viewobj);
} else {
- echo $output->view_page($course, $quiz, $cm, $context, $infomessages, $viewobj,
- $buttontext, $preventmessages);
+ echo $output->view_page($course, $quiz, $cm, $context, $viewobj);
}
echo $OUTPUT->footer();