From: tjhunt Date: Tue, 10 Mar 2009 08:39:51 +0000 (+0000) Subject: quiz settings: MDL-18485 Improve quiz settings form X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=84e628a027e2a7c3d0481d6f1893cd17e631d690;p=moodle.git quiz settings: MDL-18485 Improve quiz settings form * Reorder form fields to group things more logically. ** and on the corresponding admin page too. * Set some options to be 'Advanced' by default: ** Apply penalties. ** Each attempt builds on the last. ** Decimal places for question grades. ** The five 'Extra restrictions on attempts' settings. (password, etc.) * Admins can still change this to suit their institiution at Administration > Plugins > Activity modules > Quiz. * These new defaults are applied if the admin had not previously set any fields to be advanced. * Disable some filds when they are not applicable: ** Grading method, if num attempts = 1 ** Penaly scheme, if adaptive mode = no ** Each attempt builds of last, if num attempts = 1 ** Review after quiz closed options, if no close date. ** Delay between 1st and 2nd attempts, if num attempts = 1 ** Delay between later attempts, if num attempts < 3 * Convert quiz.timelimit to be in seconds, for consistency, and ready for the new duration field type (MDL 18500). ** Including ensuring that backup and restore is backwards compatible. * MDL-5537 New setting, questiondecimalpoints, so, for example, you can show the quiz grade as an integer, but have fractional question grades. ** There is a 'Same as overall decimal points' option, which is the default. * Improve some field labels. * Make corresponding changes in the help files. --- diff --git a/lang/en_utf8/quiz.php b/lang/en_utf8/quiz.php index 2faa8b7157..bb4b6a3445 100644 --- a/lang/en_utf8/quiz.php +++ b/lang/en_utf8/quiz.php @@ -145,15 +145,19 @@ $string['completedon'] = 'Completed on'; $string['configadaptive'] = 'If you choose Yes for this option then the student will be allowed multiple responses to a question even within the same attempt at the quiz.'; $string['configattemptsallowed'] = 'Restriction on the number of attempts students are allowed at the quiz.'; $string['configdecimaldigits'] = 'Number of digits that should be shown after the decimal point when displaying grades.'; +$string['configdecimalplaces'] = 'Number of digits that should be shown after the decimal point when displaying grades for the quiz.'; +$string['configdecimalplacesquestion'] = 'Number of digits that should be shown after the decimal point when displaying the grade for individual questions.'; $string['configdelay1'] = 'If you set a time delay, then a student has to wait for that time before they can attempt a quiz after the first attempt.'; $string['configdelay2'] = 'If you set a time delay here, then a student has to wait for that time before they can attempt their third or later attempts.'; +$string['configdelay1st2nd'] = 'If you set a time delay here, the student cannot start their second attempt until this much time has passed since the end of their first attempt.'; +$string['configdelaylater'] = 'If you set a time delay here, the student cannot start their third, fourth, ... attempt until this much time has passed since the end of their previous attempt.'; $string['configeachattemptbuildsonthelast'] = 'If multiple attempts are allowed then each new attempt contains the results of the previous attempt.'; $string['configgrademethod'] = 'When multiple attempts are allowed, which method should be used to calculate the student\'s final grade for the quiz.'; $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['configpenaltyscheme'] = 'Penalty subtracted for each wrong response in adaptive mode.'; -$string['configpopup'] = 'Use JavaScript tricks to try to restrict copy and paste, etc. during quiz attempts.'; +$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.'; $string['configrequiresubnet'] = 'Students can only attempt the quiz from these computers.'; $string['configreviewoptions'] = 'These options control what information users can see when they review a quiz attempt or look at the quiz reports.'; @@ -161,6 +165,7 @@ $string['configshowuserpicture'] = 'Show the user\'s picture on screen during at $string['configshufflequestions'] = 'If you enable this option, then the order of questions in the quiz will be randomly shuffled each time a student attempts the quiz.'; $string['configshufflewithin'] = 'If you enable this option, then the parts making up the individual questions will be randomly shuffled each time a student starts an attempt at this quiz, provided the option is also enabled in the question settings.'; $string['configtimelimit'] = 'Default time limit for quizzes in minutes. 0 mean no time limit.'; +$string['configtimelimitsec'] = 'Default time limit for quizzes in seconds. 0 mean no time limit.'; $string['configurerandomquestion'] = 'Configure question'; $string['confirmclose'] = 'You are about to close this attempt. Once you close the attempt you will no longer be able to change your answers.'; $string['confirmserverdelete'] = 'Are you sure you want to remove the server $a from the list?'; @@ -195,6 +200,8 @@ $string['datasetdefinitions'] = 'Reusable dataset definitions for category $a'; $string['datasetnumber'] = 'Number'; $string['daysavailable'] = 'Days available'; $string['decimaldigits'] = 'Decimal digits in grades'; +$string['decimalplaces'] = 'Decimal places in grades'; +$string['decimalplacesquestion'] = 'Decimal places in question grades'; $string['decimalformat'] = 'decimals'; $string['decimalpoints'] = 'Decimal points'; $string['decimals'] = 'with $a'; @@ -203,6 +210,8 @@ $string['defaultgrade'] = 'Default question grade'; $string['defaultinfo'] = 'The default category for questions.'; $string['delay1'] = 'Time delay between first and second attempt'; $string['delay2'] = 'Time delay between later attempts'; +$string['delay1st2nd'] = 'Enforced delay between 1st and 2nd attempts'; +$string['delaylater'] = 'Enforced delay between later attempts'; $string['deleteattemptcheck'] = 'Are you absolutely sure you want to completely delete these attempts?'; $string['deletequestioncheck'] = 'Are you absolutely sure you want to delete \'$a\'?'; $string['deletequestionscheck'] = 'Are you absolutely sure you want to delete the following questions?

$a'; @@ -284,6 +293,7 @@ $string['exportingquestions'] = 'Questions are being exported to file'; $string['exportname'] = 'File name'; $string['exportnameformat'] = '%%Y%%m%%d-%%H%%M'; $string['exportquestions'] = 'Export questions to file'; +$string['extraattemptrestrictions'] = 'Extra restrictions on attempts'; $string['false'] = 'False'; $string['feedback'] = 'Feedback'; $string['feedbackerrorboundaryformat'] = 'Feedback grade boundaries must be either a percentage or a number. The value you entered in boundary $a is not recognised.'; @@ -376,6 +386,11 @@ $string['itemsource'] = 'Item Source'; $string['itemsourceformat'] = 'Item Source Format'; $string['itemtypes'] = 'Remote Question Types'; $string['lastanswer'] = 'Your last answer was'; +$string['layout'] = 'Layout'; +$string['layoutasshown'] = 'Page layout as shown.'; +$string['layoutasshownwithpages'] = 'Page layout as shown. (Automatic new page every $a questions.)'; +$string['layoutshuffledandpaged'] = 'Questions randomly shuffled with $a questions per page.'; +$string['layoutshuffledsinglepage'] = 'Questions randomly shuffled, all on one page.'; $string['learnwise'] = 'Learnwise format'; $string['link'] = 'Link'; $string['listitems'] = 'Listing of Items in Quiz'; @@ -498,6 +513,7 @@ $string['qtypename'] = 'type, name'; $string['question'] = 'Question'; $string['questionbankcontents'] = 'Question Bank contents'; $string['questionbankmanagement'] = 'Question Bank management'; +$string['questionbehaviour'] = 'Question behaviour'; $string['questioncats'] = 'Question Categories'; $string['questiondeleted'] = 'This question has been deleted. Please contact your teacher'; $string['questioninuse'] = 'The question \'$a->questionname\' is currently being used in:
$a->quiznames
The question will not be deleted from these quizzes but only from the category list.'; @@ -618,6 +634,7 @@ $string['reviewresponse'] = 'Review response'; $string['reviewthisattempt'] = 'Review your responses to this attempt'; $string['rqp'] = 'Remote Question'; $string['rqps'] = 'Remote Questions'; +$string['sameasoverall'] = 'Same as for overall grades'; $string['save'] = 'Save'; $string['saveandedit'] = 'Save changes and edit questions'; $string['saveattemptfailed'] = 'Failed to save the current quiz attempt.'; @@ -650,6 +667,7 @@ $string['showcorrectanswer'] = 'In feedback, show correct answers?'; $string['showdetailedmarks'] = 'Show mark details'; $string['showfeedback'] = 'After answering, show feedback?'; $string['showhidden'] = 'Also show old questions'; +$string['showinsecurepopup'] = 'Use a \'secure\' popup window for attempts'; $string['shownoattempts'] = 'Show students with no attempts'; $string['shownoattemptsonly'] = 'Show only students with no attempts'; $string['showquestiontext'] = 'Show question text in the question list'; @@ -658,8 +676,7 @@ $string['showuserpicture'] = 'Show the user\'s picture'; $string['shuffle'] = 'Shuffle'; $string['shuffleanswers'] = 'Shuffle answers'; $string['shufflequestions'] = 'Shuffle questions'; -$string['shufflequestionsx'] = 'Shuffle questions: $a'; -$string['shufflequestionsselected'] = '* Shuffle questions has been set so question order is random. As a result, the button Reorder questions has been disabled. You can change this in $a.'; +$string['shufflequestionsselected'] = 'Shuffle questions has been set, so some actions relating to pages are not available. To change the shuffle option go to $a.'; $string['shufflewithin'] = 'Shuffle within questions'; $string['significantfigures'] = 'with $a'; $string['significantfiguresformat'] = 'significant figures'; @@ -690,6 +707,7 @@ $string['timeleft'] = 'Time left'; $string['timelimit'] = 'Time limit'; $string['timelimitexeeded'] = 'Sorry! Quiz time limit exceeded!'; $string['timelimitmin'] = 'Time limit (minutes)'; +$string['timelimitsec'] = 'Time limit (seconds)'; $string['timestr'] = '%%H:%%M:%%S on %%d/%%m/%%y'; $string['timesup'] = 'Time is up!'; $string['timetaken'] = 'Time taken'; diff --git a/lib/questionlib.php b/lib/questionlib.php index 138dbfc33b..e6e8c13971 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -1753,7 +1753,7 @@ function question_apply_penalty_and_timelimit(&$question, &$state, $attempt, $cm // deal with timelimit if ($cmoptions->timelimit) { // We allow for 5% uncertainty in the following test - if ($state->timestamp - $attempt->timestart > $cmoptions->timelimit * 63) { + if ($state->timestamp - $attempt->timestart > $cmoptions->timelimit * 1.05) { $cm = get_coursemodule_from_instance('quiz', $cmoptions->id); if (!has_capability('mod/quiz:ignoretimelimits', get_context_instance(CONTEXT_MODULE, $cm->id), $attempt->userid, false)) { @@ -2007,12 +2007,25 @@ function question_hash($question) { /** * Round a grade to to the correct number of decimal places, and format it for display. + * If $cmoptions->questiondecimalpoints is set, that is used, otherwise + * else if $cmoptions->decimalpoints is used, + * otherwise a default of 2 is used, but this should not be relied upon, and generated a developer debug warning. + * However, if $cmoptions->questiondecimalpoints is -1, the means use $cmoptions->decimalpoints. * - * @param object $cmoptions The modules settings, only ->decimalpoints is used. + * @param object $cmoptions The modules settings. * @param float $grade The grade to round. */ function question_format_grade($cmoptions, $grade) { - return format_float($grade, $cmoptions->decimalpoints); + if (isset($cmoptions->questiondecimalpoints) && $cmoptions->questiondecimalpoints != -1) { + $decimalplaces = $cmoptions->questiondecimalpoints; + } else if (isset($cmoptions->decimalpoints)) { + $decimalplaces = $cmoptions->decimalpoints; + } else { + $decimalplaces = 2; + debugging('Code that leads to question_format_grade being called should set ' . + '$cmoptions->questiondecimalpoints or $cmoptions->decimalpoints', DEBUG_DEVELOPER); + } + return format_float($grade, $decimalplaces); } /** diff --git a/mod/quiz/accessrules.php b/mod/quiz/accessrules.php index 0d215d36c0..0d41aa593d 100644 --- a/mod/quiz/accessrules.php +++ b/mod/quiz/accessrules.php @@ -12,10 +12,11 @@ class quiz_access_manager { /** * Create an instance for a particular quiz. - * @param object $quiz the quiz we will be controlling access to. - * @param integer $timenow the - * @param boolean $canpreview whether the current user has the - * @param boolean $ignoretimelimits + * @param object $quizobj An instance of the class quiz from attemptlib.php. + * The quiz we will be controlling access to. + * @param integer $timenow The time to use as 'now'. + * @param boolean $canignoretimelimits Whether this user is exempt from time + * limits (has_capability('mod/quiz:ignoretimelimits', ...)). */ public function __construct($quizobj, $timenow, $canignoretimelimits) { $this->_quizobj = $quizobj; @@ -668,10 +669,10 @@ class password_access_rule extends quiz_access_rule_base { */ class time_limit_access_rule extends quiz_access_rule_base { public function description() { - return get_string('quiztimelimit', 'quiz', format_time($this->_quiz->timelimit * 60)); + return get_string('quiztimelimit', 'quiz', format_time($this->_quiz->timelimit)); } public function time_left($attempt, $timenow) { - return $attempt->timestart + $this->_quiz->timelimit*60 - $timenow; + return $attempt->timestart + $this->_quiz->timelimit - $timenow; } } diff --git a/mod/quiz/attemptlib.php b/mod/quiz/attemptlib.php index e812a0029a..4348c41752 100644 --- a/mod/quiz/attemptlib.php +++ b/mod/quiz/attemptlib.php @@ -604,7 +604,7 @@ class quiz_attempt extends quiz { public function get_question_score($questionid) { $options = $this->get_render_options($this->states[$questionid]); if ($options->scores) { - return quiz_format_grade($this->quiz, $this->states[$questionid]->last_graded->grade); + return quiz_format_question_grade($this->quiz, $this->states[$questionid]->last_graded->grade); } else { return ''; } diff --git a/mod/quiz/backuplib.php b/mod/quiz/backuplib.php index 230777c38c..232face751 100644 --- a/mod/quiz/backuplib.php +++ b/mod/quiz/backuplib.php @@ -269,7 +269,8 @@ fwrite ($bf,full_tag("GRADE",4,false,$quiz->grade)); fwrite ($bf,full_tag("TIMECREATED",4,false,$quiz->timecreated)); fwrite ($bf,full_tag("TIMEMODIFIED",4,false,$quiz->timemodified)); - fwrite ($bf,full_tag("TIMELIMIT",4,false,$quiz->timelimit)); + fwrite ($bf,full_tag("TIMELIMIT",4,false,round($quiz->timelimit/60))); + fwrite ($bf,full_tag("TIMELIMITSECS",4,false,$quiz->timelimit)); fwrite ($bf,full_tag("PASSWORD",4,false,$quiz->password)); fwrite ($bf,full_tag("SUBNET",4,false,$quiz->subnet)); fwrite ($bf,full_tag("POPUP",4,false,$quiz->popup)); diff --git a/mod/quiz/db/install.xml b/mod/quiz/db/install.xml index 33dddfee69..1f73ef8658 100755 --- a/mod/quiz/db/install.xml +++ b/mod/quiz/db/install.xml @@ -17,8 +17,9 @@ - - + + + @@ -27,7 +28,7 @@ - + diff --git a/mod/quiz/db/upgrade.php b/mod/quiz/db/upgrade.php index 6c813ccc9a..a81a5ecc03 100644 --- a/mod/quiz/db/upgrade.php +++ b/mod/quiz/db/upgrade.php @@ -22,7 +22,7 @@ function xmldb_quiz_upgrade($oldversion) { global $CFG, $DB; - + $dbman = $DB->get_manager(); $result = true; @@ -77,7 +77,7 @@ function xmldb_quiz_upgrade($oldversion) { upgrade_mod_savepoint($result, 2008062001, 'quiz'); } - + if ($result && $oldversion < 2008072402) { /// Define field lastcron to be added to quiz_report @@ -196,7 +196,7 @@ function xmldb_quiz_upgrade($oldversion) { // question type, this is now a no-op. upgrade_mod_savepoint($result, 2008082600, 'quiz'); } - + if ($result && $oldversion < 2008112101) { /// Define field lastcron to be added to quiz_report @@ -207,7 +207,7 @@ function xmldb_quiz_upgrade($oldversion) { if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } - + /// quiz savepoint reached upgrade_mod_savepoint($result, 2008112101, 'quiz'); } @@ -227,7 +227,55 @@ function xmldb_quiz_upgrade($oldversion) { upgrade_mod_savepoint($result, 2009010700, 'quiz'); } + if ($result && $oldversion < 2009030900) { + /// If there are no quiz settings set to advanced yet, the set up the default + /// advanced fields from Moodle 2.0. + $quizconfig = get_config('quiz'); + $arealreadyadvanced = false; + foreach (array($quizconfig) as $name => $value) { + if (strpos($name, 'fix_') === 0 && !empty($value)) { + $arealreadyadvanced = true; + break; + } + } + + if (!$arealreadyadvanced) { + set_config('fix_penaltyscheme', 1, 'quiz'); + set_config('fix_attemptonlast', 1, 'quiz'); + set_config('fix_questiondecimalpoints', 1, 'quiz'); + set_config('fix_password', 1, 'quiz'); + set_config('fix_subnet', 1, 'quiz'); + set_config('fix_delay1', 1, 'quiz'); + set_config('fix_delay2', 1, 'quiz'); + set_config('fix_popup', 1, 'quiz'); + } + + /// quiz savepoint reached + upgrade_mod_savepoint($result, 2009030900, 'quiz'); + } + + if ($result && $oldversion < 2009031000) { + /// Add new questiondecimaldigits setting, separate form the overall decimaldigits one. + $table = new xmldb_table('quiz'); + $field = new xmldb_field('questiondecimalpoints', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, null, null, '2', 'decimalpoints'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + /// quiz savepoint reached + upgrade_mod_savepoint($result, 2009031000, 'quiz'); + } + + if ($result && $oldversion < 2009031001) { + /// Convert quiz.timelimit from minutes to seconds. + $DB->execute('UPDATE {quiz} SET timelimit = timelimit * 60'); + $default = get_config('quiz', 'timelimit'); + set_config('timelimit', 60 * $default, 'quiz'); + + /// quiz savepoint reached + upgrade_mod_savepoint($result, 2009031001, 'quiz'); + } + return $result; } -?> diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index 5cc08c6d56..f244b7d7ee 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -1,16 +1,40 @@ libdir.'/pagelib.php'); -require_once($CFG->libdir.'/eventslib.php'); +require_once($CFG->libdir . '/pagelib.php'); +require_once($CFG->libdir . '/eventslib.php'); /// CONSTANTS /////////////////////////////////////////////////////////////////// @@ -18,12 +42,17 @@ require_once($CFG->libdir.'/eventslib.php'); * Options determining how the grades from individual attempts are combined to give * the overall grade for a user */ -define("QUIZ_GRADEHIGHEST", "1"); -define("QUIZ_GRADEAVERAGE", "2"); -define("QUIZ_ATTEMPTFIRST", "3"); -define("QUIZ_ATTEMPTLAST", "4"); +define('QUIZ_GRADEHIGHEST', 1); +define('QUIZ_GRADEAVERAGE', 2); +define('QUIZ_ATTEMPTFIRST', 3); +define('QUIZ_ATTEMPTLAST', 4); /**#@-*/ +define('QUIZ_MAX_ATTEMPT_OPTION', 10); +define('QUIZ_MAX_QPP_OPTION', 50); +define('QUIZ_MAX_DECIMAL_OPTION', 5); +define('QUIZ_MAX_Q_DECIMAL_OPTION', 7); + /**#@+ * The different review options are stored in the bits of $quiz->review * These constants help to extract the options @@ -241,7 +270,7 @@ function quiz_user_complete($course, $user, $mod, $quiz) { if ($attempt->timefinish == 0) { print_string('unfinished'); } else { - echo quiz_format_grade($quiz, $attempt->sumgrades).'/'.$quiz->sumgrades; + echo quiz_format_grade($quiz, $attempt->sumgrades) . '/' . quiz_format_grade($quiz, $quiz->sumgrades); } echo ' - '.userdate($attempt->timemodified).'
'; } @@ -330,6 +359,20 @@ function quiz_format_grade($quiz, $grade) { return format_float($grade, $quiz->decimalpoints); } +/** + * Round a grade to to the correct number of decimal places, and format it for display. + * + * @param object $quiz The quiz table row, only $quiz->decimalpoints is used. + * @param float $grade The grade to round. + */ +function quiz_format_question_grade($quiz, $grade) { + if ($quiz->questiondecimalpoints == -1) { + return format_float($grade, $quiz->decimalpoints); + } else { + return format_float($grade, $quiz->questiondecimalpoints); + } +} + /** * Update grades in central gradebook * @@ -733,22 +776,11 @@ function quiz_print_recent_mod_activity($activity, $courseid, $detail, $modnames function quiz_process_options(&$quiz) { $quiz->timemodified = time(); - // Quiz open time. - if (empty($quiz->timeopen)) { - $quiz->preventlate = 0; - } - // Quiz name. if (!empty($quiz->name)) { $quiz->name = trim($quiz->name); } - // Time limit. (Get rid of it if the checkbox was not ticked.) - if (empty($quiz->timelimitenable)) { - $quiz->timelimit = 0; - } - $quiz->timelimit = round($quiz->timelimit); - // Password field - different in form to stop browsers that remember passwords // getting confused. $quiz->password = $quiz->quizpassword; @@ -1233,4 +1265,3 @@ function quiz_get_extra_capabilities() { return $caps; } -?> diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index c618ea2b95..8608b42b94 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -1,4 +1,28 @@ dirroot . '/mod/quiz/lib.php'); require_once($CFG->dirroot . '/mod/quiz/accessrules.php'); -require_once($CFG->dirroot . '/question/editlib.php'); require_once($CFG->dirroot . '/mod/quiz/attemptlib.php'); +require_once($CFG->dirroot . '/question/editlib.php'); require_once($CFG->libdir . '/eventslib.php'); /// Constants /////////////////////////////////////////////////////////////////// @@ -222,6 +242,15 @@ function quiz_delete_previews($quiz, $userid = null) { } } +/** + * @param integer $quizid The quiz id. + * @return boolean whether this quiz has any (non-preview) attempts. + */ +function quiz_has_attempts($quizid) { + global $DB; + return $DB->record_exists('quiz_attempts', array('quiz' => $quizid, 'preview' => 0)); +} + /// Functions to do with quiz layout and pages //////////////////////////////// /** @@ -440,13 +469,16 @@ function quiz_update_sumgrades($quiz) { * grade for this quiz. * * @param float $rawgrade the unadjusted grade, fof example $attempt->sumgrades - * @param object $quiz the quiz object. Only the fields grade, sumgrades and decimalpoints are used. + * @param object $quiz the quiz object. Only the fields grade, sumgrades, decimalpoints and questiondecimalpoints are used. + * @param mixed $round false = don't round, true = round using quiz_format_grade, 'question' = round using quiz_format_question_grade. * @return float the rescaled grade. */ function quiz_rescale_grade($rawgrade, $quiz, $round = true) { if ($quiz->sumgrades != 0) { $grade = $rawgrade * $quiz->grade / $quiz->sumgrades; - if ($round) { + if ($round === 'question') { // === really necessary here true == 'question' is true in PHP! + $grade = quiz_format_question_grade($quiz, $grade); + } else if ($round) { $grade = quiz_format_grade($quiz, $grade); } } else { @@ -582,7 +614,7 @@ function quiz_save_best_grade($quiz, $userid = null, $attempts = array()) { // Calculate the best grade $bestgrade = quiz_calculate_best_grade($quiz, $attempts); - $bestgrade = quiz_rescale_grade($bestgrade, $quiz); + $bestgrade = quiz_rescale_grade($bestgrade, $quiz, false); // Save the best grade in the database if ($grade = $DB->get_record('quiz_grades', array('quiz' => $quiz->id, 'userid' => $userid))) { @@ -1249,4 +1281,3 @@ function quiz_error($quiz, $errorcode, $a = null) { } print_error($errorcode, 'quiz', $CFG->wwwroot . '/mod/quiz/view.php?q=' . $quiz, $a); } -?> diff --git a/mod/quiz/mod_form.php b/mod/quiz/mod_form.php index 05dce8451f..1459565297 100644 --- a/mod/quiz/mod_form.php +++ b/mod/quiz/mod_form.php @@ -1,8 +1,37 @@ dirroot.'/course/moodleform_mod.php'); - -require_once("$CFG->dirroot/mod/quiz/locallib.php"); +/////////////////////////////////////////////////////////////////////////// +// // +// NOTICE OF COPYRIGHT // +// // +// Moodle - Modular Object-Oriented Dynamic Learning Environment // +// http://moodle.org // +// // +// Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com // +// // +// This program 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 2 of the License, or // +// (at your option) any later version. // +// // +// This program 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: // +// // +// http://www.gnu.org/copyleft/gpl.html // +// // +/////////////////////////////////////////////////////////////////////////// + +require_once($CFG->dirroot . '/course/moodleform_mod.php'); +require_once($CFG->dirroot . '/mod/quiz/locallib.php'); + +/** + * Settings form for the quiz module. + * + * @license http://www.gnu.org/copyleft/gpl.html GNU Public License + * @package quiz + */ class mod_quiz_mod_form extends moodleform_mod { var $_feedbacks; @@ -15,6 +44,7 @@ class mod_quiz_mod_form extends moodleform_mod { //------------------------------------------------------------------------------- $mform->addElement('header', 'general', get_string('general', 'form')); + /// Name. $mform->addElement('text', 'name', get_string('name'), array('size'=>'64')); if (!empty($CFG->formatstringstriptags)) { $mform->setType('name', PARAM_TEXT); @@ -23,64 +53,57 @@ class mod_quiz_mod_form extends moodleform_mod { } $mform->addRule('name', null, 'required', null, 'client'); - $mform->addElement('htmleditor', 'intro', get_string("introduction", "quiz")); + /// Introduction. + $mform->addElement('htmleditor', 'intro', get_string('introduction', 'quiz')); $mform->setType('intro', PARAM_RAW); $mform->setHelpButton('intro', array('richtext2', get_string('helprichtext'))); -//------------------------------------------------------------------------------- - $mform->addElement('header', 'timinghdr', get_string('timing', 'form')); - $mform->addElement('date_time_selector', 'timeopen', get_string('quizopen', 'quiz'), array('optional'=>true)); + /// Open and close dates. + $mform->addElement('date_time_selector', 'timeopen', get_string('quizopen', 'quiz'), array('optional' => true)); $mform->setHelpButton('timeopen', array('timeopen', get_string('quizopen', 'quiz'), 'quiz')); - $mform->addElement('date_time_selector', 'timeclose', get_string('quizclose', 'quiz'), array('optional'=>true)); + $mform->addElement('date_time_selector', 'timeclose', get_string('quizclose', 'quiz'), array('optional' => true)); $mform->setHelpButton('timeclose', array('timeopen', get_string('quizclose', 'quiz'), 'quiz')); - - $timelimitgrp=array(); - $timelimitgrp[] = &$mform->createElement('text', 'timelimit'); - $timelimitgrp[] = &$mform->createElement('checkbox', 'timelimitenable', '', get_string('enable')); - $mform->addGroup($timelimitgrp, 'timelimitgrp', get_string('timelimitmin', 'quiz'), array(' '), false); - $mform->setType('timelimit', PARAM_TEXT); - $timelimitgrprules = array(); - $timelimitgrprules['timelimit'][] = array(null, 'numeric', null, 'client'); - $mform->addGroupRule('timelimitgrp', $timelimitgrprules); - $mform->disabledIf('timelimitgrp', 'timelimitenable'); - $mform->setAdvanced('timelimitgrp', $quizconfig->fix_timelimit); - $mform->setHelpButton('timelimitgrp', array("timelimit", get_string("quiztimer","quiz"), "quiz")); + /// Time limit. + $mform->addElement('duration', 'timelimit', get_string('timelimit', 'quiz'), array('optional' => true)); + $mform->setHelpButton('timelimit', array('timelimit', get_string('quiztimer','quiz'), 'quiz')); + $mform->setAdvanced('timelimit', $quizconfig->fix_timelimit); $mform->setDefault('timelimit', $quizconfig->timelimit); - $mform->setDefault('timelimitenable', !empty($quizconfig->timelimit)); - - //enforced time delay between quiz attempts add-on - $timedelayoptions = array(); - $timedelayoptions[0] = get_string('none'); - $timedelayoptions[1800] = get_string('numminutes', '', 30); - $timedelayoptions[3600] = get_string('numminutes', '', 60); - for($i=2; $i<=23; $i++) { - $seconds = $i*3600; - $timedelayoptions[$seconds] = get_string('numhours', '', $i); - } - $timedelayoptions[86400] = get_string('numhours', '', 24); - for($i=2; $i<=7; $i++) { - $seconds = $i*86400; - $timedelayoptions[$seconds] = get_string('numdays', '', $i); + /// Number of attempts. + $attemptoptions = array('0' => get_string('unlimited')); + for ($i = 1; $i <= QUIZ_MAX_ATTEMPT_OPTION; $i++) { + $attemptoptions[$i] = $i; } - $mform->addElement('select', 'delay1', get_string("delay1", "quiz"), $timedelayoptions); - $mform->setHelpButton('delay1', array("timedelay1", get_string("delay1", "quiz"), "quiz")); - $mform->setAdvanced('delay1', $quizconfig->fix_delay1); - $mform->setDefault('delay1', $quizconfig->delay1); + $mform->addElement('select', 'attempts', get_string('attemptsallowed', 'quiz'), $attemptoptions); + $mform->setHelpButton('attempts', array('attempts', get_string('attemptsallowed','quiz'), 'quiz')); + $mform->setAdvanced('attempts', $quizconfig->fix_attempts); + $mform->setDefault('attempts', $quizconfig->attempts); - $mform->addElement('select', 'delay2', get_string("delay2", "quiz"), $timedelayoptions); - $mform->setHelpButton('delay2', array("timedelay2", get_string("delay2", "quiz"), "quiz")); - $mform->setAdvanced('delay2', $quizconfig->fix_delay2); - $mform->setDefault('delay2', $quizconfig->delay2); + /// Grading method. + $mform->addElement('select', 'grademethod', get_string('grademethod', 'quiz'), quiz_get_grading_options()); + $mform->setHelpButton('grademethod', array('grademethod', get_string('grademethod','quiz'), 'quiz')); + $mform->setAdvanced('grademethod', $quizconfig->fix_grademethod); + $mform->setDefault('grademethod', $quizconfig->grademethod); + $mform->disabledIf('grademethod', 'attempts', 'eq', 1); + + $mform->addElement('hidden', 'grade', $quizconfig->maximumgrade); //------------------------------------------------------------------------------- - $mform->addElement('header', 'displayhdr', get_string('display', 'form')); + $mform->addElement('header', 'layouthdr', get_string('layout', 'quiz')); + + /// Shuffle questions. + $mform->addElement('selectyesno', 'shufflequestions', get_string('shufflequestions', 'quiz')); + $mform->setHelpButton('shufflequestions', array('shufflequestions', get_string('shufflequestions','quiz'), 'quiz')); + $mform->setAdvanced('shufflequestions', $quizconfig->fix_shufflequestions); + $mform->setDefault('shufflequestions', $quizconfig->shufflequestions); + + /// Questions per page. $perpage = array(); $perpage[0] = get_string('never'); $perpage[1] = get_string('aftereachquestion', 'quiz'); - for ($i = 2; $i <= 50; ++$i) { + for ($i = 2; $i <= QUIZ_MAX_QPP_OPTION; ++$i) { $perpage[$i] = get_string('afternquestions', 'quiz', $i); } $mform->addElement('select', 'questionsperpage', get_string('newpageevery', 'quiz'), $perpage); @@ -88,72 +111,41 @@ class mod_quiz_mod_form extends moodleform_mod { $mform->setAdvanced('questionsperpage', $quizconfig->fix_questionsperpage); $mform->setDefault('questionsperpage', $quizconfig->questionsperpage); - $mform->addElement('selectyesno', 'shufflequestions', get_string("shufflequestions", "quiz")); - $mform->setHelpButton('shufflequestions', array("shufflequestions", get_string("shufflequestions","quiz"), "quiz")); - $mform->setAdvanced('shufflequestions', $quizconfig->fix_shufflequestions); - $mform->setDefault('shufflequestions', $quizconfig->shufflequestions); +//------------------------------------------------------------------------------- + $mform->addElement('header', 'interactionhdr', get_string('questionbehaviour', 'quiz')); - $mform->addElement('selectyesno', 'shuffleanswers', get_string("shufflewithin", "quiz")); - $mform->setHelpButton('shuffleanswers', array("shufflewithin", get_string("shufflewithin","quiz"), "quiz")); + /// Shuffle within questions. + $mform->addElement('selectyesno', 'shuffleanswers', get_string('shufflewithin', 'quiz')); + $mform->setHelpButton('shuffleanswers', array('shufflewithin', get_string('shufflewithin','quiz'), 'quiz')); $mform->setAdvanced('shuffleanswers', $quizconfig->fix_shuffleanswers); $mform->setDefault('shuffleanswers', $quizconfig->shuffleanswers); - $mform->addElement('selectyesno', 'showuserpicture', get_string('showuserpicture', 'quiz')); - $mform->setHelpButton('showuserpicture', array('showuserpicture', get_string('showuserpicture', 'quiz'), 'quiz')); - $mform->setAdvanced('showuserpicture', $quizconfig->fix_showuserpicture); - $mform->setDefault('showuserpicture', $quizconfig->showuserpicture); - -//------------------------------------------------------------------------------- - $mform->addElement('header', 'attemptshdr', get_string('attempts', 'quiz')); - $attemptoptions = array('0' => get_string('unlimited')); - for ($i = 1; $i <= 10; $i++) { - $attemptoptions[$i] = $i; - } - $mform->addElement('select', 'attempts', get_string("attemptsallowed", "quiz"), $attemptoptions); - $mform->setHelpButton('attempts', array("attempts", get_string("attemptsallowed","quiz"), "quiz")); - $mform->setAdvanced('attempts', $quizconfig->fix_attempts); - $mform->setDefault('attempts', $quizconfig->attempts); - - $mform->addElement('selectyesno', 'attemptonlast', get_string("eachattemptbuildsonthelast", "quiz")); - $mform->setHelpButton('attemptonlast', array("repeatattempts", get_string("eachattemptbuildsonthelast", "quiz"), "quiz")); - $mform->setAdvanced('attemptonlast', $quizconfig->fix_attemptonlast); - $mform->setDefault('attemptonlast', $quizconfig->attemptonlast); - - $mform->addElement('selectyesno', 'adaptive', get_string("adaptive", "quiz")); - $mform->setHelpButton('adaptive', array("adaptive", get_string("adaptive","quiz"), "quiz")); + /// Adaptive mode. + $mform->addElement('selectyesno', 'adaptive', get_string('adaptive', 'quiz')); + $mform->setHelpButton('adaptive', array('adaptive', get_string('adaptive','quiz'), 'quiz')); $mform->setAdvanced('adaptive', $quizconfig->fix_optionflags); $mform->setDefault('adaptive', $quizconfig->optionflags & QUESTION_ADAPTIVE); - -//------------------------------------------------------------------------------- - $mform->addElement('header', 'gradeshdr', get_string('grades', 'grades')); - $mform->addElement('select', 'grademethod', get_string("grademethod", "quiz"), quiz_get_grading_options()); - $mform->setHelpButton('grademethod', array("grademethod", get_string("grademethod","quiz"), "quiz")); - $mform->setAdvanced('grademethod', $quizconfig->fix_grademethod); - $mform->setDefault('grademethod', $quizconfig->grademethod); - - $mform->addElement('selectyesno', 'penaltyscheme', get_string("penaltyscheme", "quiz")); - $mform->setHelpButton('penaltyscheme', array("penaltyscheme", get_string("penaltyscheme","quiz"), "quiz")); + /// Apply penalties. + $mform->addElement('selectyesno', 'penaltyscheme', get_string('penaltyscheme', 'quiz')); + $mform->setHelpButton('penaltyscheme', array('penaltyscheme', get_string('penaltyscheme','quiz'), 'quiz')); $mform->setAdvanced('penaltyscheme', $quizconfig->fix_penaltyscheme); $mform->setDefault('penaltyscheme', $quizconfig->penaltyscheme); + $mform->disabledIf('penaltyscheme', 'adaptive', 'neq', 1); - $options = array( - 0 => '0', - 1 => '1', - 2 => '2', - 3 => '3'); - $mform->addElement('select', 'decimalpoints', get_string("decimaldigits", "quiz"), $options); - $mform->setHelpButton('decimalpoints', array("decimalpoints", get_string("decimaldigits","quiz"), "quiz")); - $mform->setAdvanced('decimalpoints', $quizconfig->fix_decimalpoints); - $mform->setDefault('decimalpoints', $quizconfig->decimalpoints); - - $mform->addElement('hidden', 'grade', $quizconfig->maximumgrade); + /// Each attempt builds on last. + $mform->addElement('selectyesno', 'attemptonlast', get_string('eachattemptbuildsonthelast', 'quiz')); + $mform->setHelpButton('attemptonlast', array('repeatattempts', get_string('eachattemptbuildsonthelast', 'quiz'), 'quiz')); + $mform->setAdvanced('attemptonlast', $quizconfig->fix_attemptonlast); + $mform->setDefault('attemptonlast', $quizconfig->attemptonlast); + $mform->disabledIf('attemptonlast', 'attempts', 'eq', 1); //------------------------------------------------------------------------------- $mform->addElement('header', 'reviewoptionshdr', get_string('reviewoptionsheading', 'quiz')); $mform->setHelpButton('reviewoptionshdr', array('reviewoptions', get_string('reviewoptionsheading','quiz'), 'quiz')); $mform->setAdvanced('reviewoptionshdr', $quizconfig->fix_review); + /// Review options. $immediatelyoptionsgrp=array(); $immediatelyoptionsgrp[] = &$mform->createElement('checkbox', 'responsesimmediately', '', get_string('responses', 'quiz')); $immediatelyoptionsgrp[] = &$mform->createElement('checkbox', 'answersimmediately', '', get_string('answers', 'quiz')); @@ -161,7 +153,7 @@ class mod_quiz_mod_form extends moodleform_mod { $immediatelyoptionsgrp[] = &$mform->createElement('checkbox', 'generalfeedbackimmediately', '', get_string('generalfeedback', 'quiz')); $immediatelyoptionsgrp[] = &$mform->createElement('checkbox', 'scoreimmediately', '', get_string('scores', 'quiz')); $immediatelyoptionsgrp[] = &$mform->createElement('checkbox', 'overallfeedbackimmediately', '', get_string('overallfeedback', 'quiz')); - $mform->addGroup($immediatelyoptionsgrp, 'immediatelyoptionsgrp', get_string("reviewimmediately", "quiz"), null, false); + $mform->addGroup($immediatelyoptionsgrp, 'immediatelyoptionsgrp', get_string('reviewimmediately', 'quiz'), null, false); $mform->setDefault('responsesimmediately', $quizconfig->review & QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_IMMEDIATELY); $mform->setDefault('answersimmediately', $quizconfig->review & QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_IMMEDIATELY); $mform->setDefault('feedbackimmediately', $quizconfig->review & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_IMMEDIATELY); @@ -176,7 +168,7 @@ class mod_quiz_mod_form extends moodleform_mod { $openoptionsgrp[] = &$mform->createElement('checkbox', 'generalfeedbackopen', '', get_string('generalfeedback', 'quiz')); $openoptionsgrp[] = &$mform->createElement('checkbox', 'scoreopen', '', get_string('scores', 'quiz')); $openoptionsgrp[] = &$mform->createElement('checkbox', 'overallfeedbackopen', '', get_string('overallfeedback', 'quiz')); - $mform->addGroup($openoptionsgrp, 'openoptionsgrp', get_string("reviewopen", "quiz"), array(' '), false); + $mform->addGroup($openoptionsgrp, 'openoptionsgrp', get_string('reviewopen', 'quiz'), array(' '), false); $mform->setDefault('responsesopen', $quizconfig->review & QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_OPEN); $mform->setDefault('answersopen', $quizconfig->review & QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_OPEN); $mform->setDefault('feedbackopen', $quizconfig->review & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_OPEN); @@ -184,7 +176,6 @@ class mod_quiz_mod_form extends moodleform_mod { $mform->setDefault('scoreopen', $quizconfig->review & QUIZ_REVIEW_SCORES & QUIZ_REVIEW_OPEN); $mform->setDefault('overallfeedbackopen', $quizconfig->review & QUIZ_REVIEW_OVERALLFEEDBACK & QUIZ_REVIEW_OPEN); - $closedoptionsgrp=array(); $closedoptionsgrp[] = &$mform->createElement('checkbox', 'responsesclosed', '', get_string('responses', 'quiz')); $closedoptionsgrp[] = &$mform->createElement('checkbox', 'answersclosed', '', get_string('answers', 'quiz')); @@ -192,40 +183,81 @@ class mod_quiz_mod_form extends moodleform_mod { $closedoptionsgrp[] = &$mform->createElement('checkbox', 'generalfeedbackclosed', '', get_string('generalfeedback', 'quiz')); $closedoptionsgrp[] = &$mform->createElement('checkbox', 'scoreclosed', '', get_string('scores', 'quiz')); $closedoptionsgrp[] = &$mform->createElement('checkbox', 'overallfeedbackclosed', '', get_string('overallfeedback', 'quiz')); - $mform->addGroup($closedoptionsgrp, 'closedoptionsgrp', get_string("reviewclosed", "quiz"), array(' '), false); + $mform->addGroup($closedoptionsgrp, 'closedoptionsgrp', get_string('reviewclosed', 'quiz'), array(' '), false); $mform->setDefault('responsesclosed', $quizconfig->review & QUIZ_REVIEW_RESPONSES & QUIZ_REVIEW_CLOSED); $mform->setDefault('answersclosed', $quizconfig->review & QUIZ_REVIEW_ANSWERS & QUIZ_REVIEW_CLOSED); $mform->setDefault('feedbackclosed', $quizconfig->review & QUIZ_REVIEW_FEEDBACK & QUIZ_REVIEW_CLOSED); $mform->setDefault('generalfeedbackclosed', $quizconfig->review & QUIZ_REVIEW_GENERALFEEDBACK & QUIZ_REVIEW_CLOSED); $mform->setDefault('scoreclosed', $quizconfig->review & QUIZ_REVIEW_SCORES & QUIZ_REVIEW_CLOSED); $mform->setDefault('overallfeedbackclosed', $quizconfig->review & QUIZ_REVIEW_OVERALLFEEDBACK & QUIZ_REVIEW_CLOSED); + $mform->disabledIf('closedoptionsgrp', 'timeclose[enabled]'); //------------------------------------------------------------------------------- - $mform->addElement('header', 'security', get_string('security', 'form')); + $mform->addElement('header', 'display', get_string('display', 'form')); - $mform->addElement('selectyesno', 'popup', get_string("popup", "quiz")); - $mform->setHelpButton('popup', array("popup", get_string("popup", "quiz"), "quiz")); - $mform->setAdvanced('popup', $quizconfig->fix_popup); - $mform->setDefault('popup', $quizconfig->popup); + /// Show user picture. + $mform->addElement('selectyesno', 'showuserpicture', get_string('showuserpicture', 'quiz')); + $mform->setHelpButton('showuserpicture', array('showuserpicture', get_string('showuserpicture', 'quiz'), 'quiz')); + $mform->setAdvanced('showuserpicture', $quizconfig->fix_showuserpicture); + $mform->setDefault('showuserpicture', $quizconfig->showuserpicture); + + /// Overall decimal points. + $options = array(); + for ($i = 0; $i <= QUIZ_MAX_DECIMAL_OPTION; $i++) { + $options[$i] = $i; + } + $mform->addElement('select', 'decimalpoints', get_string('decimalplaces', 'quiz'), $options); + $mform->setHelpButton('decimalpoints', array('decimalpoints', get_string('decimalplaces','quiz'), 'quiz')); + $mform->setAdvanced('decimalpoints', $quizconfig->fix_decimalpoints); + $mform->setDefault('decimalpoints', $quizconfig->decimalpoints); - $mform->addElement('passwordunmask', 'quizpassword', get_string("requirepassword", "quiz")); + /// Question decimal points. + $options = array(-1 => get_string('sameasoverall', 'quiz')); + for ($i = 0; $i <= QUIZ_MAX_Q_DECIMAL_OPTION; $i++) { + $options[$i] = $i; + } + $mform->addElement('select', 'questiondecimalpoints', get_string('decimalplacesquestion', 'quiz'), $options); + $mform->setHelpButton('questiondecimalpoints', array('decimalplacesquestion', get_string('decimalplacesquestion','quiz'), 'quiz')); + $mform->setAdvanced('questiondecimalpoints', $quizconfig->fix_questiondecimalpoints); + $mform->setDefault('questiondecimalpoints', $quizconfig->questiondecimalpoints); + +//------------------------------------------------------------------------------- + $mform->addElement('header', 'security', get_string('extraattemptrestrictions', 'quiz')); + + /// Enforced time delay between quiz attempts. + $mform->addElement('passwordunmask', 'quizpassword', get_string('requirepassword', 'quiz')); $mform->setType('quizpassword', PARAM_TEXT); - $mform->setHelpButton('quizpassword', array("requirepassword", get_string("requirepassword", "quiz"), "quiz")); + $mform->setHelpButton('quizpassword', array('requirepassword', get_string('requirepassword', 'quiz'), 'quiz')); $mform->setAdvanced('quizpassword', $quizconfig->fix_password); $mform->setDefault('quizpassword', $quizconfig->password); - $mform->addElement('text', 'subnet', get_string("requiresubnet", "quiz")); + /// IP address. + $mform->addElement('text', 'subnet', get_string('requiresubnet', 'quiz')); $mform->setType('subnet', PARAM_TEXT); - $mform->setHelpButton('subnet', array("requiresubnet", get_string("requiresubnet", "quiz"), "quiz")); + $mform->setHelpButton('subnet', array('requiresubnet', get_string('requiresubnet', 'quiz'), 'quiz')); $mform->setAdvanced('subnet', $quizconfig->fix_subnet); $mform->setDefault('subnet', $quizconfig->subnet); -//------------------------------------------------------------------------------- - $features = new stdClass; - $features->groups = true; - $features->groupings = true; - $features->groupmembersonly = true; - $this->standard_coursemodule_elements($features); + /// Enforced time delay between quiz attempts. + $mform->addElement('duration', 'delay1', get_string('delay1st2nd', 'quiz'), array('optional' => true)); + $mform->setHelpButton('delay1', array('timedelay1', get_string('delay1st2nd', 'quiz'), 'quiz')); + $mform->setAdvanced('delay1', $quizconfig->fix_delay1); + $mform->setDefault('delay1', $quizconfig->delay1); + $mform->disabledIf('delay1', 'attempts', 'eq', 1); + + $mform->addElement('duration', 'delay2', get_string('delaylater', 'quiz'), array('optional' => true)); + $mform->setHelpButton('delay2', array('timedelay2', get_string('delaylater', 'quiz'), 'quiz')); + $mform->setAdvanced('delay2', $quizconfig->fix_delay2); + $mform->setDefault('delay2', $quizconfig->delay2); + $mform->disabledIf('delay2', 'attempts', 'eq', 1); + $mform->disabledIf('delay2', 'attempts', 'eq', 2); + + /// 'Secure' window. + $mform->addElement('selectyesno', 'popup', get_string('showinsecurepopup', 'quiz')); + $mform->setHelpButton('popup', array('popup', get_string('showinsecurepopup', 'quiz'), 'quiz')); + $mform->setAdvanced('popup', $quizconfig->fix_popup); + $mform->setDefault('popup', $quizconfig->popup); + //------------------------------------------------------------------------------- $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'quiz')); $mform->setHelpButton('overallfeedbackhdr', array('overallfeedback', get_string('overallfeedback', 'quiz'), 'quiz')); @@ -246,7 +278,7 @@ class mod_quiz_mod_form extends moodleform_mod { $mform->setType('feedbacktext', PARAM_RAW); $mform->setType('feedbackboundaries', PARAM_NOTAGS); - $nextel=$this->repeat_elements($repeatarray, $numfeedbacks-1, + $nextel=$this->repeat_elements($repeatarray, $numfeedbacks - 1, array(), 'boundary_repeats', 'boundary_add_fields', 3, get_string('addmoreoverallfeedbacks', 'quiz'), true); @@ -257,6 +289,13 @@ class mod_quiz_mod_form extends moodleform_mod { $insertEl = &MoodleQuickForm::createElement('static', 'gradeboundarystatic2', get_string('gradeboundary', 'quiz'), '0%'); $mform->insertElementBefore($insertEl, 'boundary_add_fields'); +//------------------------------------------------------------------------------- + $features = new stdClass; + $features->groups = true; + $features->groupings = true; + $features->groupmembersonly = true; + $this->standard_coursemodule_elements($features); + //------------------------------------------------------------------------------- // buttons $this->add_action_buttons(); diff --git a/mod/quiz/report/reportlib.php b/mod/quiz/report/reportlib.php index 54feb712ea..0a0e6b2b37 100644 --- a/mod/quiz/report/reportlib.php +++ b/mod/quiz/report/reportlib.php @@ -162,11 +162,11 @@ function quiz_format_average_grade_for_questions($avggradebyq, $questions, $quiz foreach(array_keys($questions) as $questionid) { if (isset($avggradebyq[$questionid])){ $grade = $avggradebyq[$questionid]; - $grade = quiz_rescale_grade($grade, $quiz); + $grade = quiz_rescale_grade($grade, $quiz, 'question'); } else { $grade = '--'; } - $row['qsgrade'.$questionid]= $grade; + $row['qsgrade'.$questionid] = $grade; } return $row; } diff --git a/mod/quiz/restorelib.php b/mod/quiz/restorelib.php index d84bedf35a..d4fdf7736c 100644 --- a/mod/quiz/restorelib.php +++ b/mod/quiz/restorelib.php @@ -83,7 +83,11 @@ $quiz->grade = backup_todb($info['MOD']['#']['GRADE']['0']['#']); $quiz->timecreated = backup_todb($info['MOD']['#']['TIMECREATED']['0']['#']); $quiz->timemodified = backup_todb($info['MOD']['#']['TIMEMODIFIED']['0']['#']); - $quiz->timelimit = backup_todb($info['MOD']['#']['TIMELIMIT']['0']['#']); + if (isset($info['MOD']['#']['TIMELIMITSECS']['0']['#'])) { + $quiz->timelimit = backup_todb($info['MOD']['#']['TIMELIMITSECS']['0']['#']); + } else { + $quiz->timelimit = backup_todb($info['MOD']['#']['TIMELIMIT']['0']['#']) * 60; + } $quiz->password = backup_todb($info['MOD']['#']['PASSWORD']['0']['#']); $quiz->subnet = backup_todb($info['MOD']['#']['SUBNET']['0']['#']); $quiz->popup = backup_todb($info['MOD']['#']['POPUP']['0']['#']); diff --git a/mod/quiz/restorelibpre15.php b/mod/quiz/restorelibpre15.php index 451fec6644..4fc900807e 100644 --- a/mod/quiz/restorelibpre15.php +++ b/mod/quiz/restorelibpre15.php @@ -1237,7 +1237,7 @@ $quiz->grade = backup_todb($info['MOD']['#']['GRADE']['0']['#']); $quiz->timecreated = backup_todb($info['MOD']['#']['TIMECREATED']['0']['#']); $quiz->timemodified = backup_todb($info['MOD']['#']['TIMEMODIFIED']['0']['#']); - $quiz->timelimit = backup_todb($info['MOD']['#']['TIMELIMIT']['0']['#']); + $quiz->timelimit = backup_todb($info['MOD']['#']['TIMELIMIT']['0']['#']) * 60; $quiz->password = backup_todb($info['MOD']['#']['PASSWORD']['0']['#']); $quiz->subnet = backup_todb($info['MOD']['#']['SUBNET']['0']['#']); $quiz->popup = backup_todb($info['MOD']['#']['POPUP']['0']['#']); diff --git a/mod/quiz/review.php b/mod/quiz/review.php index 4254bc7825..d6e61da5d3 100644 --- a/mod/quiz/review.php +++ b/mod/quiz/review.php @@ -116,13 +116,12 @@ /// Work out some time-related things. $attempt = $attemptobj->get_attempt(); $quiz = $attemptobj->get_quiz(); - $timelimit = $quiz->timelimit * 60; $overtime = 0; if ($attempt->timefinish) { if ($timetaken = ($attempt->timefinish - $attempt->timestart)) { - if($timelimit && $timetaken > ($timelimit + 60)) { - $overtime = $timetaken - $timelimit; + if($quiz->timelimit && $timetaken > ($quiz->timelimit + 60)) { + $overtime = $timetaken - $quiz->timelimit; $overtime = format_time($overtime); } $timetaken = format_time($timetaken); diff --git a/mod/quiz/settingstree.php b/mod/quiz/settingstree.php index 231def4692..9e1d272d88 100644 --- a/mod/quiz/settingstree.php +++ b/mod/quiz/settingstree.php @@ -39,119 +39,115 @@ $quizsettings = new admin_settingpage('modsettingquiz', $pagetitle, 'moodle/site // Introductory explanation that all the settings are defaults for the add quiz form. $quizsettings->add(new admin_setting_heading('quizintro', '', get_string('configintro', 'quiz'))); -// timelimit +// Time limit $quizsettings->add(new admin_setting_text_with_advanced('quiz/timelimit', - get_string('timelimit', 'quiz'), get_string('configtimelimit', 'quiz'), + get_string('timelimitsec', 'quiz'), get_string('configtimelimitsec', 'quiz'), array('value' => '0', 'fix' => false), PARAM_INT)); -// delay1 and delay2 -$timedelayoptions = array(); -$timedelayoptions[0] = get_string('none'); -$timedelayoptions[1800] = get_string('numminutes', '', 30); -$timedelayoptions[3600] = get_string('numminutes', '', 60); -for($i=2; $i<=23; $i++) { - $seconds = $i*3600; - $timedelayoptions[$seconds] = get_string('numhours', '', $i); -} -$timedelayoptions[86400] = get_string('numhours', '', 24); -for($i=2; $i<=7; $i++) { - $seconds = $i*86400; - $timedelayoptions[$seconds] = get_string('numdays', '', $i); +// Number of attempts +$options = array(get_string('unlimited')); +for ($i = 1; $i <= QUIZ_MAX_ATTEMPT_OPTION; $i++) { + $options[$i] = $i; } -$quizsettings->add(new admin_setting_combo_with_advanced('quiz/delay1', - get_string('delay1', 'quiz'), get_string('configdelay1', 'quiz'), - array('value' => 0, 'fix' => false), $timedelayoptions)); -$quizsettings->add(new admin_setting_combo_with_advanced('quiz/delay2', - get_string('delay2', 'quiz'), get_string('configdelay2', 'quiz'), - array('value' => 0, 'fix' => false), $timedelayoptions)); - -// questionsperpage +$quizsettings->add(new admin_setting_combo_with_advanced('quiz/attempts', + get_string('attemptsallowed', 'quiz'), get_string('configattemptsallowed', 'quiz'), + array('value' => 0, 'fix' => false), $options)); + +// Grading method. +$quizsettings->add(new admin_setting_combo_with_advanced('quiz/grademethod', + get_string('grademethod', 'quiz'), get_string('configgrademethod', 'quiz'), + array('value' => QUIZ_GRADEHIGHEST, 'fix' => false), quiz_get_grading_options())); + +// Maximum grade +$quizsettings->add(new admin_setting_configtext('quiz/maximumgrade', + get_string('maximumgrade'), get_string('configmaximumgrade', 'quiz'), 10, PARAM_INT)); + +// Shuffle questions +$quizsettings->add(new admin_setting_yesno_with_advanced('quiz/shufflequestions', + get_string('shufflequestions', 'quiz'), get_string('configshufflequestions', 'quiz'), + array('value' => 0, 'fix' => false))); + +// Questions per page $perpage = array(); $perpage[0] = get_string('never'); $perpage[1] = get_string('aftereachquestion', 'quiz'); -for ($i = 2; $i <= 50; ++$i) { +for ($i = 2; $i <= QUIZ_MAX_QPP_OPTION; ++$i) { $perpage[$i] = get_string('afternquestions', 'quiz', $i); } $quizsettings->add(new admin_setting_combo_with_advanced('quiz/questionsperpage', get_string('newpageevery', 'quiz'), get_string('confignewpageevery', 'quiz'), array('value' => 1, 'fix' => false), $perpage)); -// shufflequestions -$quizsettings->add(new admin_setting_yesno_with_advanced('quiz/shufflequestions', - get_string('shufflequestions', 'quiz'), get_string('configshufflequestions', 'quiz'), - array('value' => 0, 'fix' => false))); - -// shuffleanswers +// Shuffle within questions $quizsettings->add(new admin_setting_yesno_with_advanced('quiz/shuffleanswers', get_string('shufflewithin', 'quiz'), get_string('configshufflewithin', 'quiz'), array('value' => 1, 'fix' => false))); -// showuserpicture -$quizsettings->add(new admin_setting_yesno_with_advanced('quiz/showuserpicture', - get_string('showuserpicture', 'quiz'), get_string('configshowuserpicture', 'quiz'), - array('value' => 0, 'fix' => false))); - -// attempts -$options = array(get_string('unlimited')); -for ($i = 1; $i <= 6; $i++) { - $options[$i] = $i; -} -$quizsettings->add(new admin_setting_combo_with_advanced('quiz/attempts', - get_string('attemptsallowed', 'quiz'), get_string('configattemptsallowed', 'quiz'), - array('value' => 0, 'fix' => false), $options)); - -// attemptonlast -$quizsettings->add(new admin_setting_yesno_with_advanced('quiz/attemptonlast', - get_string('eachattemptbuildsonthelast', 'quiz'), get_string('configeachattemptbuildsonthelast', 'quiz'), - array('value' => 0, 'fix' => false))); - -// optionflags +// Adaptive mode. $quizsettings->add(new admin_setting_yesno_with_advanced('quiz/optionflags', get_string('adaptive', 'quiz'), get_string('configadaptive', 'quiz'), array('value' => 1, 'fix' => false))); -// maximumgrade -$quizsettings->add(new admin_setting_configtext('quiz/maximumgrade', - get_string('maximumgrade'), get_string('configmaximumgrade', 'quiz'), 10, PARAM_INT)); - -// grademethod -$quizsettings->add(new admin_setting_combo_with_advanced('quiz/grademethod', - get_string('grademethod', 'quiz'), get_string('configgrademethod', 'quiz'), - array('value' => QUIZ_GRADEHIGHEST, 'fix' => false), quiz_get_grading_options())); - -// penaltyscheme +// Apply penalties. $quizsettings->add(new admin_setting_yesno_with_advanced('quiz/penaltyscheme', get_string('penaltyscheme', 'quiz'), get_string('configpenaltyscheme', 'quiz'), - array('value' => 1, 'fix' => false))); + array('value' => 1, 'fix' => true))); + +// Each attempt builds on last. +$quizsettings->add(new admin_setting_yesno_with_advanced('quiz/attemptonlast', + get_string('eachattemptbuildsonthelast', 'quiz'), get_string('configeachattemptbuildsonthelast', 'quiz'), + array('value' => 0, 'fix' => true))); + +// Review options. +$quizsettings->add(new admin_setting_quiz_reviewoptions('quiz/review', + get_string('reviewoptions', 'quiz'), get_string('configreviewoptions', 'quiz'), + array('value' => QUIZ_REVIEW_IMMEDIATELY | QUIZ_REVIEW_OPEN | QUIZ_REVIEW_CLOSED, 'fix' => false))); + +// Show the user's picture +$quizsettings->add(new admin_setting_yesno_with_advanced('quiz/showuserpicture', + get_string('showuserpicture', 'quiz'), get_string('configshowuserpicture', 'quiz'), + array('value' => 0, 'fix' => false))); -// decimalpoints +// Decimal places for overall grades. $options = array(); -for ($i = 0; $i <= 5; $i++) { +for ($i = 0; $i <= QUIZ_MAX_DECIMAL_OPTION; $i++) { $options[$i] = $i; } $quizsettings->add(new admin_setting_combo_with_advanced('quiz/decimalpoints', - get_string('decimaldigits', 'quiz'), get_string('configdecimaldigits', 'quiz'), + get_string('decimalplaces', 'quiz'), get_string('configdecimalplaces', 'quiz'), array('value' => 2, 'fix' => false), $options)); -// review -$quizsettings->add(new admin_setting_quiz_reviewoptions('quiz/review', - get_string('reviewoptions', 'quiz'), get_string('configreviewoptions', 'quiz'), - array('value' => 0x3fffffff, 'fix' => false))); - -// popup -$quizsettings->add(new admin_setting_yesno_with_advanced('quiz/popup', - get_string('popup', 'quiz'), get_string('configpopup', 'quiz'), - array('value' => 0, 'fix' => false))); +// Decimal places for question grades. +$options = array(-1 => get_string('sameasoverall', 'quiz')); +for ($i = 0; $i <= QUIZ_MAX_Q_DECIMAL_OPTION; $i++) { + $options[$i] = $i; +} +$quizsettings->add(new admin_setting_combo_with_advanced('quiz/questiondecimalpoints', + get_string('decimalplacesquestion', 'quiz'), get_string('configdecimalplacesquestion', 'quiz'), + array('value' => -1, 'fix' => true), $options)); -// quizpassword +// Password. $quizsettings->add(new admin_setting_text_with_advanced('quiz/password', get_string('requirepassword', 'quiz'), get_string('configrequirepassword', 'quiz'), - array('value' => '', 'fix' => false), PARAM_TEXT)); + array('value' => '', 'fix' => true), PARAM_TEXT)); -// subnet +// IP restrictions. $quizsettings->add(new admin_setting_text_with_advanced('quiz/subnet', get_string('requiresubnet', 'quiz'), get_string('configrequiresubnet', 'quiz'), - array('value' => '', 'fix' => false), PARAM_TEXT)); + array('value' => '', 'fix' => true), PARAM_TEXT)); + +// Enforced delay between attempts. +$quizsettings->add(new admin_setting_text_with_advanced('quiz/delay1', + get_string('delay1st2nd', 'quiz'), get_string('configdelay1st2nd', 'quiz'), + array('value' => 0, 'fix' => true), PARAM_INTEGER)); +$quizsettings->add(new admin_setting_text_with_advanced('quiz/delay2', + get_string('delaylater', 'quiz'), get_string('configdelaylater', 'quiz'), + array('value' => 0, 'fix' => true), PARAM_INTEGER)); + +// 'Secure' window. +$quizsettings->add(new admin_setting_yesno_with_advanced('quiz/popup', + get_string('showinsecurepopup', 'quiz'), get_string('configpopup', 'quiz'), + array('value' => 0, 'fix' => true))); /// Now, depending on whether any reports have their own settings page, add /// the quiz setting page to the appropriate place in the tree. @@ -171,4 +167,3 @@ if (empty($reportsbyname)) { $ADMIN->add('modsettingsquizcat', $settings); } } -?> diff --git a/mod/quiz/simpletest/testaccessrules.php b/mod/quiz/simpletest/testaccessrules.php index 5e10cbcfef..3c6994ad1b 100644 --- a/mod/quiz/simpletest/testaccessrules.php +++ b/mod/quiz/simpletest/testaccessrules.php @@ -74,7 +74,7 @@ class simple_rules_test extends UnitTestCase { function test_time_limit_access_rule() { $quiz = new stdClass; - $quiz->timelimit = 60; + $quiz->timelimit = 3600; $quiz->questions = ''; $cm = new stdClass; $cm->id = 0; diff --git a/mod/quiz/simpletest/testlib.php b/mod/quiz/simpletest/testlib.php index f878f0123b..c125b8c93e 100644 --- a/mod/quiz/simpletest/testlib.php +++ b/mod/quiz/simpletest/testlib.php @@ -26,5 +26,32 @@ class quiz_lib_test extends UnitTestCase { $quiz->sumgrades = '100.0000'; $this->assertFalse(quiz_has_grades($quiz)); } + + function test_quiz_format_grade() { + $quiz = new stdClass; + $quiz->decimalpoints = 2; + $this->assertEqual(quiz_format_grade($quiz, 0.12345678), format_float(0.12, 2)); + $this->assertEqual(quiz_format_grade($quiz, 0), format_float(0, 2)); + $this->assertEqual(quiz_format_grade($quiz, 1.000000000000), format_float(1, 2)); + $quiz->decimalpoints = 0; + $this->assertEqual(quiz_format_grade($quiz, 0.12345678), '0'); + } + + function test_quiz_format_question_grade() { + $quiz = new stdClass; + $quiz->decimalpoints = 2; + $quiz->questiondecimalpoints = 2; + $this->assertEqual(quiz_format_question_grade($quiz, 0.12345678), format_float(0.12, 2)); + $this->assertEqual(quiz_format_question_grade($quiz, 0), format_float(0, 2)); + $this->assertEqual(quiz_format_question_grade($quiz, 1.000000000000), format_float(1, 2)); + $quiz->decimalpoints = 3; + $quiz->questiondecimalpoints = -1; + $this->assertEqual(quiz_format_question_grade($quiz, 0.12345678), format_float(0.123, 3)); + $this->assertEqual(quiz_format_question_grade($quiz, 0), format_float(0, 3)); + $this->assertEqual(quiz_format_question_grade($quiz, 1.000000000000), format_float(1, 3)); + $quiz->questiondecimalpoints = 4; + $this->assertEqual(quiz_format_question_grade($quiz, 0.12345678), format_float(0.1235, 4)); + $this->assertEqual(quiz_format_question_grade($quiz, 0), format_float(0, 4)); + $this->assertEqual(quiz_format_question_grade($quiz, 1.000000000000), format_float(1, 4)); + } } -?> \ No newline at end of file diff --git a/mod/quiz/simpletest/testlocallib.php b/mod/quiz/simpletest/testlocallib.php index 01d115f173..6bc35f9bba 100644 --- a/mod/quiz/simpletest/testlocallib.php +++ b/mod/quiz/simpletest/testlocallib.php @@ -71,5 +71,19 @@ class quiz_locallib_test extends UnitTestCase { $this->assertEqual(quiz_clean_layout('1,0,2', true), '1,0,2,0'); $this->assertEqual(quiz_clean_layout('0,1,0,0,2,0', true), '1,0,2,0'); } + + function test_quiz_rescale_grade() { + $quiz = new stdClass; + $quiz->decimalpoints = 2; + $quiz->questiondecimalpoints = 3; + $quiz->grade = 10; + $quiz->sumgrades = 10; + $this->assertEqual(quiz_rescale_grade(0.12345678, $quiz, false), 0.12345678); + $this->assertEqual(quiz_rescale_grade(0.12345678, $quiz, true), format_float(0.12, 2)); + $this->assertEqual(quiz_rescale_grade(0.12345678, $quiz, 'question'), format_float(0.123, 3)); + $quiz->sumgrades = 5; + $this->assertEqual(quiz_rescale_grade(0.12345678, $quiz, false), 0.24691356); + $this->assertEqual(quiz_rescale_grade(0.12345678, $quiz, true), format_float(0.25, 2)); + $this->assertEqual(quiz_rescale_grade(0.12345678, $quiz, 'question'), format_float(0.247, 3)); + } } -?> \ No newline at end of file diff --git a/mod/quiz/version.php b/mod/quiz/version.php index 5453ac40e4..8b8359c7a4 100644 --- a/mod/quiz/version.php +++ b/mod/quiz/version.php @@ -5,7 +5,7 @@ // This fragment is called by moodle_needs_upgrading() and /admin/index.php //////////////////////////////////////////////////////////////////////////////// -$module->version = 2009011400; // The (date) version of this module +$module->version = 2009031001; // The (date) version of this module $module->requires = 2008072401; // Requires this Moodle version $module->cron = 0; // How often should cron check this module (seconds)? diff --git a/mod/quiz/view.php b/mod/quiz/view.php index e0a87e1254..84f0e68afb 100644 --- a/mod/quiz/view.php +++ b/mod/quiz/view.php @@ -258,11 +258,11 @@ } // Ouside the if because we may be showing feedback but not grades. - $attemptgrade = quiz_rescale_grade($attempt->sumgrades, $quiz); + $attemptgrade = quiz_rescale_grade($attempt->sumgrades, $quiz, false); if ($gradecolumn) { if ($attemptoptions->scores && $attempt->timefinish > 0) { - $formattedgrade = $attemptgrade; + $formattedgrade = quiz_format_grade($quiz, $attemptgrade); // highlight the highest grade if appropriate if ($overallstats && !$attempt->preview && $numattempts > 1 && !is_null($mygrade) && $attemptgrade == $mygrade && $quiz->grademethod == QUIZ_GRADEHIGHEST) {