Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

MDL-11047 quiz: enable sequential navigation

  • Loading branch information...
commit 33c8d37b6f2fa380a50ef97d60103ed9a1f9c376 1 parent a2b30aa
@mackensen mackensen authored
View
4 mod/quiz/attempt.php
@@ -98,6 +98,10 @@
// Update attempt page
if ($attemptobj->get_currentpage() != $page) {
+ if ($attemptobj->get_navigation_method() == QUIZ_NAVMETHOD_SEQ && $attemptobj->get_currentpage() > $page) {
+ // Prevent out of sequence access
+ redirect($attemptobj->start_attempt_url(null, $attemptobj->get_currentpage()));
+ }
$DB->set_field('quiz_attempts', 'currentpage', $page);
}
View
32 mod/quiz/attemptlib.php
@@ -178,6 +178,11 @@ public function get_quiz_name() {
return $this->quiz->name;
}
+ /** @return int the quiz navigation method. */
+ public function get_navigation_method() {
+ return $this->quiz->navmethod;
+ }
+
/** @return int the number of attempts allowed at this quiz (0 = infinite). */
public function get_num_attempts_allowed() {
return $this->quiz->attempts;
@@ -545,6 +550,11 @@ public function get_quiz_name() {
return $this->quizobj->get_quiz_name();
}
+ /** @return int the quiz navigation method. */
+ public function get_navigation_method() {
+ return $this->quizobj->get_navigation_method();
+ }
+
/** @return object the course_module object. */
public function get_cm() {
return $this->quizobj->get_cm();
@@ -709,6 +719,21 @@ public function check_review_capability() {
}
/**
+ * Checks whether a user may navigate to a particular slot
+ */
+ public function can_navigate_to($slot) {
+ switch ($this->get_navigation_method()) {
+ case QUIZ_NAVMETHOD_FREE:
+ return true;
+ break;
+ case QUIZ_NAVMETHOD_SEQ:
+ return false;
+ break;
+ }
+ return true;
+ }
+
+ /**
* @return int one of the mod_quiz_display_options::DURING,
* IMMEDIATELY_AFTER, LATER_WHILE_OPEN or AFTER_CLOSE constants.
*/
@@ -1314,6 +1339,7 @@ public function get_question_buttons() {
$button->id = 'quiznavbutton' . $slot;
$button->number = $qa->get_question()->_number;
$button->stateclass = $qa->get_state_class($showcorrectness);
+ $button->navmethod = $this->attemptobj->get_navigation_method();
if (!$showcorrectness && $button->stateclass == 'notanswered') {
$button->stateclass = 'complete';
}
@@ -1380,7 +1406,11 @@ public function user_picture() {
*/
class quiz_attempt_nav_panel extends quiz_nav_panel_base {
public function get_question_url($slot) {
- return $this->attemptobj->attempt_url($slot, -1, $this->page);
+ if ($this->attemptobj->can_navigate_to($slot)) {
+ return $this->attemptobj->attempt_url($slot, -1, $this->page);
+ } else {
+ return null;
+ }
}
public function render_before_button_bits(mod_quiz_renderer $output) {
View
2  mod/quiz/backup/moodle2/backup_quiz_stepslib.php
@@ -42,7 +42,7 @@ protected function define_structure() {
$quiz = new backup_nested_element('quiz', array('id'), array(
'name', 'intro', 'introformat', 'timeopen',
'timeclose', 'preferredbehaviour', 'attempts_number',
- 'attemptonlast', 'grademethod', 'decimalpoints', 'questiondecimalpoints',
+ 'attemptonlast', 'grademethod', 'navmethod', 'decimalpoints', 'questiondecimalpoints',
'reviewattempt', 'reviewcorrectness', 'reviewmarks',
'reviewspecificfeedback', 'reviewgeneralfeedback',
'reviewrightanswer', 'reviewoverallfeedback',
View
5 mod/quiz/db/install.xml
@@ -26,8 +26,9 @@
<FIELD NAME="reviewgeneralfeedback" TYPE="int" LENGTH="6" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether users are allowed to review their quiz attempts at various times. A bit field, like reviewattempt." PREVIOUS="reviewspecificfeedback" NEXT="reviewrightanswer"/>
<FIELD NAME="reviewrightanswer" TYPE="int" LENGTH="6" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether users are allowed to review their quiz attempts at various times. A bit field, like reviewattempt." PREVIOUS="reviewgeneralfeedback" NEXT="reviewoverallfeedback"/>
<FIELD NAME="reviewoverallfeedback" TYPE="int" LENGTH="6" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether users are allowed to review their quiz attempts at various times. A bit field, like reviewattempt." PREVIOUS="reviewrightanswer" NEXT="questionsperpage"/>
- <FIELD NAME="questionsperpage" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="reviewoverallfeedback" NEXT="shufflequestions"/>
- <FIELD NAME="shufflequestions" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="questionsperpage" NEXT="shuffleanswers"/>
+ <FIELD NAME="questionsperpage" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="reviewoverallfeedback" NEXT="navmethod"/>
+ <FIELD NAME="navmethod" TYPE="char" LENGTH="16" NOTNULL="true" DEFAULT="free" SEQUENCE="false" PREVIOUS="questionsperpage" NEXT="shufflequestions"/>
+ <FIELD NAME="shufflequestions" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="navmethod" NEXT="shuffleanswers"/>
<FIELD NAME="shuffleanswers" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="shufflequestions" NEXT="questions"/>
<FIELD NAME="questions" TYPE="text" NOTNULL="true" SEQUENCE="false" PREVIOUS="shuffleanswers" NEXT="sumgrades"/>
<FIELD NAME="sumgrades" TYPE="number" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" DECIMALS="5" PREVIOUS="questions" NEXT="grade"/>
View
12 mod/quiz/db/upgrade.php
@@ -82,6 +82,18 @@ function xmldb_quiz_upgrade($oldversion) {
upgrade_mod_savepoint(true, 2011120703, 'quiz');
}
+ if ($oldversion < 2011120704) {
+ // Configuration option for navigation method
+ $table = new xmldb_table('quiz');
+
+ $field = new xmldb_field('navmethod', XMLDB_TYPE_CHAR, '16', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 'free');
+
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+ upgrade_mod_savepoint(true, 2011120704, 'quiz');
+ }
+
return true;
}
View
6 mod/quiz/lang/en/quiz.php
@@ -163,6 +163,7 @@
$string['configintro'] = 'The values you set here define the default values that are used in the settings form when you create a new quiz. You can also configure which quiz settings are considered advanced.';
$string['configmaximumgrade'] = 'The default grade that the quiz grade is scaled to be out of.';
$string['confignewpageevery'] = 'When adding questions to the quiz page breaks will automatically be inserted according to the setting you choose here.';
+$string['confignavmethod'] = 'In Free navigation, questions may be answered in any order using navigation. In Sequential, questions must be answered in strict sequence.';
$string['configpenaltyscheme'] = 'Penalty subtracted for each wrong response in adaptive mode.';
$string['configpopup'] = 'Force the attempt to open in a popup window, and use JavaScript tricks to try to restrict copy and paste, etc. during quiz attempts.';
$string['configrequirepassword'] = 'Students must enter this password before they can attempt the quiz.';
@@ -412,6 +413,10 @@
$string['multipleanswers'] = 'Choose at least one answer.';
$string['multiplier'] = 'Multiplier';
$string['name'] = 'Name';
+$string['navmethod'] = 'Navigation method';
+$string['navmethod_free'] = 'Free';
+$string['navmethod_help'] = 'When sequential navigation is enabled a student must progress through the quiz in order and may not return to previous pages nor skip ahead.';
+$string['navmethod_seq'] = 'Sequential';
$string['navnojswarning'] = 'Warning: these links will not save your answers. Use the next button at the bottom of the page.';
$string['neverallononepage'] = 'Never, all questions on one page';
$string['newattemptfail'] = 'Error: Could not start a new attempt at the quiz';
@@ -625,6 +630,7 @@
$string['response'] = 'Response';
$string['responses'] = 'Responses';
$string['results'] = 'Results';
+$string['returnattempt'] = 'Return to attempt';
$string['reuseifpossible'] = 'reuse previously removed';
$string['reverttodefaults'] = 'Revert to quiz defaults';
$string['review'] = 'Review';
View
17 mod/quiz/lib.php
@@ -58,6 +58,13 @@
*/
define('QUIZ_MAX_EVENT_LENGTH', 5*24*60*60); // 5 days
+/**#@+
+ * Options for navigation method within quizzes.
+ */
+define('QUIZ_NAVMETHOD_FREE', 'free');
+define('QUIZ_NAVMETHOD_SEQ', 'sequential');
+/**#@-*/
+
/**
* Given an object containing all the necessary data,
* (defined by the form in mod_form.php) this function
@@ -1745,3 +1752,13 @@ function quiz_page_type_list($pagetype, $parentcontext, $currentcontext) {
'mod-quiz-edit'=>get_string('page-mod-quiz-edit', 'quiz'));
return $module_pagetype;
}
+
+/**
+ * @return the options for quiz navigation.
+ */
+function quiz_get_navigation_options() {
+ return array(
+ QUIZ_NAVMETHOD_FREE => get_string('navmethod_free', 'quiz'),
+ QUIZ_NAVMETHOD_SEQ => get_string('navmethod_seq', 'quiz')
+ );
+}
View
7 mod/quiz/mod_form.php
@@ -154,6 +154,13 @@ protected function definition() {
$mform->addHelpButton('questionsperpagegrp', 'newpage', 'quiz');
$mform->setAdvanced('questionsperpagegrp', $quizconfig->questionsperpage_adv);
+ // Navigation method
+ $mform->addElement('select', 'navmethod', get_string('navmethod', 'quiz'),
+ quiz_get_navigation_options());
+ $mform->addHelpButton('navmethod', 'navmethod', 'quiz');
+ $mform->setAdvanced('navmethod', $quizconfig->navmethod_adv);
+ $mform->setDefault('navmethod', $quizconfig->navmethod);
+
//-------------------------------------------------------------------------------
$mform->addElement('header', 'interactionhdr', get_string('questionbehaviour', 'quiz'));
View
36 mod/quiz/renderer.php
@@ -312,7 +312,7 @@ public function navigation_panel(quiz_nav_panel_base $panel) {
* @param quiz_nav_question_button $button
*/
protected function render_quiz_nav_question_button(quiz_nav_question_button $button) {
- $classes = array('qnbutton', $button->stateclass);
+ $classes = array('qnbutton', $button->stateclass, $button->navmethod);
$attributes = array();
if ($button->currentpage) {
@@ -338,13 +338,17 @@ protected function render_quiz_nav_question_button(quiz_nav_question_button $but
$a = new stdClass();
$a->number = $button->number;
$a->attributes = implode(' ', $attributes);
-
- return html_writer::link($button->url,
- html_writer::tag('span', '', array('class' => 'thispageholder')) .
- html_writer::tag('span', '', array('class' => 'trafficlight')) .
- get_string($qnostring, 'quiz', $a),
- array('class' => implode(' ', $classes), 'id' => $button->id,
- 'title' => $button->statestring));
+ $tagcontents = html_writer::tag('span', '', array('class' => 'thispageholder')) .
+ html_writer::tag('span', '', array('class' => 'trafficlight')) .
+ get_string($qnostring, 'quiz', $a);
+ $tagattributes = array('class' => implode(' ', $classes), 'id' => $button->id,
+ 'title' => $button->statestring);
+
+ if ($button->url) {
+ return html_writer::link($button->url, $tagcontents, $tagattributes);
+ } else {
+ return html_writer::tag('span', $tagcontents, $tagattributes);
+ }
}
/**
@@ -572,9 +576,13 @@ public function summary_table($attemptobj, $displayoptions) {
$flag = html_writer::empty_tag('img', array('src' => $this->pix_url('i/flagged'),
'alt' => get_string('flagged', 'question'), 'class' => 'questionflag'));
}
- $row = array(html_writer::link($attemptobj->attempt_url($slot),
- $attemptobj->get_question_number($slot) . $flag),
- $attemptobj->get_question_status($slot, $displayoptions->correctness));
+ if ($attemptobj->can_navigate_to($slot)) {
+ $row = array(html_writer::link($attemptobj->attempt_url($slot),
+ $attemptobj->get_question_number($slot) . $flag),
+ $attemptobj->get_question_status($slot, $displayoptions->correctness));
+ } else {
+ $row = array($attemptobj->get_question_number($slot) . $flag, $attemptobj->get_question_status($slot, $displayoptions->correctness));
+ }
if ($markscolumn) {
$row[] = $attemptobj->get_question_mark($slot);
}
@@ -599,6 +607,12 @@ public function summary_page_controls($attemptobj) {
// countdown timer
$output .= $this->countdown_timer();
+ // Return to place button
+ $button = new single_button(
+ new moodle_url($attemptobj->attempt_url(null, $attemptobj->get_currentpage())), get_string('returnattempt', 'quiz'));
+ $output .= $this->container($this->container($this->render($button),
+ 'controls'), 'submitbtns mdl-align');
+
// Finish attempt button.
$options = array(
'attempt' => $attemptobj->get_attemptid(),
View
5 mod/quiz/settings.php
@@ -89,6 +89,11 @@
get_string('newpageevery', 'quiz'), get_string('confignewpageevery', 'quiz'),
array('value' => 1, 'fix' => false), $perpage));
+// Navigation method
+$quizsettings->add(new admin_setting_configselect_with_advanced('quiz/navmethod',
+ get_string('navmethod', 'quiz'), get_string('confignavmethod', 'quiz'),
+ array('value' => QUIZ_NAVMETHOD_FREE, 'adv' => true), quiz_get_navigation_options()));
+
// Shuffle within questions
$quizsettings->add(new admin_setting_configcheckbox_with_advanced('quiz/shuffleanswers',
get_string('shufflewithin', 'quiz'), get_string('configshufflewithin', 'quiz'),
View
5 mod/quiz/styles.css
@@ -21,8 +21,6 @@ body.jsenabled .questionflagcheckbox {display: none;}
.path-mod-quiz #user-picture img {width: auto;height: auto;float: left;}
.path-mod-quiz .qnbutton {display: block; position: relative; float: left; width: 1.5em; height: 1.5em; overflow: hidden; margin: 0.3em 0.3em 0.3em 0; padding: 0; border: 1px solid #bbb; background: #ddd; text-align: center; vertical-align: middle;line-height: 1.5em !important; font-weight: bold; text-decoration: none;}
-.path-mod-quiz .qnbutton:hover {text-decoration: underline;}
-.path-mod-quiz .qnbutton span {cursor: pointer; cursor: hand;}
.path-mod-quiz .qnbutton .trafficlight,
.path-mod-quiz .qnbutton .thispageholder {display: block; position: absolute; top: 0; bottom: 0; left: 0; right: 0;}
@@ -43,6 +41,9 @@ body.jsenabled .questionflagcheckbox {display: none;}
.path-mod-quiz .qnbutton.notanswered .trafficlight,
.path-mod-quiz .qnbutton.incorrect .trafficlight {border-top: 3px solid #800;}
+.path-mod-quiz .qnbutton.free:hover {text-decoration: underline;}
+.path-mod-quiz .qnbutton.free span {cursor: pointer; cursor: hand;}
+
.path-mod-quiz .othernav {clear: both; margin: 0.5em 0;}
.path-mod-quiz .othernav a,
.path-mod-quiz .othernav input {display: block;margin: 0.5em 0;}
View
2  mod/quiz/version.php
@@ -25,7 +25,7 @@
defined('MOODLE_INTERNAL') || die();
-$module->version = 2011120703; // The current module version (Date: YYYYMMDDXX)
+$module->version = 2011120704; // The current module version (Date: YYYYMMDDXX)
$module->requires = 2011112900; // Requires this Moodle version
$module->component = 'mod_quiz'; // Full name of the plugin (used for diagnostics)
$module->cron = 60;
Please sign in to comment.
Something went wrong with that request. Please try again.