Skip to content

Commit

Permalink
MDL-11047 quiz: enable sequential navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
mackensen committed Mar 15, 2012
1 parent a2b30aa commit 33c8d37
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 18 deletions.
4 changes: 4 additions & 0 deletions mod/quiz/attempt.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
32 changes: 31 additions & 1 deletion mod/quiz/attemptlib.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -708,6 +718,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.
Expand Down Expand Up @@ -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';
}
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion mod/quiz/backup/moodle2/backup_quiz_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
5 changes: 3 additions & 2 deletions mod/quiz/db/install.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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"/>
Expand Down
12 changes: 12 additions & 0 deletions mod/quiz/db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

6 changes: 6 additions & 0 deletions mod/quiz/lang/en/quiz.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.';
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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';
Expand Down
17 changes: 17 additions & 0 deletions mod/quiz/lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')
);
}
7 changes: 7 additions & 0 deletions mod/quiz/mod_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'));

Expand Down
36 changes: 25 additions & 11 deletions mod/quiz/renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
}
}

/**
Expand Down Expand Up @@ -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);
}
Expand All @@ -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(),
Expand Down
5 changes: 5 additions & 0 deletions mod/quiz/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
5 changes: 3 additions & 2 deletions mod/quiz/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;}
Expand All @@ -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;}
Expand Down
2 changes: 1 addition & 1 deletion mod/quiz/version.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

0 comments on commit 33c8d37

Please sign in to comment.