get_string("gradehighest", "quiz"),
GRADEAVERAGE => get_string("gradeaverage", "quiz"),
ATTEMPTFIRST => get_string("attemptfirst", "quiz"),
ATTEMPTLAST => get_string("attemptlast", "quiz"));
define("SHORTANSWER", "1");
define("TRUEFALSE", "2");
define("MULTICHOICE", "3");
define("RANDOM", "4");
define("MATCH", "5");
define("RANDOMSAMATCH", "6");
define("DESCRIPTION", "7");
define("NUMERICAL", "8");
define("MULTIANSWER", "9");
define("CALCULATED", "10");
// The $QUIZ_QUESTION_TYPE array holds the names of all the question types that the user should
// be able to create directly. Some internal question types like random questions are excluded.
// The complete list of question types can be found in $QUIZ_QTYPES.
$QUIZ_QUESTION_TYPE = array ( MULTICHOICE => get_string("multichoice", "quiz"),
TRUEFALSE => get_string("truefalse", "quiz"),
SHORTANSWER => get_string("shortanswer", "quiz"),
NUMERICAL => get_string("numerical", "quiz"),
CALCULATED => get_string("calculated", "quiz"),
MATCH => get_string("match", "quiz"),
DESCRIPTION => get_string("description", "quiz"),
RANDOMSAMATCH => get_string("randomsamatch", "quiz"),
MULTIANSWER => get_string("multianswer", "quiz")
);
define("QUIZ_PICTURE_MAX_HEIGHT", "600"); // Not currently implemented
define("QUIZ_PICTURE_MAX_WIDTH", "600"); // Not currently implemented
define("QUIZ_MAX_NUMBER_ANSWERS", "10");
define("QUIZ_CATEGORIES_SORTORDER", "999");
define('QUIZ_REVIEW_AFTER', 1);
define('QUIZ_REVIEW_BEFORE', 2);
$QUIZ_QTYPES= array();
/// QUIZ_QTYPES INITIATION //////////////////
class quiz_default_questiontype {
function name() {
return 'default';
}
function uses_quizfile($question, $relativefilepath) {
// The default does only check whether the file is used as image:
return $question->image == $relativefilepath;
}
function save_question_options($question) {
/// Given some question info and some data about the the answers
/// this function parses, organises and saves the question
/// It is used by question.php through ->save_question when
/// saving new data from a form, and also by import.php when
/// importing questions
///
/// If this is an update, and old answers already exist, then
/// these are overwritten using an update(). To do this, it
/// it is assumed that the IDs in quiz_answers are in the same
/// sort order as the new answers being saved. This should always
/// be true, but it's something to keep in mind if fiddling with
/// question.php
///
/// Returns $result->error or $result->noticeyesno or $result->notice
/// This default implementation must be overridden:
$result->error = "Unsupported question type ($question->qtype)!";
return $result;
}
function save_question($question, $form, $course) {
// This default implementation is suitable for most
// question types.
// First, save the basic question itself
$question->name = trim($form->name);
$question->questiontext = trim($form->questiontext);
$question->questiontextformat = $form->questiontextformat;
if (empty($form->image)) {
$question->image = "";
} else {
$question->image = $form->image;
}
if (empty($question->name)) {
$question->name = strip_tags($question->questiontext);
if (empty($question->name)) {
$question->name = '-';
}
}
if (isset($form->defaultgrade)) {
$question->defaultgrade = $form->defaultgrade;
}
if (!empty($question->id)) { // Question already exists
$question->version ++; // Update version number of question
if (!update_record("quiz_questions", $question)) {
error("Could not update question!");
}
} else { // Question is a new one
$question->stamp = make_unique_id_code(); // Set the unique code (not to be changed)
$question->version = 1;
if (!$question->id = insert_record("quiz_questions", $question)) {
error("Could not insert new question!");
}
}
// Now to save all the answers and type-specific options
$form->id = $question->id;
$form->qtype = $question->qtype;
$form->category = $question->category;
$result = $this->save_question_options($form);
if (!empty($result->error)) {
error($result->error);
}
if (!empty($result->notice)) {
notice($result->notice, "question.php?id=$question->id");
}
if (!empty($result->noticeyesno)) {
notice_yesno($result->noticeyesno, "question.php?id=$question->id", "edit.php");
print_footer($course);
exit;
}
return $question;
}
/// Convenience function that is used within the question types only
function extract_response_id($responsekey) {
if (ereg('[0-9]'.$this->name().'([0-9]+)', $responsekey, $regs)) {
return $regs[1];
} else {
return false;
}
}
function wrapped_questions($question) {
/// Overridden only by question types whose questions can
/// wrap other questions. Two question types that do this
/// are RANDOMSAMATCH and RANDOM
/// If there are wrapped questions, then this method returns
/// comma separated list of them...
return false;
}
function convert_to_response_answer_field($questionresponse) {
/// This function is very much the inverse of extract_response
/// This function and extract_response, should be
/// obsolete as soon as we get a better response storage
/// Right now they are a bridge between a consistent
/// response model and the old field answer in quiz_responses
/// This is the default implemention...
return implode(',', $questionresponse);
}
function get_answers($question) {
// Returns the answers for the specified question
// The default behaviour that signals that something is wrong
return false;
}
function create_response($question, $nameprefix, $questionsinuse) {
/// This rather smart solution works for most cases:
$rawresponse->question = $question->id;
$rawresponse->answer = '';
return $this->extract_response($rawresponse, $nameprefix);
}
function extract_response($rawresponse, $nameprefix) {
/// This function is very much the inverse of convert_to_response_answer_field
/// This function and convert_to_response_answer_field, should be
/// obsolete as soon as we get a better response storage
/// Right now they are a bridge between a consistent
/// response model and the old field answer in quiz_responses
/// Default behaviour that works for singlton response question types
/// like SHORTANSWER, NUMERICAL and TRUEFALSE
return array($nameprefix => $rawresponse->answer);
}
function print_question_number_and_grading_details
($number, $grade, $actualgrade=false, $recentlyadded=false, $questionid=0, $courseid=0) {
/// Print question number and grade:
global $CFG;
static $streditquestions, $strmarks, $strrecentlyaddedquestion;
if (!isset($streditquestions)) {
$streditquestions = get_string('editquestions', 'quiz');
$strmarks = get_string('marks', 'quiz');
$strrecentlyaddedquestion = get_string('recentlyaddedquestion', 'quiz');
}
echo '
';
if (false !== $actualgrade) {
echo "$strmarks: $actualgrade/$grade
";
} else {
echo "$grade $strmarks";
}
}
print_spacer(1,100);
/// Print possible recently-added information:
if ($recentlyadded) {
echo '
';
// Notify the user of this recently added question
echo ''.$strrecentlyaddedquestion.'';
echo '
';
} else { // The normal case
echo '
';
}
}
function print_question($currentnumber, $quiz, $question,
$readonly, $resultdetails) {
/// Note that this method must return the number of the next
/// question, making it possible not to increase the number when
/// overriding this method (as for qtype=DESCRIPTION).
echo '
";
return $currentnumber + 1;
}
function print_question_formulation_and_controls($question,
$quiz, $readonly, $answers, $correctanswers, $nameprefix) {
/// This default implementation must be overridden by all
/// question type implemenations, unless the default
/// implementation of print_question has been overridden...
notify('Error: Question formulation and input controls has not'
.' been implemented for question type '.$this->name());
}
function actual_number_of_questions($question) {
/// Used for the feature number-of-questions-per-page
/// to determine the actual number of questions wrapped
/// by this question. The default is ONE!
return 1;
}
function grade_response($question, $nameprefix) {
// Analyzes $question->response[] and determines the result
// The result is to be returned in this structure:
// ->grade (The fraction of maxgrade awarded on the question)
// ->answers (result answer records)
// ->correctanswers (potential answer records for best ->response[])
error('grade_response has not been implemented for question type '
.$this->name());
}
function get_config_options() {
// Returns an array of objects describing the options for the question type
// to be included on the quiz module admin page
//
// Configuration options can be included by setting the following fields in
// the object:
// ->name (The name of the option within this question type
// - the full option name will be constructed as
// "quiz_{$this->name()}_$name", the human readable name
// will be displayed with get_string($name, 'quiz'))
// ->code (The code to display the form element, help button, etc.
// i.e. the content for the central table cell. Be sure
// to name the element "quiz_{$this->name()}_$name" and
// set the value to $CFG->{"quiz_{$this->name()}_$name"})
// ->help (Name of the string from the quiz module language file
// to be used for the help message in the third column of
// the table. An empty string (or the field not set)
// means to leave the box empty)
//
// Links to custom settings pages can be included by setting the following
// fields in the object:
// ->name (The name of the link text string -
// get_string($name, 'quiz') will be called)
// ->link (The filename part of the URL for the link
// - the full URL is contructed as
// "$CFG->wwwroot/mod/quiz/questiontypes/{$this->name()}/$link?sesskey=$sesskey"
// [but with the relavant calls to the s and rawurlencode
// functions] where $sesskey is the sesskey for the user)
// No options by default
return false;
}
function print_replacement_options($question, $course, $quizid='0') {
// This function is used near the end of the question edit forms in all questiontypes
// It prints the table of quizzes in which the question is used
// containing the checkboxes to allow the teacher to replace the old question version
// no need to display replacement options if the question is new
if(empty($question->id)) {
return true;
}
// get quizzes using the question (using the question_grades table)
$quizlist = array();
if(!$grades = get_records('quiz_question_grades', 'question', $question->id)) {
$grades = array();
}
foreach($grades as $grade) {
$quizlist[$grade->quiz] = $grade->quiz;
}
$quizlist = implode(',', $quizlist);
if(empty($quizlist) or !$quizzes = get_records_list('quiz', 'id', $quizlist)) {
$quizzes = array();
}
// do the printing
if(count($quizzes) > 0) {
// print the table
$strquizname = get_string('modulename', 'quiz');
$strdoreplace = get_string('replace', 'quiz');
$straffectedstudents = get_string('affectedstudents', 'quiz', $course->students);
echo "
\n";
echo "
".get_string("replacementoptions", "quiz").":
\n";
echo "
\n";
echo "
\n";
echo "
\n";
echo "
$strquizname
\n";
echo "
$strdoreplace
\n";
echo "
$straffectedstudents
\n";
echo "
\n";
foreach($quizzes as $quiz) {
// work out whethere it should be checked by default
$checked = '';
if((int)$quizid === (int)$quiz->id
or empty($quiz->usercount)) {
$checked = "checked=\"checked\"";
}
// find how many different students have already attempted this quiz
$students = array();
if($attempts = get_records('quiz_attempts', 'quiz', $quiz->id)) {
foreach($attempts as $attempt) {
if (!isteacher($course->id, $attempt->userid) // Ignore teacher attempts
and record_exists('quiz_responses', 'attempt', $attempt->id, 'question', $question->id, 'originalquestion', 0)) {
$students[$attempt->userid] = 1;
}
}
}
$studentcount = count($students);
$strstudents = $studentcount === 1 ? $course->student : $course->students;
echo "
\n";
}
function print_question_form_end($question, $submitscript='') {
// This function is used at the end of the question edit forms in all questiontypes
// It prints the submit buttons and the standard hidden form fields
global $USER;
echo '
';
if ($question->id) {
echo ' ';
}
echo '
';
// The following hidden field indicates that the versioning code should be turned on, i.e.,
// that old versions should be kept if necessary
echo '
';
}
}
quiz_load_questiontypes();
function quiz_load_questiontypes() {
global $QUIZ_QTYPES;
global $CFG;
$qtypenames= get_list_of_plugins('mod/quiz/questiontypes');
foreach($qtypenames as $qtypename) {
// Instanciates all plug-in question types
$qtypefilepath= "$CFG->dirroot/mod/quiz/questiontypes/$qtypename/questiontype.php";
// echo "Loading $qtypename "; // Uncomment for debugging
if (is_readable($qtypefilepath)) {
require_once($qtypefilepath);
}
}
}
/// SQL FUNCTIONS ////////////////////////////////////////////////////////////////////
function quiz_move_questions($category1, $category2) {
global $CFG;
return execute_sql("UPDATE {$CFG->prefix}quiz_questions
SET category = '$category2'
WHERE category = '$category1'",
false);
}
function quiz_get_question_grades($quizid, $questionlist) {
global $CFG;
return get_records_sql("SELECT question,grade
FROM {$CFG->prefix}quiz_question_grades
WHERE quiz = '$quizid'
AND question IN ($questionlist)");
}
function quiz_questiongrades_update($grades, $quizid) {
// this is called from edit.php to store changes to the question grades
// in the quiz_question_grades table. It does not update 'sumgrades' in the quiz table.
$existing = get_records("quiz_question_grades", "quiz", $quizid, "", "question,grade,id");
foreach ($grades as $question => $grade) {
unset($questiongrade);
$questiongrade->quiz = $quizid;
$questiongrade->question = $question;
$questiongrade->grade = $grade;
if (isset($existing[$question])) {
if ($existing[$question]->grade != $grade) {
$questiongrade->id = $existing[$question]->id;
if (!update_record("quiz_question_grades", $questiongrade)) {
return false;
}
}
} else {
if (!insert_record("quiz_question_grades", $questiongrade)) {
return false;
}
}
}
}
function quiz_get_grade_records($quiz) {
/// Gets all info required to display the table of quiz results
/// for report.php
global $CFG;
return get_records_sql("SELECT qg.*, u.firstname, u.lastname, u.picture
FROM {$CFG->prefix}quiz_grades qg,
{$CFG->prefix}user u
WHERE qg.quiz = '$quiz->id'
AND qg.userid = u.id");
}
function quiz_get_answers($question) {
// Given a question, returns the correct answers for a given question
global $QUIZ_QTYPES;
return $QUIZ_QTYPES[$question->qtype]->get_answers($question);
}
function quiz_get_attempt_questions($quiz, $attempt, $attempting = false) {
/// Returns the questions of the quiz attempt in a format used for
/// grading and printing them...
///
/// $attempting should be set to true if this function is called in
/// order to create an attempt page and false if it is called to create
/// a review page.
///
/// On top of the ordinary persistent question fields,
/// this function also set these properties:
//
/// ->response - contains names (as keys) and values (as values)
/// for all question html-form inputs
/// ->recentlyadded - true only if the question has been added to the quiz
/// after the responses for the attempt were saved;
/// false otherwise
/// ->maxgrade - the max grade the question has on the quiz if grades
/// are used on the quiz; false otherwise
global $QUIZ_QTYPES;
global $CFG;
/////////////////////////
/// Get the questions:
/////////////////////////
if (!($questions =
get_records_list('quiz_questions', 'id', $quiz->questions))) {
notify('Error when reading questions from the database!');
return false;
}
////////////////////////////////////////////
/// Determine ->maxgrade for all questions
////////////////////////////////////////////
If (!($grades = quiz_get_question_grades($quiz->id, $quiz->questions))) {
$grades = array();
}
foreach ($questions as $qid => $question) {
if (isset($grades[$qid])) {
$questions[$qid]->maxgrade = $grades[$qid]->grade;
} else {
$questions[$qid]->maxgrade = 0.0;
}
}
//////////////////////////////////////////////////////////////
/// Determine attributes ->response and ->recentlyadded (hard)
//////////////////////////////////////////////////////////////
/// Get all existing responses on this attempt
$rawresponses = get_records_sql("
SELECT question, answer, attempt
FROM {$CFG->prefix}quiz_responses
WHERE attempt = '$attempt->id' ");
/// The setting for ->recentlyadded depends on whether this is
/// a test attempt or just a review
if ($attempting) {
/// This is a test attempt so there is a need to create responses
/// in case there are none existing.
/// Further - the attribute recentlyadded is determined from
/// whether the question has a response in the previous attempt,
/// which might be used in case the attemptonlast quiz option
/// is true.
$prevattempt = $attempt->attempt;
$prevresponses= array();
while (--$prevattempt) {
$prevresponses = get_records_sql("
SELECT r.question, r.answer, r.attempt, r.grade
FROM {$CFG->prefix}quiz_responses r, {$CFG->prefix}quiz_attempts a
WHERE a.quiz='$quiz->id' AND a.userid='$attempt->userid'
AND a.attempt='$prevattempt' AND r.attempt=a.id ");
if (!empty($prevresponses)) {
break;
}
}
$questionsinuse = $quiz->questions; // used if responses must be created
foreach ($questions as $qid => $question) {
if ($questions[$qid]->recentlyadded =
$prevattempt && empty($prevresponses[$qid])) {
/* No action */
} else if ($prevattempt && $quiz->attemptonlast
&& empty($rawresponses[$qid])) {
/// Store the previous response on this attempt!
$rawresponses[$qid] = $prevresponses[$qid];
$rawresponses[$qid]->attempt = $attempt->id;
$rawresponses[$qid]->id =
insert_record("quiz_responses", $rawresponses[$qid])
or error("Unable to create attemptonlast response for question $qid");
}
/* Extract possible response and its wrapped questions */
if (!empty($rawresponses[$qid])) {
$questions[$qid]->response = $QUIZ_QTYPES[$question->qtype]
->extract_response($rawresponses[$qid],
quiz_qtype_nameprefix($question));
/// Catch any additional wrapped questions:
if ($wrapped = $QUIZ_QTYPES[$question->qtype]
->wrapped_questions($questions[$question->id],
quiz_qtype_nameprefix($question))) {
$questionsinuse .= ",$wrapped";
}
}
}
/// Make sure all the questions will have responses:
foreach ($questions as $question) {
if (empty($question->response)) {
/// No response on this question
$nameprefix = quiz_qtype_nameprefix($question);
$questions[$question->id]->response =
$QUIZ_QTYPES[$question->qtype]->create_response
($question, $nameprefix, $questionsinuse);
//////////////////////////////////////////////
// Saving the newly created response before
// continuing with the quiz...
//////////////////////////////////////////////
$responserecord->attempt = $attempt->id;
$responserecord->question = $question->id;
$responserecord->answer = $QUIZ_QTYPES[$question->qtype]
->convert_to_response_answer_field
($questions[$question->id]->response);
insert_record("quiz_responses", $responserecord)
or error("Unable to create initial response for question $question->id");
/// Catch any additional wrapped questions:
if ($wrapped = $QUIZ_QTYPES[$question->qtype]
->wrapped_questions($questions[$question->id],
quiz_qtype_nameprefix($question))) {
$questionsinuse .= ",$wrapped";
}
}
}
} else {
/// In the case of review, the recentlyadded flag is set true
/// when the question has been added after the attempt and new
/// responses are never created
foreach ($questions as $qid => $question) {
if ($questions[$qid]->recentlyadded = empty($rawresponses[$qid])) {
/* No action */
} else {
$questions[$qid]->response = $QUIZ_QTYPES[$question->qtype]
->extract_response($rawresponses[$qid],
quiz_qtype_nameprefix($question));
}
}
}
return $questions;
}
function get_list_of_questions($questionlist) {
/// Returns an ordered list of questions, including course for each
global $CFG;
return get_records_sql("SELECT q.*,c.course
FROM {$CFG->prefix}quiz_questions q,
{$CFG->prefix}quiz_categories c
WHERE q.id in ($questionlist)
AND q.category = c.id");
}
//////////////////////////////////////////////////////////////////////////////////////
/// Any other quiz functions go here. Each of them must have a name that
/// starts with quiz_
function quiz_qtype_nameprefix($question, $prefixstart='question') {
global $QUIZ_QTYPES;
return $prefixstart.$question->id.$QUIZ_QTYPES[$question->qtype]->name();
}
function quiz_extract_posted_id($name, $nameprefix='question') {
if (ereg("^$nameprefix([0-9]+)", $name, $regs)) {
return $regs[1];
} else {
return false;
}
}
function quiz_print_comment($text) {
echo "".format_text($text, true, false)."";
}
function quiz_print_correctanswer($text) {
echo "
$text
";
}
function quiz_print_question_icon($question, $editlink=true) {
// Prints a question icon
global $QUIZ_QUESTION_TYPE;
global $QUIZ_QTYPES;
if ($editlink) {
echo "id\" title=\""
.$QUIZ_QTYPES[$question->qtype]->name()."\">";
}
echo '';
if ($editlink) {
echo "\n";
}
}
function quiz_print_possible_question_image($quizid, $question) {
// Includes the question image if there is one
global $CFG;
if ($quizid == '') {
$quizid = '0';
}
if ($question->image) {
echo 'wwwroot/mod/quiz/quizfile.php/$quizid/$question->id/$question->image";
} else {
echo "$CFG->wwwroot/mod/quiz/quizfile.php?file=/$quizid/$question->id/$question->image";
}
echo '" alt="" />';
}
}
function quiz_navigation_javascript($link) {
return "javascript:navigate($link);";
}
function quiz_print_navigation_panel($questions, $questionsperpage, $navigation) {
global $QUIZ_QTYPES;
$numberinglayout = array();
$nextqnumber = 1;
foreach ($questions as $question) {
if ($qnumberinc = $QUIZ_QTYPES[$question->qtype]
->actual_number_of_questions($question)) {
$numberinglayout[] = $nextqnumber;
$nextqnumber += $qnumberinc;
}
}
if ($nextqnumber - $qnumberinc <= $questionsperpage) {
/// The total number of questions does not exceed the maximum
/// number of allowed questions per page so...
return 0;
}
/// else - Navigation menu will be printed!
///////////////////////////////////////////////
/// Determine the layout of the navigation menu
///////////////////////////////////////////////
if (1 == $questionsperpage) {
/// The simple case:
$pagelinkagelayout = $pagenavigationlayout = $numberinglayout;
} else {
/// More complicated:
$pagenavigationlayout = array();
$pagelinkagelayout = array($currentpagestart = 1);
foreach ($numberinglayout as $questionnumber) {
if ($questionnumber - $currentpagestart >= $questionsperpage) {
$pagenavigationlayout[] = $currentpagestart
.'-'. ($questionnumber - 1);
if ($currentpagestart < $navigation
&& $navigation < $questionnumber) {
// $navigation is out of sync so adjust for robustness
$navigation = $currentpagestart;
}
$pagelinkagelayout[] = $currentpagestart = $questionnumber;
}
}
$pagenavigationlayout[] = $currentpagestart .'-'. ($nextqnumber - 1);
if ($currentpagestart < $navigation) {
// $firstquestion is out of sync so adjust it for robustness...
$navigation = $currentpagestart;
}
}
foreach ($pagelinkagelayout as $key => $link) {
if ($link < $navigation) {
$previouspagelink = $link;
} else if ($link == $navigation) {
$currentnavigationtitle = $pagenavigationlayout[$key];
} else {
$endpagelink = $link;
if (false == isset($nextpagelink)) {
$nextpagelink = $link;
}
}
}
///////////////////////////////////////////////
/// Print the navigation menu
///////////////////////////////////////////////
echo '
';
echo '';
}
function add_indented_names(&$categories, $id = 0, $indent = 0) {
// returns the categories with their names indented to show parent-child relationships
$fillstr = ' ';
$fill = str_repeat($fillstr, $indent);
$children = array();
$keys = array_keys($categories);
foreach ($keys as $key) {
if (!isset($categories[$key]->processed) && $categories[$key]->parent == $id) {
$children[$key] = $categories[$key];
$children[$key]->indentedname = $fill . $children[$key]->name;
$categories[$key]->processed = true;
$children = $children + add_indented_names($categories, $children[$key]->id, $indent + 1);
}
}
return $children;
}
function quiz_category_select_menu($courseid,$published=false,$only_editable=false,$selected="") {
/// displays a select menu of categories with appended coursenames
/// optionaly non editable categories may be excluded
/// added Howard Miller June '04
// get sql fragment for published
$publishsql="";
if ($published) {
$publishsql = "or publish=1";
}
if (!isadmin()) {
$categories = get_records_select("quiz_categories","course=$courseid $publishsql", 'parent, sortorder, name ASC');
} else {
$categories = get_records_select("quiz_categories", '', 'parent, sortorder, name ASC');
}
$categories = add_indented_names($categories);
echo "\n";
}
function quiz_get_category_coursename($category, $courseid = 0) {
/// if the category is not from this course and is published , adds on the course
/// name
$cname = (isset($category->indentedname)) ? $category->indentedname : $category->name;
if ($category->course != $courseid && $category->publish) {
if ($catcourse=get_record("course","id",$category->course)) {
$cname .= " ($catcourse->shortname) ";
}
}
return $cname;
}
function quiz_get_all_question_grades($questionlist, $quizid) {
// Given a list of question IDs, finds grades or invents them to
// create an array of matching grades
if (empty($questionlist)) {
return array();
}
$questions = quiz_get_question_grades($quizid, $questionlist);
$list = explode(",", $questionlist);
$grades = array();
foreach ($list as $qid) {
if (isset($questions[$qid])) {
$grades[$qid] = $questions[$qid]->grade;
} else {
$grades[$qid] = 1;
}
}
return $grades;
}
function quiz_gradesmenu_options($defaultgrade) {
// Especially for multianswer questions it is often
// desirable to have the grade of the question in a quiz
// larger than the earlier maximum of 10 points.
// This function makes quiz question list grade selector drop-down
// have the maximum grade option set to the highest value between 10
// and the defaultgrade of the question.
if ($defaultgrade && $defaultgrade>10) {
$maxgrade = $defaultgrade;
} else {
$maxgrade = 10;
}
unset($gradesmenu);
for ($i=$maxgrade ; $i>=0 ; --$i) {
$gradesmenu[$i] = $i;
}
return $gradesmenu;
}
function quiz_print_question_list($questionlist, $grades, $allowdelete=true, $quizid=0) {
// Prints a list of quiz questions in a small layout form with knobs
// returns sum of maximum grades
// $questionlist is comma-separated list
// $grades is an array of corresponding grades
global $USER;
if (!$questionlist) {
echo "