* 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.
$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.';
$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 <b>$a</b> from the list?';
$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';
$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?<br /><br />$a';
$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.';
$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. <small>(Automatic new page every $a questions.)</small>';
+$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';
$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: <br />$a->quiznames<br />The question will not be deleted from these quizzes but only from the category list.';
$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.';
$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';
$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';
$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';
// 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)) {
/**
* 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);
}
/**
/**
* 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;
*/
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;
}
}
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 '';
}
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));
<FIELD NAME="attempts" TYPE="int" LENGTH="6" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="The maximum number of attempts that a student is allowed at this quiz. 0 mean no limit." PREVIOUS="penaltyscheme" NEXT="attemptonlast"/>
<FIELD NAME="attemptonlast" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="Whether each attempt builds on last mode is on." PREVIOUS="attempts" NEXT="grademethod"/>
<FIELD NAME="grademethod" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="1" SEQUENCE="false" ENUM="false" COMMENT="How individual attempt grades are combined to get the overall grade. From the top of mod/quiz/lib.php: 1 = QUIZ_GRADEHIGHEST, 2 = QUIZ_GRADEAVERAGE, 3 = QUIZ_ATTEMPTFIRST, 4 = QUIZ_ATTEMPTLAST." PREVIOUS="attemptonlast" NEXT="decimalpoints"/>
- <FIELD NAME="decimalpoints" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="2" SEQUENCE="false" ENUM="false" COMMENT="Number of decimal points to display when printing scores belonging to this quiz." PREVIOUS="grademethod" NEXT="review"/>
- <FIELD NAME="review" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="A bitfield encoding the Review options setting. Read the code of function quiz_get_reviewoptions from mod/quiz/locallib.php to see what each bit means." PREVIOUS="decimalpoints" NEXT="questionsperpage"/>
+ <FIELD NAME="decimalpoints" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="2" SEQUENCE="false" ENUM="false" COMMENT="Number of decimal points to display when printing scores belonging to this quiz." PREVIOUS="grademethod" NEXT="questiondecimalpoints"/>
+ <FIELD NAME="questiondecimalpoints" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="-2" SEQUENCE="false" ENUM="false" COMMENT="The number of decimal digits to use when displaying question grades. -1 = use decimalpoints, otherwise a separate setting." PREVIOUS="decimalpoints" NEXT="review"/>
+ <FIELD NAME="review" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="A bitfield encoding the Review options setting. Read the code of function quiz_get_reviewoptions from mod/quiz/locallib.php to see what each bit means." PREVIOUS="questiondecimalpoints" NEXT="questionsperpage"/>
<FIELD NAME="questionsperpage" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="This does not do exactly what the name suggests, and the behaviour depends on the shufflequestions setting." PREVIOUS="review" NEXT="shufflequestions"/>
<FIELD NAME="shufflequestions" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="Whether the list of questions in the quiz should be randomly shuffled at the start of each attempt." PREVIOUS="questionsperpage" NEXT="shuffleanswers"/>
<FIELD NAME="shuffleanswers" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="Whether, in question types that support it, individual parts of a question should be shuffled." PREVIOUS="shufflequestions" NEXT="questions"/>
<FIELD NAME="grade" TYPE="number" LENGTH="10" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" ENUM="false" DECIMALS="5" COMMENT="Maximum grade that you can get from this quiz." PREVIOUS="sumgrades" NEXT="timecreated"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="Timestamp when this quiz was created." PREVIOUS="grade" NEXT="timemodified"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="Timestamp when this row was last updated." PREVIOUS="timecreated" NEXT="timelimit"/>
- <FIELD NAME="timelimit" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="Time limit in minutes. (This should really be changed to seconds for consistency.)" PREVIOUS="timemodified" NEXT="password"/>
+ <FIELD NAME="timelimit" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="Time limit in seconds." PREVIOUS="timemodified" NEXT="password"/>
<FIELD NAME="password" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" COMMENT="Students must enter this password before they can attempt the quiz." PREVIOUS="timelimit" NEXT="subnet"/>
<FIELD NAME="subnet" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" ENUM="false" COMMENT="If set, only allow attempts from certain IP addresses. The checking is performed by the address_in_subnet function from lib/moodlelib.php." PREVIOUS="password" NEXT="popup"/>
<FIELD NAME="popup" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" ENUM="false" COMMENT="Force the quiz to be attempted in a full-screen pop-up window with some evil JavaScript that attempts to prevent copying and pasting, etc." PREVIOUS="subnet" NEXT="delay1"/>
function xmldb_quiz_upgrade($oldversion) {
global $CFG, $DB;
-
+
$dbman = $DB->get_manager();
$result = true;
upgrade_mod_savepoint($result, 2008062001, 'quiz');
}
-
+
if ($result && $oldversion < 2008072402) {
/// Define field lastcron to be added to quiz_report
// 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
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
-
+
/// quiz savepoint reached
upgrade_mod_savepoint($result, 2008112101, 'quiz');
}
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;
}
-?>
<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// 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 //
+// //
+///////////////////////////////////////////////////////////////////////////
+
/**
-* Library of functions for the quiz module.
-*
-* This contains functions that are called also from outside the quiz module
-* Functions that are only called by the quiz module itself are in {@link locallib.php}
-* @author Martin Dougiamas and many others.
-* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
-* @package quiz
-*/
+ * Library of functions for the quiz module.
+ *
+ * This contains functions that are called also from outside the quiz module
+ * Functions that are only called by the quiz module itself are in {@link locallib.php}
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package quiz
+ */
-require_once($CFG->libdir.'/pagelib.php');
-require_once($CFG->libdir.'/eventslib.php');
+require_once($CFG->libdir . '/pagelib.php');
+require_once($CFG->libdir . '/eventslib.php');
/// CONSTANTS ///////////////////////////////////////////////////////////////////
* 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
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).'<br />';
}
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
*
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;
return $caps;
}
-?>
<?php // $Id$
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// 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 //
+// //
+///////////////////////////////////////////////////////////////////////////
+
/**
* Library of functions used by the quiz module.
*
* the module-indpendent code for handling questions and which in turn
* initialises all the questiontype classes.
*
- * @author Martin Dougiamas and many others. This has recently been completely
- * rewritten by Alex Smith, Julian Sedding and Gustav Delius as part of
- * the Serving Mathematics project
- * {@link http://maths.york.ac.uk/serving_maths}
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package quiz
*/
*/
require_once($CFG->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 ///////////////////////////////////////////////////////////////////
}
}
+/**
+ * @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 ////////////////////////////////
/**
* 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 {
// 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))) {
}
print_error($errorcode, 'quiz', $CFG->wwwroot . '/mod/quiz/view.php?q=' . $quiz, $a);
}
-?>
<?php // $Id$
-require_once ($CFG->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;
//-------------------------------------------------------------------------------
$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);
}
$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);
$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'));
$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);
$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);
$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'));
$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'));
$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);
$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();
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;
}
$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']['#']);
$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']['#']);
/// 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);
// 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.
$ADMIN->add('modsettingsquizcat', $settings);
}
}
-?>
function test_time_limit_access_rule() {
$quiz = new stdClass;
- $quiz->timelimit = 60;
+ $quiz->timelimit = 3600;
$quiz->questions = '';
$cm = new stdClass;
$cm->id = 0;
$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
$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
// 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)?
}
// 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) {