Merge branch 'MDL-43749' of git://github.com/timhunt/moodle
This commit is contained in:
@@ -4236,7 +4236,7 @@ abstract class restore_questions_activity_structure_step extends restore_activit
|
||||
|
||||
/**
|
||||
* Process the attempt data defined by {@link add_legacy_question_attempt_data()}.
|
||||
* @param object $data contains all the grouped attempt data ot process.
|
||||
* @param object $data contains all the grouped attempt data to process.
|
||||
* @param pbject $quiz data about the activity the attempts belong to. Required
|
||||
* fields are (basically this only works for the quiz module):
|
||||
* oldquestions => list of question ids in this activity - using old ids.
|
||||
@@ -4298,7 +4298,8 @@ abstract class restore_questions_activity_structure_step extends restore_activit
|
||||
$this->inform_new_usage_id($usage->id);
|
||||
|
||||
$data->uniqueid = $usage->id;
|
||||
$upgrader->save_usage($quiz->preferredbehaviour, $data, $qas, $quiz->questions);
|
||||
$upgrader->save_usage($quiz->preferredbehaviour, $data, $qas,
|
||||
$this->questions_recode_layout($quiz->oldquestions));
|
||||
}
|
||||
|
||||
protected function find_question_session_and_states($data, $questionid) {
|
||||
|
||||
+32
-24
@@ -718,51 +718,59 @@ function question_preview_popup_params() {
|
||||
* to pull in extra data. See, for example, the usage in mod/quiz/attemptlib.php, and
|
||||
* read the code below to see how the SQL is assembled. Throws exceptions on error.
|
||||
*
|
||||
* @global object
|
||||
* @global object
|
||||
* @param array $questionids array of question ids.
|
||||
* @param array $questionids array of question ids to load. If null, then all
|
||||
* questions matched by $join will be loaded.
|
||||
* @param string $extrafields extra SQL code to be added to the query.
|
||||
* @param string $join extra SQL code to be added to the query.
|
||||
* @param array $extraparams values for any placeholders in $join.
|
||||
* You are strongly recommended to use named placeholder.
|
||||
* You must use named placeholders.
|
||||
* @param string $orderby what to order the results by. Optional, default is unspecified order.
|
||||
*
|
||||
* @return array partially complete question objects. You need to call get_question_options
|
||||
* on them before they can be properly used.
|
||||
*/
|
||||
function question_preload_questions($questionids, $extrafields = '', $join = '',
|
||||
$extraparams = array()) {
|
||||
function question_preload_questions($questionids = null, $extrafields = '', $join = '',
|
||||
$extraparams = array(), $orderby = '') {
|
||||
global $DB;
|
||||
if (empty($questionids)) {
|
||||
return array();
|
||||
|
||||
if ($questionids === null) {
|
||||
$where = '';
|
||||
$params = array();
|
||||
} else {
|
||||
if (empty($questionids)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
list($questionidcondition, $params) = $DB->get_in_or_equal(
|
||||
$questionids, SQL_PARAMS_NAMED, 'qid0000');
|
||||
$where = 'WHERE q.id ' . $questionidcondition;
|
||||
}
|
||||
|
||||
if ($join) {
|
||||
$join = ' JOIN '.$join;
|
||||
$join = 'JOIN ' . $join;
|
||||
}
|
||||
|
||||
if ($extrafields) {
|
||||
$extrafields = ', ' . $extrafields;
|
||||
}
|
||||
list($questionidcondition, $params) = $DB->get_in_or_equal(
|
||||
$questionids, SQL_PARAMS_NAMED, 'qid0000');
|
||||
$sql = 'SELECT q.*, qc.contextid' . $extrafields . ' FROM {question} q
|
||||
JOIN {question_categories} qc ON q.category = qc.id' .
|
||||
$join .
|
||||
' WHERE q.id ' . $questionidcondition;
|
||||
|
||||
// Load the questions
|
||||
if (!$questions = $DB->get_records_sql($sql, $extraparams + $params)) {
|
||||
return array();
|
||||
if ($orderby) {
|
||||
$orderby = 'ORDER BY ' . $orderby;
|
||||
}
|
||||
|
||||
$sql = "SELECT q.*, qc.contextid{$extrafields}
|
||||
FROM {question} q
|
||||
JOIN {question_categories} qc ON q.category = qc.id
|
||||
{$join}
|
||||
{$where}
|
||||
{$orderby}";
|
||||
|
||||
// Load the questions.
|
||||
$questions = $DB->get_records_sql($sql, $extraparams + $params);
|
||||
foreach ($questions as $question) {
|
||||
$question->_partiallyloaded = true;
|
||||
}
|
||||
|
||||
// Note, a possible optimisation here would be to not load the TEXT fields
|
||||
// (that is, questiontext and generalfeedback) here, and instead load them in
|
||||
// question_load_questions. That would add one DB query, but reduce the amount
|
||||
// of data transferred most of the time. I am not going to do this optimisation
|
||||
// until it is shown to be worthwhile.
|
||||
|
||||
return $questions;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ class quizaccess_delaybetweenattempts_testcase extends basic_testcase {
|
||||
$quiz->delay1 = 1000;
|
||||
$quiz->delay2 = 0;
|
||||
$quiz->timeclose = 0;
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
@@ -79,7 +78,6 @@ class quizaccess_delaybetweenattempts_testcase extends basic_testcase {
|
||||
$quiz->delay1 = 0;
|
||||
$quiz->delay2 = 1000;
|
||||
$quiz->timeclose = 0;
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
@@ -119,7 +117,6 @@ class quizaccess_delaybetweenattempts_testcase extends basic_testcase {
|
||||
$quiz->delay1 = 2000;
|
||||
$quiz->delay2 = 1000;
|
||||
$quiz->timeclose = 0;
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
@@ -171,7 +168,6 @@ class quizaccess_delaybetweenattempts_testcase extends basic_testcase {
|
||||
$quiz->delay1 = 2000;
|
||||
$quiz->delay2 = 1000;
|
||||
$quiz->timeclose = 15000;
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
@@ -228,7 +224,6 @@ class quizaccess_delaybetweenattempts_testcase extends basic_testcase {
|
||||
$quiz->delay1 = 2000;
|
||||
$quiz->delay2 = 1000;
|
||||
$quiz->timeclose = 0;
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
|
||||
@@ -48,7 +48,6 @@ class quizaccess_ipaddress_testcase extends basic_testcase {
|
||||
// does not always work, for example using the mac install package on my laptop.
|
||||
$quiz->subnet = getremoteaddr(null);
|
||||
if (!empty($quiz->subnet)) {
|
||||
$quiz->questions = '';
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
$rule = new quizaccess_ipaddress($quizobj, 0);
|
||||
|
||||
@@ -61,7 +60,6 @@ class quizaccess_ipaddress_testcase extends basic_testcase {
|
||||
}
|
||||
|
||||
$quiz->subnet = '0.0.0.0';
|
||||
$quiz->questions = '';
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
$rule = new quizaccess_ipaddress($quizobj, 0);
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ class quizaccess_numattempts_testcase extends basic_testcase {
|
||||
public function test_num_attempts_access_rule() {
|
||||
$quiz = new stdClass();
|
||||
$quiz->attempts = 3;
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
|
||||
@@ -43,7 +43,6 @@ class quizaccess_openclosedate_testcase extends basic_testcase {
|
||||
$quiz->timeopen = 0;
|
||||
$quiz->timeclose = 0;
|
||||
$quiz->overduehandling = 'autosubmit';
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
@@ -73,7 +72,6 @@ class quizaccess_openclosedate_testcase extends basic_testcase {
|
||||
$quiz->timeopen = 10000;
|
||||
$quiz->timeclose = 0;
|
||||
$quiz->overduehandling = 'autosubmit';
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
@@ -105,7 +103,6 @@ class quizaccess_openclosedate_testcase extends basic_testcase {
|
||||
$quiz->timeopen = 0;
|
||||
$quiz->timeclose = 20000;
|
||||
$quiz->overduehandling = 'autosubmit';
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
@@ -144,7 +141,6 @@ class quizaccess_openclosedate_testcase extends basic_testcase {
|
||||
$quiz->timeopen = 10000;
|
||||
$quiz->timeclose = 20000;
|
||||
$quiz->overduehandling = 'autosubmit';
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
@@ -197,7 +193,6 @@ class quizaccess_openclosedate_testcase extends basic_testcase {
|
||||
$quiz->timeclose = 20000;
|
||||
$quiz->overduehandling = 'graceperiod';
|
||||
$quiz->graceperiod = 1000;
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
|
||||
@@ -41,7 +41,6 @@ class quizaccess_password_testcase extends basic_testcase {
|
||||
public function test_password_access_rule() {
|
||||
$quiz = new stdClass();
|
||||
$quiz->password = 'frog';
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
|
||||
@@ -42,7 +42,6 @@ class quizaccess_safebrowser_testcase extends basic_testcase {
|
||||
public function test_safebrowser_access_rule() {
|
||||
$quiz = new stdClass();
|
||||
$quiz->browsersecurity = 'safebrowser';
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
|
||||
@@ -43,7 +43,6 @@ class quizaccess_securewindow_testcase extends basic_testcase {
|
||||
public function test_securewindow_access_rule() {
|
||||
$quiz = new stdClass();
|
||||
$quiz->browsersecurity = 'securewindow';
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
|
||||
@@ -40,7 +40,6 @@ class quizaccess_timelimit_testcase extends basic_testcase {
|
||||
public function test_time_limit_access_rule() {
|
||||
$quiz = new stdClass();
|
||||
$quiz->timelimit = 3600;
|
||||
$quiz->questions = '';
|
||||
$cm = new stdClass();
|
||||
$cm->id = 0;
|
||||
$quizobj = new quiz($quiz, $cm, null);
|
||||
|
||||
+14
-18
@@ -65,7 +65,6 @@ class quiz {
|
||||
protected $cm;
|
||||
protected $quiz;
|
||||
protected $context;
|
||||
protected $questionids;
|
||||
|
||||
// Fields set later if that data is needed.
|
||||
protected $questions = null;
|
||||
@@ -89,12 +88,6 @@ class quiz {
|
||||
if ($getcontext && !empty($cm->id)) {
|
||||
$this->context = context_module::instance($cm->id);
|
||||
}
|
||||
$questionids = quiz_questions_in_quiz($this->quiz->questions);
|
||||
if ($questionids) {
|
||||
$this->questionids = explode(',', quiz_questions_in_quiz($this->quiz->questions));
|
||||
} else {
|
||||
$this->questionids = array(); // Which idiot made explode(',', '') = array('')?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,13 +125,10 @@ class quiz {
|
||||
* Load just basic information about all the questions in this quiz.
|
||||
*/
|
||||
public function preload_questions() {
|
||||
if (empty($this->questionids)) {
|
||||
throw new moodle_quiz_exception($this, 'noquestions', $this->edit_url());
|
||||
}
|
||||
$this->questions = question_preload_questions($this->questionids,
|
||||
'qqi.maxmark, qqi.id AS instance',
|
||||
'{quiz_question_instances} qqi ON qqi.quizid = :quizid AND q.id = qqi.questionid',
|
||||
array('quizid' => $this->quiz->id));
|
||||
$this->questions = question_preload_questions(null,
|
||||
'slot.maxmark, slot.id AS slotid, slot.slot, slot.page',
|
||||
'{quiz_slots} slot ON slot.quizid = :quizid AND q.id = slot.questionid',
|
||||
array('quizid' => $this->quiz->id), 'slot.slot');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,8 +138,11 @@ class quiz {
|
||||
* @param array $questionids question ids of the questions to load. null for all.
|
||||
*/
|
||||
public function load_questions($questionids = null) {
|
||||
if ($this->questions === null) {
|
||||
throw new coding_exception('You must call preload_questions before calling load_questions.');
|
||||
}
|
||||
if (is_null($questionids)) {
|
||||
$questionids = $this->questionids;
|
||||
$questionids = array_keys($this->questions);
|
||||
}
|
||||
$questionstoprocess = array();
|
||||
foreach ($questionids as $id) {
|
||||
@@ -226,7 +219,10 @@ class quiz {
|
||||
* @return whether any questions have been added to this quiz.
|
||||
*/
|
||||
public function has_questions() {
|
||||
return !empty($this->questionids);
|
||||
if ($this->questions === null) {
|
||||
$this->preload_questions();
|
||||
}
|
||||
return !empty($this->questions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,7 +238,7 @@ class quiz {
|
||||
*/
|
||||
public function get_questions($questionids = null) {
|
||||
if (is_null($questionids)) {
|
||||
$questionids = $this->questionids;
|
||||
$questionids = array_keys($this->questions);
|
||||
}
|
||||
$questions = array();
|
||||
foreach ($questionids as $id) {
|
||||
@@ -530,7 +526,7 @@ class quiz_attempt {
|
||||
$this->pagelayout = array();
|
||||
|
||||
// Break up the layout string into pages.
|
||||
$pagelayouts = explode(',0', quiz_clean_layout($this->attempt->layout, true));
|
||||
$pagelayouts = explode(',0', $this->attempt->layout);
|
||||
|
||||
// Strip off any empty last page (normally there is one).
|
||||
if (end($pagelayouts) == '') {
|
||||
|
||||
@@ -47,7 +47,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru
|
||||
'reviewspecificfeedback', 'reviewgeneralfeedback',
|
||||
'reviewrightanswer', 'reviewoverallfeedback',
|
||||
'questionsperpage', 'navmethod', 'shufflequestions', 'shuffleanswers',
|
||||
'questions', 'sumgrades', 'grade', 'timecreated',
|
||||
'sumgrades', 'grade', 'timecreated',
|
||||
'timemodified', 'password', 'subnet', 'browsersecurity',
|
||||
'delay1', 'delay2', 'showuserpicture', 'showblocks'));
|
||||
|
||||
@@ -57,7 +57,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru
|
||||
$qinstances = new backup_nested_element('question_instances');
|
||||
|
||||
$qinstance = new backup_nested_element('question_instance', array('id'), array(
|
||||
'questionid', 'maxmark'));
|
||||
'slot', 'page', 'questionid', 'maxmark'));
|
||||
|
||||
$feedbacks = new backup_nested_element('feedbacks');
|
||||
|
||||
@@ -107,7 +107,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru
|
||||
// Define sources.
|
||||
$quiz->set_source_table('quiz', array('id' => backup::VAR_ACTIVITYID));
|
||||
|
||||
$qinstance->set_source_table('quiz_question_instances',
|
||||
$qinstance->set_source_table('quiz_slots',
|
||||
array('quizid' => backup::VAR_PARENTID));
|
||||
|
||||
$feedback->set_source_table('quiz_feedback',
|
||||
|
||||
@@ -92,9 +92,10 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st
|
||||
$data->timecreated = $this->apply_date_offset($data->timecreated);
|
||||
$data->timemodified = $this->apply_date_offset($data->timemodified);
|
||||
|
||||
// Needed by {@link process_quiz_attempt_legacy}.
|
||||
$this->oldquizlayout = $data->questions;
|
||||
$data->questions = $this->questions_recode_layout($data->questions);
|
||||
if (property_exists($data, 'questions')) {
|
||||
// Needed by {@link process_quiz_attempt_legacy}, in which case it will be present.
|
||||
$this->oldquizlayout = $data->questions;
|
||||
}
|
||||
|
||||
// The setting quiz->attempts can come both in data->attempts and
|
||||
// data->attempts_number, handle both. MDL-26229.
|
||||
@@ -243,10 +244,36 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st
|
||||
$data->maxmark = $data->grade;
|
||||
}
|
||||
|
||||
if (!property_exists($data, 'slot')) {
|
||||
$page = 1;
|
||||
$slot = 1;
|
||||
foreach (explode(',', $this->oldquizlayout) as $item) {
|
||||
if ($item == 0) {
|
||||
$page += 1;
|
||||
continue;
|
||||
}
|
||||
if ($item == $data->questionid) {
|
||||
$data->slot = $slot;
|
||||
$data->page = $page;
|
||||
break;
|
||||
}
|
||||
$slot += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!property_exists($data, 'slot')) {
|
||||
// There was a question_instance in the backup file for a question
|
||||
// that was not acutally in the quiz. Drop it.
|
||||
$this->log('question ' . $data->questionid . ' was associated with quiz ' .
|
||||
$quiz->id . ' but not actually used. ' .
|
||||
'The instance has been ignored.', backup::LOG_INFO);
|
||||
return;
|
||||
}
|
||||
|
||||
$data->quizid = $this->get_new_parentid('quiz');
|
||||
$data->questionid = $this->get_mappingid('question', $data->questionid);
|
||||
|
||||
$DB->insert_record('quiz_question_instances', $data);
|
||||
$DB->insert_record('quiz_slots', $data);
|
||||
}
|
||||
|
||||
protected function process_quiz_feedback($data) {
|
||||
|
||||
+52
-48
@@ -33,7 +33,6 @@
|
||||
<FIELD NAME="navmethod" TYPE="char" LENGTH="16" NOTNULL="true" DEFAULT="free" SEQUENCE="false" COMMENT="Any constraints on how the user is allowed to navigate around the quiz. Currently recognised values are 'free' and 'seq'."/>
|
||||
<FIELD NAME="shufflequestions" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether the question order should be shuffled for each attempt."/>
|
||||
<FIELD NAME="shuffleanswers" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether the parts of the question should be shuffled, in those question types that support it."/>
|
||||
<FIELD NAME="questions" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="Comma-separated list of question ids, with 0s for page breaks. The quiz layout. See also the quiz_question_instances table."/>
|
||||
<FIELD NAME="sumgrades" TYPE="number" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The total of all the question instance maxmarks."/>
|
||||
<FIELD NAME="grade" TYPE="number" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The total that the quiz overall grade is scaled to be out of."/>
|
||||
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The time when the quiz was added to the course."/>
|
||||
@@ -53,6 +52,57 @@
|
||||
<INDEX NAME="course" UNIQUE="false" FIELDS="course"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
<TABLE NAME="quiz_slots" COMMENT="Stores the question used in a quiz, with the order, and for each question, which page it appears on, and the maximum mark (weight).">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="slot" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Where this question comes in order in the list of questions in this quiz. Like question_attempts.slot."/>
|
||||
<FIELD NAME="quizid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references quiz.id."/>
|
||||
<FIELD NAME="page" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The page number that this questions appears on. If the question in slot n appears on page p, then the question in slot n+1 must appear on page p or p+1. Well, except that when a quiz is being created, there may be empty pages, which would cause the page number to jump here."/>
|
||||
<FIELD NAME="questionid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references question.id."/>
|
||||
<FIELD NAME="maxmark" TYPE="number" LENGTH="12" NOTNULL="true" DEFAULT="0" SEQUENCE="false" DECIMALS="7" COMMENT="How many marks this question contributes to quiz.sumgrades."/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="quizid" TYPE="foreign" FIELDS="quizid" REFTABLE="quiz" REFFIELDS="id"/>
|
||||
<KEY NAME="questionid" TYPE="foreign" FIELDS="questionid" REFTABLE="question" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
<INDEXES>
|
||||
<INDEX NAME="quizid-slot" UNIQUE="true" FIELDS="quizid, slot"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
<TABLE NAME="quiz_feedback" COMMENT="Feedback given to students based on which grade band their overall score lies.">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="quizid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references quiz.id."/>
|
||||
<FIELD NAME="feedbacktext" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The feedback to show for a attempt where mingrade <= attempt grade < maxgrade. See function quiz_feedback_for_grade in mod/quiz/locallib.php."/>
|
||||
<FIELD NAME="feedbacktextformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="mingrade" TYPE="number" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The lower limit of this grade band. Inclusive."/>
|
||||
<FIELD NAME="maxgrade" TYPE="number" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The upper limit of this grade band. Exclusive."/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="quizid" TYPE="foreign" FIELDS="quizid" REFTABLE="quiz" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="quiz_overrides" COMMENT="The overrides to quiz settings on a per-user and per-group basis.">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="quiz" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references quiz.id"/>
|
||||
<FIELD NAME="groupid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Foreign key references groups.id. Can be null if this is a per-user override."/>
|
||||
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Foreign key references user.id. Can be null if this is a per-group override."/>
|
||||
<FIELD NAME="timeopen" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time at which students may start attempting this quiz. Can be null, in which case the quiz default is used."/>
|
||||
<FIELD NAME="timeclose" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time by which students must have completed their attempt. Can be null, in which case the quiz default is used."/>
|
||||
<FIELD NAME="timelimit" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time limit in seconds. Can be null, in which case the quiz default is used."/>
|
||||
<FIELD NAME="attempts" TYPE="int" LENGTH="6" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="password" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Quiz password. Can be null, in which case the quiz default is used."/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="quiz" TYPE="foreign" FIELDS="quiz" REFTABLE="quiz" REFFIELDS="id"/>
|
||||
<KEY NAME="groupid" TYPE="foreign" FIELDS="groupid" REFTABLE="groups" REFFIELDS="id"/>
|
||||
<KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="quiz_attempts" COMMENT="Stores users attempts at quizzes.">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" COMMENT="Standard Moodle primary key."/>
|
||||
@@ -97,52 +147,6 @@
|
||||
<INDEX NAME="userid" UNIQUE="false" FIELDS="userid"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
<TABLE NAME="quiz_question_instances" COMMENT="Stores the maximum possible grade (weight) for each question used in a quiz.">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="quizid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references quiz.id."/>
|
||||
<FIELD NAME="questionid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references question.id."/>
|
||||
<FIELD NAME="maxmark" TYPE="number" LENGTH="12" NOTNULL="true" DEFAULT="0" SEQUENCE="false" DECIMALS="7" COMMENT="How many marks this question contributes to quiz.sumgrades."/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="quizid" TYPE="foreign" FIELDS="quizid" REFTABLE="quiz" REFFIELDS="id"/>
|
||||
<KEY NAME="questionid" TYPE="foreign" FIELDS="questionid" REFTABLE="question" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="quiz_feedback" COMMENT="Feedback given to students based on which grade band their overall score lies.">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="quizid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references quiz.id."/>
|
||||
<FIELD NAME="feedbacktext" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="The feedback to show for a attempt where mingrade <= attempt grade < maxgrade. See function quiz_feedback_for_grade in mod/quiz/locallib.php."/>
|
||||
<FIELD NAME="feedbacktextformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="mingrade" TYPE="number" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The lower limit of this grade band. Inclusive."/>
|
||||
<FIELD NAME="maxgrade" TYPE="number" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" DECIMALS="5" COMMENT="The upper limit of this grade band. Exclusive."/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="quizid" TYPE="foreign" FIELDS="quizid" REFTABLE="quiz" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="quiz_overrides" COMMENT="The overrides to quiz settings on a per-user and per-group basis.">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="quiz" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Foreign key references quiz.id"/>
|
||||
<FIELD NAME="groupid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Foreign key references groups.id. Can be null if this is a per-user override."/>
|
||||
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Foreign key references user.id. Can be null if this is a per-group override."/>
|
||||
<FIELD NAME="timeopen" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time at which students may start attempting this quiz. Can be null, in which case the quiz default is used."/>
|
||||
<FIELD NAME="timeclose" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time by which students must have completed their attempt. Can be null, in which case the quiz default is used."/>
|
||||
<FIELD NAME="timelimit" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Time limit in seconds. Can be null, in which case the quiz default is used."/>
|
||||
<FIELD NAME="attempts" TYPE="int" LENGTH="6" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="password" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Quiz password. Can be null, in which case the quiz default is used."/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="quiz" TYPE="foreign" FIELDS="quiz" REFTABLE="quiz" REFFIELDS="id"/>
|
||||
<KEY NAME="groupid" TYPE="foreign" FIELDS="groupid" REFTABLE="groups" REFFIELDS="id"/>
|
||||
<KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
</TABLE>
|
||||
<TABLE NAME="quiz_reports" COMMENT="Lists all the installed quiz reports and their display order and so on. No need to worry about deleting old records. Only records with an equivalent directory are displayed.">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
@@ -158,4 +162,4 @@
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
</TABLES>
|
||||
</XMLDB>
|
||||
</XMLDB>
|
||||
|
||||
@@ -515,6 +515,252 @@ function xmldb_quiz_upgrade($oldversion) {
|
||||
upgrade_mod_savepoint(true, 2014021300, 'quiz');
|
||||
}
|
||||
|
||||
if ($oldversion < 2014022000) {
|
||||
|
||||
// Define table quiz_question_instances to be renamed to quiz_slots.
|
||||
$table = new xmldb_table('quiz_question_instances');
|
||||
|
||||
// Launch rename table for quiz_question_instances.
|
||||
$dbman->rename_table($table, 'quiz_slots');
|
||||
|
||||
// Quiz savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2014022000, 'quiz');
|
||||
}
|
||||
|
||||
if ($oldversion < 2014022001) {
|
||||
|
||||
// Define field slot to be added to quiz_slots.
|
||||
$table = new xmldb_table('quiz_slots');
|
||||
$field = new xmldb_field('slot', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'id');
|
||||
|
||||
// Conditionally launch add field slot.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Quiz savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2014022001, 'quiz');
|
||||
}
|
||||
|
||||
if ($oldversion < 2014022002) {
|
||||
|
||||
// Define field page to be added to quiz_slots.
|
||||
$table = new xmldb_table('quiz_slots');
|
||||
$field = new xmldb_field('page', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'quizid');
|
||||
|
||||
// Conditionally launch add field page.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Quiz savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2014022002, 'quiz');
|
||||
}
|
||||
|
||||
if ($oldversion < 2014022003) {
|
||||
|
||||
// Use the information in the old quiz.questions column to fill in the
|
||||
// new slot and page columns.
|
||||
$numquizzes = $DB->count_records('quiz');
|
||||
if ($numquizzes > 0) {
|
||||
$pbar = new progress_bar('quizquestionstoslots', 500, true);
|
||||
$transaction = $DB->start_delegated_transaction();
|
||||
|
||||
$numberdone = 0;
|
||||
$quizzes = $DB->get_recordset('quiz', null, 'id', 'id,questions,sumgrades');
|
||||
foreach ($quizzes as $quiz) {
|
||||
if ($quiz->questions === '') {
|
||||
$questionsinorder = array();
|
||||
} else {
|
||||
$questionsinorder = explode(',', $quiz->questions);
|
||||
}
|
||||
|
||||
$questionidtoslotrowid = $DB->get_records_menu('quiz_slots',
|
||||
array('quizid' => $quiz->id), '', 'questionid, id');
|
||||
|
||||
$problemfound = false;
|
||||
$currentpage = 1;
|
||||
$slot = 1;
|
||||
foreach ($questionsinorder as $questionid) {
|
||||
if ($questionid === '0') {
|
||||
// Page break.
|
||||
$currentpage++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (array_key_exists($questionid, $questionidtoslotrowid)) {
|
||||
// Normal case. quiz_slots entry is present.
|
||||
// Just need to add slot and page.
|
||||
$quizslot = new stdClass();
|
||||
$quizslot->id = $questionidtoslotrowid[$questionid];
|
||||
$quizslot->slot = $slot;
|
||||
$quizslot->page = $currentpage;
|
||||
$DB->update_record('quiz_slots', $quizslot);
|
||||
|
||||
unset($questionidtoslotrowid[$questionid]); // So we can do a sanity check later.
|
||||
$slot++;
|
||||
continue;
|
||||
|
||||
} else {
|
||||
// This should not happen. The question was listed in
|
||||
// quiz.questions, but there was not an entry for it in
|
||||
// quiz_slots (formerly quiz_question_instances).
|
||||
// Previously, if such question ids were found, then
|
||||
// starting an attempt at the quiz would throw an exception.
|
||||
// Here, we try to add the missing data.
|
||||
$problemfound = true;
|
||||
$defaultmark = $DB->get_field('question', 'defaultmark',
|
||||
array('id' => $questionid), IGNORE_MISSING);
|
||||
if ($defaultmark === false) {
|
||||
debugging('During upgrade, detected that question ' .
|
||||
$questionid . ' was listed as being part of quiz ' .
|
||||
$quiz->id . ' but this question no longer exists. Ignoring it.', DEBUG_NORMAL);
|
||||
|
||||
// Non-existent question. Ignore it completely.
|
||||
continue;
|
||||
}
|
||||
|
||||
debugging('During upgrade, detected that question ' .
|
||||
$questionid . ' was listed as being part of quiz ' .
|
||||
$quiz->id . ' but there was not entry for it in ' .
|
||||
'quiz_question_instances/quiz_slots. Creating an entry with default mark.', DEBUG_NORMAL);
|
||||
$quizslot = new stdClass();
|
||||
$quizslot->quizid = $quiz->id;
|
||||
$quizslot->slot = $slot;
|
||||
$quizslot->page = $currentpage;
|
||||
$quizslot->questionid = $questionid;
|
||||
$quizslot->maxmark = $defaultmark;
|
||||
$DB->insert_record('quiz_slots', $quizslot);
|
||||
|
||||
$slot++;
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Now, as a sanity check, ensure we have done all the
|
||||
// quiz_slots rows linked to this quiz.
|
||||
if (!empty($questionidtoslotrowid)) {
|
||||
debugging('During upgrade, detected that questions ' .
|
||||
implode(', ', array_keys($questionidtoslotrowid)) .
|
||||
' had instances in quiz ' . $quiz->id . ' but were not actually used. ' .
|
||||
'The instances have been removed.', DEBUG_NORMAL);
|
||||
|
||||
$DB->delete_records_list('quiz_slots', 'id', array_values($questionidtoslotrowid));
|
||||
$problemfound = true;
|
||||
}
|
||||
|
||||
// If there were problems found, we probably need to re-compute
|
||||
// quiz.sumgrades.
|
||||
if ($problemfound) {
|
||||
// C/f the quiz_update_sumgrades function in locallib.php,
|
||||
// but note that what we do here is a bit simpler.
|
||||
$newsumgrades = $DB->get_field_sql(
|
||||
"SELECT SUM(maxmark)
|
||||
FROM {quiz_slots}
|
||||
WHERE quizid = ?",
|
||||
array($quiz->id));
|
||||
if (abs($newsumgrades - $quiz->sumgrades) > 0.000005) {
|
||||
debugging('Because of the previously mentioned problems, ' .
|
||||
'sumgrades for quiz ' . $quiz->id .
|
||||
' was changed from ' . $quiz->sumgrades . ' to ' .
|
||||
$newsumgrades . ' You should probably check that this quiz is still working: ' .
|
||||
$CFG->wwwroot . '/mod/quiz/view.php?q=' . $quiz->id . '.', DEBUG_NORMAL);
|
||||
$DB->set_field('quiz', 'sumgrades', $newsumgrades, array('id' => $quiz->id));
|
||||
}
|
||||
}
|
||||
|
||||
// Done with this quiz. Update progress bar.
|
||||
$numberdone++;
|
||||
$pbar->update($numberdone, $numquizzes,
|
||||
"Upgrading quiz structure - {$numberdone}/{$numquizzes}.");
|
||||
}
|
||||
|
||||
$transaction->allow_commit();
|
||||
}
|
||||
|
||||
// Quiz savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2014022003, 'quiz');
|
||||
}
|
||||
|
||||
if ($oldversion < 2014022004) {
|
||||
|
||||
// If, for any reason, there were any quiz_slots missed, then try
|
||||
// to do something about that now before we add the NOT NULL constraints.
|
||||
// In fact, becuase of the sanity check at the end of the above check,
|
||||
// any such quiz_slots rows must refer to a non-existent quiz id, so
|
||||
// delete them.
|
||||
$DB->delete_records_select('quiz_slots',
|
||||
'NOT EXISTS (SELECT 1 FROM {quiz} q WHERE q.id = quizid)');
|
||||
|
||||
// Quiz savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2014022004, 'quiz');
|
||||
|
||||
// Now, if any quiz_slots rows are left with slot or page NULL, something
|
||||
// is badly wrong.
|
||||
if ($DB->record_exists_select('quiz_slots', 'slot IS NULL OR page IS NULL')) {
|
||||
throw new coding_exception('Something went wrong in the quiz upgrade step for MDL-43749. ' .
|
||||
'Some quiz_slots still contain NULLs which will break the NOT NULL constraints we need to add. ' .
|
||||
'Please report this problem at http://tracker.moodle.org/ so that it can be investigated. Thank you.');
|
||||
}
|
||||
}
|
||||
|
||||
if ($oldversion < 2014022005) {
|
||||
|
||||
// Changing nullability of field slot on table quiz_slots to not null.
|
||||
$table = new xmldb_table('quiz_slots');
|
||||
$field = new xmldb_field('slot', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id');
|
||||
|
||||
// Launch change of nullability for field slot.
|
||||
$dbman->change_field_notnull($table, $field);
|
||||
|
||||
// Quiz savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2014022005, 'quiz');
|
||||
}
|
||||
|
||||
if ($oldversion < 2014022006) {
|
||||
|
||||
// Changing nullability of field page on table quiz_slots to not null.
|
||||
$table = new xmldb_table('quiz_slots');
|
||||
$field = new xmldb_field('page', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'quizid');
|
||||
|
||||
// Launch change of nullability for field page.
|
||||
$dbman->change_field_notnull($table, $field);
|
||||
|
||||
// Quiz savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2014022006, 'quiz');
|
||||
}
|
||||
|
||||
if ($oldversion < 2014022007) {
|
||||
|
||||
// Define index quizid-slot (unique) to be added to quiz_slots.
|
||||
$table = new xmldb_table('quiz_slots');
|
||||
$index = new xmldb_index('quizid-slot', XMLDB_INDEX_UNIQUE, array('quizid', 'slot'));
|
||||
|
||||
// Conditionally launch add index quizid-slot.
|
||||
if (!$dbman->index_exists($table, $index)) {
|
||||
$dbman->add_index($table, $index);
|
||||
}
|
||||
|
||||
// Quiz savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2014022007, 'quiz');
|
||||
}
|
||||
|
||||
if ($oldversion < 2014022008) {
|
||||
|
||||
// Define field questions to be dropped from quiz.
|
||||
$table = new xmldb_table('quiz');
|
||||
$field = new xmldb_field('questions');
|
||||
|
||||
// Conditionally launch drop field questions.
|
||||
if ($dbman->field_exists($table, $field)) {
|
||||
$dbman->drop_field($table, $field);
|
||||
}
|
||||
|
||||
// Quiz savepoint reached.
|
||||
upgrade_mod_savepoint(true, 2014022008, 'quiz');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+99
-78
@@ -119,7 +119,6 @@ $scrollpos = optional_param('scrollpos', '', PARAM_INT);
|
||||
|
||||
list($thispageurl, $contexts, $cmid, $cm, $quiz, $pagevars) =
|
||||
question_edit_setup('editq', '/mod/quiz/edit.php', true);
|
||||
$quiz->questions = quiz_clean_layout($quiz->questions);
|
||||
|
||||
$defaultcategoryobj = question_make_default_categories($contexts->all());
|
||||
$defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
|
||||
@@ -161,23 +160,14 @@ add_to_log($cm->course, 'quiz', 'editquestions',
|
||||
// You need mod/quiz:manage in addition to question capabilities to access this page.
|
||||
require_capability('mod/quiz:manage', $contexts->lowest());
|
||||
|
||||
if (empty($quiz->grades)) {
|
||||
$quiz->grades = quiz_get_all_question_grades($quiz);
|
||||
}
|
||||
|
||||
// Process commands ============================================================
|
||||
if ($quiz->shufflequestions) {
|
||||
// Strip page breaks before processing actions, so that re-ordering works
|
||||
// as expected when shuffle questions is on.
|
||||
$quiz->questions = quiz_repaginate($quiz->questions, 0);
|
||||
}
|
||||
|
||||
// Get the list of question ids had their check-boxes ticked.
|
||||
$selectedquestionids = array();
|
||||
$selectedslots = array();
|
||||
$params = (array) data_submitted();
|
||||
foreach ($params as $key => $value) {
|
||||
if (preg_match('!^s([0-9]+)$!', $key, $matches)) {
|
||||
$selectedquestionids[] = $matches[1];
|
||||
$selectedslots[] = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,15 +176,13 @@ if ($scrollpos) {
|
||||
$afteractionurl->param('scrollpos', $scrollpos);
|
||||
}
|
||||
if (($up = optional_param('up', false, PARAM_INT)) && confirm_sesskey()) {
|
||||
$quiz->questions = quiz_move_question_up($quiz->questions, $up);
|
||||
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
|
||||
quiz_move_question_up($quiz, $up);
|
||||
quiz_delete_previews($quiz);
|
||||
redirect($afteractionurl);
|
||||
}
|
||||
|
||||
if (($down = optional_param('down', false, PARAM_INT)) && confirm_sesskey()) {
|
||||
$quiz->questions = quiz_move_question_down($quiz->questions, $down);
|
||||
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
|
||||
quiz_move_question_down($quiz, $down);
|
||||
quiz_delete_previews($quiz);
|
||||
redirect($afteractionurl);
|
||||
}
|
||||
@@ -202,8 +190,7 @@ if (($down = optional_param('down', false, PARAM_INT)) && confirm_sesskey()) {
|
||||
if (optional_param('repaginate', false, PARAM_BOOL) && confirm_sesskey()) {
|
||||
// Re-paginate the quiz.
|
||||
$questionsperpage = optional_param('questionsperpage', $quiz->questionsperpage, PARAM_INT);
|
||||
$quiz->questions = quiz_repaginate($quiz->questions, $questionsperpage );
|
||||
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
|
||||
quiz_repaginate_questions($quiz->id, $questionsperpage );
|
||||
quiz_delete_previews($quiz);
|
||||
redirect($afteractionurl);
|
||||
}
|
||||
@@ -248,51 +235,46 @@ if ((optional_param('addrandom', false, PARAM_BOOL)) && confirm_sesskey()) {
|
||||
}
|
||||
|
||||
if (optional_param('addnewpagesafterselected', null, PARAM_CLEAN) &&
|
||||
!empty($selectedquestionids) && confirm_sesskey()) {
|
||||
foreach ($selectedquestionids as $questionid) {
|
||||
$quiz->questions = quiz_add_page_break_after($quiz->questions, $questionid);
|
||||
!empty($selectedslots) && confirm_sesskey()) {
|
||||
foreach ($selectedslots as $slot) {
|
||||
quiz_add_page_break_after_slot($quiz, $slot);
|
||||
}
|
||||
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
|
||||
quiz_delete_previews($quiz);
|
||||
redirect($afteractionurl);
|
||||
}
|
||||
|
||||
$addpage = optional_param('addpage', false, PARAM_INT);
|
||||
if ($addpage !== false && confirm_sesskey()) {
|
||||
$quiz->questions = quiz_add_page_break_at($quiz->questions, $addpage);
|
||||
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
|
||||
quiz_add_page_break_after_slot($quiz, $addpage);
|
||||
quiz_delete_previews($quiz);
|
||||
redirect($afteractionurl);
|
||||
}
|
||||
|
||||
$deleteemptypage = optional_param('deleteemptypage', false, PARAM_INT);
|
||||
if (($deleteemptypage !== false) && confirm_sesskey()) {
|
||||
$quiz->questions = quiz_delete_empty_page($quiz->questions, $deleteemptypage);
|
||||
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
|
||||
quiz_delete_empty_page($quiz, $deleteemptypage);
|
||||
quiz_delete_previews($quiz);
|
||||
redirect($afteractionurl);
|
||||
}
|
||||
|
||||
$remove = optional_param('remove', false, PARAM_INT);
|
||||
if ($remove && confirm_sesskey()) {
|
||||
if ($remove && confirm_sesskey() && quiz_has_question_use($quiz, $remove)) {
|
||||
// Remove a question from the quiz.
|
||||
// We require the user to have the 'use' capability on the question,
|
||||
// so that then can add it back if they remove the wrong one by mistake,
|
||||
// but, if the question is missing, it can always be removed.
|
||||
if ($DB->record_exists('question', array('id' => $remove))) {
|
||||
quiz_require_question_use($remove);
|
||||
}
|
||||
quiz_remove_question($quiz, $remove);
|
||||
quiz_remove_slot($quiz, $remove);
|
||||
quiz_delete_previews($quiz);
|
||||
quiz_update_sumgrades($quiz);
|
||||
redirect($afteractionurl);
|
||||
}
|
||||
|
||||
if (optional_param('quizdeleteselected', false, PARAM_BOOL) &&
|
||||
!empty($selectedquestionids) && confirm_sesskey()) {
|
||||
foreach ($selectedquestionids as $questionid) {
|
||||
if (quiz_has_question_use($questionid)) {
|
||||
quiz_remove_question($quiz, $questionid);
|
||||
!empty($selectedslots) && confirm_sesskey()) {
|
||||
// Work backwards, since removing a question renumbers following slots.
|
||||
foreach (array_reverse($selectedslots) as $slot) {
|
||||
if (quiz_has_question_use($quiz, $slot)) {
|
||||
quiz_remove_slot($quiz, $slot);
|
||||
}
|
||||
}
|
||||
quiz_delete_previews($quiz);
|
||||
@@ -304,8 +286,6 @@ if (optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey()) {
|
||||
$deletepreviews = false;
|
||||
$recomputesummarks = false;
|
||||
|
||||
$oldquestions = explode(',', $quiz->questions); // The questions in the old order.
|
||||
$questions = array(); // For questions in the new order.
|
||||
$rawdata = (array) data_submitted();
|
||||
$moveonpagequestions = array();
|
||||
$moveselectedonpage = optional_param('moveselectedonpagetop', 0, PARAM_INT);
|
||||
@@ -313,73 +293,111 @@ if (optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey()) {
|
||||
$moveselectedonpage = optional_param('moveselectedonpagebottom', 0, PARAM_INT);
|
||||
}
|
||||
|
||||
$newslotorder = array();
|
||||
foreach ($rawdata as $key => $value) {
|
||||
if (preg_match('!^g([0-9]+)$!', $key, $matches)) {
|
||||
// Parse input for question -> grades.
|
||||
$questionid = $matches[1];
|
||||
$quiz->grades[$questionid] = unformat_float($value);
|
||||
quiz_update_question_instance($quiz->grades[$questionid], $questionid, $quiz);
|
||||
$slotnumber = $matches[1];
|
||||
$newgrade = unformat_float($value);
|
||||
quiz_update_slot_maxmark($DB->get_record('quiz_slots',
|
||||
array('quizid' => $quiz->id, 'slot' => $slotnumber), '*', MUST_EXIST), $newgrade);
|
||||
$deletepreviews = true;
|
||||
$recomputesummarks = true;
|
||||
|
||||
} else if (preg_match('!^o(pg)?([0-9]+)$!', $key, $matches)) {
|
||||
// Parse input for ordering info.
|
||||
$questionid = $matches[2];
|
||||
$slotnumber = $matches[2];
|
||||
// Make sure two questions don't overwrite each other. If we get a second
|
||||
// question with the same position, shift the second one along to the next gap.
|
||||
$value = clean_param($value, PARAM_INT);
|
||||
while (array_key_exists($value, $questions)) {
|
||||
while (array_key_exists($value, $newslotorder)) {
|
||||
$value++;
|
||||
}
|
||||
if ($matches[1]) {
|
||||
// This is a page-break entry.
|
||||
$questions[$value] = 0;
|
||||
$newslotorder[$value] = 0;
|
||||
} else {
|
||||
$questions[$value] = $questionid;
|
||||
$newslotorder[$value] = $slotnumber;
|
||||
}
|
||||
$deletepreviews = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If ordering info was given, reorder the questions.
|
||||
if ($questions) {
|
||||
ksort($questions);
|
||||
$questions[] = 0;
|
||||
$quiz->questions = implode(',', $questions);
|
||||
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
|
||||
$deletepreviews = true;
|
||||
}
|
||||
|
||||
// Get a list of questions to move, later to be added in the appropriate
|
||||
// place in the string.
|
||||
if ($moveselectedonpage) {
|
||||
$questions = explode(',', $quiz->questions);
|
||||
$newquestions = array();
|
||||
// Remove the questions from their original positions first.
|
||||
foreach ($questions as $questionid) {
|
||||
if (!in_array($questionid, $selectedquestionids)) {
|
||||
$newquestions[] = $questionid;
|
||||
|
||||
// Make up a $newslotorder, then let the next if statement do the work.
|
||||
$oldslots = $DB->get_records('quiz_slots', array('quizid' => $quiz->id), 'slot');
|
||||
|
||||
$beforepage = array();
|
||||
$onpage = array();
|
||||
$afterpage = array();
|
||||
foreach ($oldslots as $oldslot) {
|
||||
if (in_array($oldslot->slot, $selectedslots)) {
|
||||
$onpage[] = $oldslot;
|
||||
} else if ($oldslot->page <= $moveselectedonpage) {
|
||||
$beforepage[] = $oldslot;
|
||||
} else {
|
||||
$afterpage[] = $oldslot;
|
||||
}
|
||||
}
|
||||
$questions = $newquestions;
|
||||
|
||||
// Move to the end of the selected page.
|
||||
$pagebreakpositions = array_keys($questions, 0);
|
||||
$numpages = count($pagebreakpositions);
|
||||
|
||||
// Ensure the target page number is in range.
|
||||
for ($i = $moveselectedonpage; $i > $numpages; $i--) {
|
||||
$questions[] = 0;
|
||||
$pagebreakpositions[] = count($questions) - 1;
|
||||
$newslotorder = array();
|
||||
$currentpage = 1;
|
||||
$index = 10;
|
||||
foreach ($beforepage as $slot) {
|
||||
while ($currentpage < $slot->page) {
|
||||
$newslotorder[$index] = 0;
|
||||
$index += 10;
|
||||
$currentpage += 1;
|
||||
}
|
||||
$newslotorder[$index] = $slot->slot;
|
||||
$index += 10;
|
||||
}
|
||||
$moveselectedpos = $pagebreakpositions[$moveselectedonpage - 1];
|
||||
|
||||
// Do the move.
|
||||
array_splice($questions, $moveselectedpos, 0, $selectedquestionids);
|
||||
$quiz->questions = implode(',', $questions);
|
||||
while ($currentpage < $moveselectedonpage) {
|
||||
$newslotorder[$index] = 0;
|
||||
$index += 10;
|
||||
$currentpage += 1;
|
||||
}
|
||||
foreach ($onpage as $slot) {
|
||||
$newslotorder[$index] = $slot->slot;
|
||||
$index += 10;
|
||||
}
|
||||
|
||||
// Update the database.
|
||||
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
|
||||
foreach ($afterpage as $slot) {
|
||||
while ($currentpage < $slot->page) {
|
||||
$newslotorder[$index] = 0;
|
||||
$index += 10;
|
||||
$currentpage += 1;
|
||||
}
|
||||
$newslotorder[$index] = $slot->slot;
|
||||
$index += 10;
|
||||
}
|
||||
}
|
||||
|
||||
// If ordering info was given, reorder the questions.
|
||||
if ($newslotorder) {
|
||||
ksort($newslotorder);
|
||||
$currentpage = 1;
|
||||
$currentslot = 1;
|
||||
$slotreorder = array();
|
||||
$slotpages = array();
|
||||
foreach ($newslotorder as $slotnumber) {
|
||||
if ($slotnumber == 0) {
|
||||
$currentpage += 1;
|
||||
continue;
|
||||
}
|
||||
$slotreorder[$slotnumber] = $currentslot;
|
||||
$slotpages[$currentslot] = $currentpage;
|
||||
$currentslot += 1;
|
||||
}
|
||||
$trans = $DB->start_delegated_transaction();
|
||||
update_field_with_unique_index('quiz_slots',
|
||||
'slot', $slotreorder, array('quizid' => $quiz->id));
|
||||
foreach ($slotpages as $slotnumber => $page) {
|
||||
$DB->set_field('quiz_slots', 'page', $page, array('quizid' => $quiz->id, 'slot' => $slotnumber));
|
||||
}
|
||||
$trans->allow_commit();
|
||||
$deletepreviews = true;
|
||||
}
|
||||
|
||||
@@ -421,7 +439,11 @@ echo $OUTPUT->header();
|
||||
$quizeditconfig = new stdClass();
|
||||
$quizeditconfig->url = $thispageurl->out(true, array('qbanktool' => '0'));
|
||||
$quizeditconfig->dialoglisteners = array();
|
||||
$numberoflisteners = max(quiz_number_of_pages($quiz->questions), 1);
|
||||
$numberoflisteners = $DB->get_field_sql("
|
||||
SELECT COALESCE(MAX(page), 1)
|
||||
FROM {quiz_slots}
|
||||
WHERE quizid = ?", array($quiz->id));
|
||||
|
||||
for ($pageiter = 1; $pageiter <= $numberoflisteners; $pageiter++) {
|
||||
$quizeditconfig->dialoglisteners[] = 'addrandomdialoglaunch_' . $pageiter;
|
||||
}
|
||||
@@ -487,7 +509,6 @@ echo '<div class="quizcontents ' . $quizcontentsclass . '" id="quizcontentsblock
|
||||
if ($quiz->shufflequestions) {
|
||||
$repaginatingdisabledhtml = 'disabled="disabled"';
|
||||
$repaginatingdisabled = true;
|
||||
$quiz->questions = quiz_repaginate($quiz->questions, $quiz->questionsperpage);
|
||||
} else {
|
||||
$repaginatingdisabledhtml = '';
|
||||
$repaginatingdisabled = false;
|
||||
|
||||
+214
-229
@@ -48,12 +48,20 @@ function quiz_require_question_use($questionid) {
|
||||
|
||||
/**
|
||||
* Verify that the question exists, and the user has permission to use it.
|
||||
* @param int $questionid The id of the question.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param int $slot which question in the quiz to test.
|
||||
* @return bool whether the user can use this question.
|
||||
*/
|
||||
function quiz_has_question_use($questionid) {
|
||||
function quiz_has_question_use($quiz, $slot) {
|
||||
global $DB;
|
||||
$question = $DB->get_record('question', array('id' => $questionid), '*', MUST_EXIST);
|
||||
$question = $DB->get_record_sql("
|
||||
SELECT q.*
|
||||
FROM {quiz_slots} slot
|
||||
JOIN {question} q ON q.id = slot.questionid
|
||||
WHERE slot.quizid = ? AND slot.slot = ?", array($quiz->id, $slot));
|
||||
if (!$question) {
|
||||
return false;
|
||||
}
|
||||
return question_has_capability_on($question, 'use');
|
||||
}
|
||||
|
||||
@@ -62,118 +70,127 @@ function quiz_has_question_use($questionid) {
|
||||
* @param object $quiz the quiz object.
|
||||
* @param int $questionid The id of the question to be deleted.
|
||||
*/
|
||||
function quiz_remove_question($quiz, $questionid) {
|
||||
function quiz_remove_slot($quiz, $slotnumber) {
|
||||
global $DB;
|
||||
|
||||
$questionids = explode(',', $quiz->questions);
|
||||
$key = array_search($questionid, $questionids);
|
||||
if ($key === false) {
|
||||
$slot = $DB->get_record('quiz_slots', array('quizid' => $quiz->id, 'slot' => $slotnumber));
|
||||
$maxslot = $DB->get_field_sql('SELECT MAX(slot) FROM {quiz_slots} WHERE quizid = ?', array($quiz->id));
|
||||
if (!$slot) {
|
||||
return;
|
||||
}
|
||||
|
||||
unset($questionids[$key]);
|
||||
$quiz->questions = implode(',', $questionids);
|
||||
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
|
||||
$DB->delete_records('quiz_question_instances',
|
||||
array('quizid' => $quiz->instance, 'questionid' => $questionid));
|
||||
$trans = $DB->start_delegated_transaction();
|
||||
$DB->delete_records('quiz_slots', array('id' => $slot->id));
|
||||
for ($i = $slot->slot + 1; $i <= $maxslot; $i++) {
|
||||
$DB->set_field('quiz_slots', 'slot', $i - 1,
|
||||
array('quizid' => $quiz->id, 'slot' => $i));
|
||||
}
|
||||
$trans->allow_commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an empty page from the quiz layout. If that is not possible, do nothing.
|
||||
* @param string $layout the existinng layout, $quiz->questions.
|
||||
* @param int $index the position into $layout where the empty page should be removed.
|
||||
* @return the updated layout
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param int $pagenumber the page number to delete.
|
||||
*/
|
||||
function quiz_delete_empty_page($layout, $index) {
|
||||
$questionids = explode(',', $layout);
|
||||
function quiz_delete_empty_page($quiz, $pagenumber) {
|
||||
global $DB;
|
||||
|
||||
if ($index < -1 || $index >= count($questionids) - 1) {
|
||||
return $layout;
|
||||
if ($DB->record_exists('quiz_slots', array('quizid' => $quiz->id, 'page' => $pagenumber))) {
|
||||
// This was not an empty page.
|
||||
return;
|
||||
}
|
||||
|
||||
if (($index >= 0 && $questionids[$index] != 0) || $questionids[$index + 1] != 0) {
|
||||
return $layout; // This was not an empty page.
|
||||
}
|
||||
|
||||
unset($questionids[$index + 1]);
|
||||
|
||||
return implode(',', $questionids);
|
||||
$DB->execute('UPDATE {quiz_slots} SET page = page - 1 WHERE quizid = ? AND page > ?',
|
||||
array($quiz->id, $pagenumber));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a question to a quiz
|
||||
*
|
||||
* Adds a question to a quiz by updating $quiz as well as the
|
||||
* quiz and quiz_question_instances tables. It also adds a page break
|
||||
* if required.
|
||||
* @param int $id The id of the question to be added
|
||||
* quiz and quiz_slots tables. It also adds a page break if required.
|
||||
* @param int $questionid The id of the question to be added
|
||||
* @param object $quiz The extended quiz object as used by edit.php
|
||||
* This is updated by this function
|
||||
* @param int $page Which page in quiz to add the question on. If 0 (default),
|
||||
* add at the end
|
||||
* @param float $maxmark The maximum mark to set for this question. (Optional,
|
||||
* defaults to question.defaultmark.
|
||||
* @return bool false if the question was already in the quiz
|
||||
*/
|
||||
function quiz_add_quiz_question($id, $quiz, $page = 0) {
|
||||
function quiz_add_quiz_question($questionid, $quiz, $page = 0, $maxmark = null) {
|
||||
global $DB;
|
||||
$questions = explode(',', quiz_clean_layout($quiz->questions));
|
||||
if (in_array($id, $questions)) {
|
||||
$slots = $DB->get_records('quiz_slots', array('quizid' => $quiz->id),
|
||||
'slot', 'questionid, slot, page, id');
|
||||
if (array_key_exists($questionid, $slots)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove ending page break if it is not needed.
|
||||
if ($breaks = array_keys($questions, 0)) {
|
||||
// Determine location of the last two page breaks.
|
||||
$end = end($breaks);
|
||||
$last = prev($breaks);
|
||||
$last = $last ? $last : -1;
|
||||
if (!$quiz->questionsperpage || (($end - $last - 1) < $quiz->questionsperpage)) {
|
||||
array_pop($questions);
|
||||
}
|
||||
}
|
||||
if (is_int($page) && $page >= 1) {
|
||||
$numofpages = quiz_number_of_pages($quiz->questions);
|
||||
if ($numofpages<$page) {
|
||||
// The page specified does not exist in quiz.
|
||||
$page = 0;
|
||||
} else {
|
||||
// Add ending page break - the following logic requires doing
|
||||
// this at this point.
|
||||
$questions[] = 0;
|
||||
$currentpage = 1;
|
||||
$addnow = false;
|
||||
foreach ($questions as $question) {
|
||||
if ($question == 0) {
|
||||
$currentpage++;
|
||||
// The current page is the one after the one we want to add on,
|
||||
// so we add the question before adding the current page.
|
||||
if ($currentpage == $page + 1) {
|
||||
$questions_new[] = $id;
|
||||
}
|
||||
}
|
||||
$questions_new[] = $question;
|
||||
}
|
||||
$questions = $questions_new;
|
||||
}
|
||||
}
|
||||
if ($page == 0) {
|
||||
// Add question.
|
||||
$questions[] = $id;
|
||||
// Add ending page break.
|
||||
$questions[] = 0;
|
||||
}
|
||||
$trans = $DB->start_delegated_transaction();
|
||||
|
||||
// Save new questionslist in database.
|
||||
$quiz->questions = implode(',', $questions);
|
||||
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
|
||||
$maxpage = 1;
|
||||
$numonlastpage = 0;
|
||||
foreach ($slots as $slot) {
|
||||
if ($slot->page > $maxpage) {
|
||||
$maxpage = $slot->page;
|
||||
$numonlastpage = 1;
|
||||
} else {
|
||||
$numonlastpage += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new question instance.
|
||||
$instance = new stdClass();
|
||||
$instance->quizid = $quiz->id;
|
||||
$instance->questionid = $id;
|
||||
$instance->maxmark = $DB->get_field('question', 'defaultmark', array('id' => $id));
|
||||
$DB->insert_record('quiz_question_instances', $instance);
|
||||
$slot = new stdClass();
|
||||
$slot->quizid = $quiz->id;
|
||||
$slot->questionid = $questionid;
|
||||
|
||||
if ($maxmark !== null) {
|
||||
$slot->maxmark = $maxmark;
|
||||
} else {
|
||||
$slot->maxmark = $DB->get_field('question', 'defaultmark', array('id' => $questionid));
|
||||
}
|
||||
|
||||
if (is_int($page) && $page >= 1) {
|
||||
// Adding on a given page.
|
||||
$lastslotbefore = 1;
|
||||
foreach (array_reverse($slots) as $otherslot) {
|
||||
if ($otherslot->page > $page) {
|
||||
$DB->set_field('quiz_slots', 'slot', $otherslot->slot + 1, array('id' => $otherslot->id));
|
||||
} else {
|
||||
$lastslotbefore = $otherslot->slot;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$slot->slot = $lastslotbefore + 1;
|
||||
$slot->page = min($page, $maxpage + 1);
|
||||
|
||||
} else {
|
||||
$lastslot = end($slots);
|
||||
if ($lastslot) {
|
||||
$slot->slot = $lastslot->slot + 1;
|
||||
} else {
|
||||
$slot->slot = 1;
|
||||
}
|
||||
if ($quiz->questionsperpage && $numonlastpage >= $quiz->questionsperpage) {
|
||||
$slot->page = $maxpage + 1;
|
||||
} else {
|
||||
$slot->page = $maxpage;
|
||||
}
|
||||
}
|
||||
|
||||
$DB->insert_record('quiz_slots', $slot);
|
||||
$trans->allow_commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a random question to the quiz at a given point.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param int $addonpage the page on which to add the question.
|
||||
* @param int $categoryid the question category to add the question from.
|
||||
* @param int $number the number of random questions to add.
|
||||
* @param bool $includesubcategories whether to include questoins from subcategories.
|
||||
*/
|
||||
function quiz_add_random_questions($quiz, $addonpage, $categoryid, $number,
|
||||
$includesubcategories) {
|
||||
global $DB;
|
||||
@@ -195,7 +212,7 @@ function quiz_add_random_questions($quiz, $addonpage, $categoryid, $number,
|
||||
AND " . $DB->sql_compare_text('questiontext') . " = ?
|
||||
AND NOT EXISTS (
|
||||
SELECT *
|
||||
FROM {quiz_question_instances}
|
||||
FROM {quiz_slots}
|
||||
WHERE questionid = q.id)
|
||||
ORDER BY id", array($category->id, $includesubcategories))) {
|
||||
// Take as many of these as needed.
|
||||
@@ -228,136 +245,103 @@ function quiz_add_random_questions($quiz, $addonpage, $categoryid, $number,
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a page break after at particular position$.
|
||||
* @param string $layout the existinng layout, $quiz->questions.
|
||||
* @param int $index the position into $layout where the empty page should be removed.
|
||||
* @return the updated layout
|
||||
* Add a page break after a particular slot.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param int $slot the slot to add the page break after.
|
||||
*/
|
||||
function quiz_add_page_break_at($layout, $index) {
|
||||
$questionids = explode(',', $layout);
|
||||
if ($index < 0 || $index >= count($questionids)) {
|
||||
return $layout;
|
||||
}
|
||||
|
||||
array_splice($questionids, $index, 0, '0');
|
||||
|
||||
return implode(',', $questionids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a page break after a particular question.
|
||||
* @param string $layout the existinng layout, $quiz->questions.
|
||||
* @param int $qustionid the question to add the page break after.
|
||||
* @return the updated layout
|
||||
*/
|
||||
function quiz_add_page_break_after($layout, $questionid) {
|
||||
$questionids = explode(',', $layout);
|
||||
$key = array_search($questionid, $questionids);
|
||||
if ($key === false || !$questionid) {
|
||||
return $layout;
|
||||
}
|
||||
|
||||
array_splice($questionids, $key + 1, 0, '0');
|
||||
|
||||
return implode(',', $questionids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the database after $quiz->questions has been changed. For example,
|
||||
* this deletes preview attempts and updates $quiz->sumgrades.
|
||||
* @param $quiz the quiz object.
|
||||
*/
|
||||
function quiz_save_new_layout($quiz) {
|
||||
function quiz_add_page_break_after_slot($quiz, $slot) {
|
||||
global $DB;
|
||||
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
|
||||
quiz_delete_previews($quiz);
|
||||
quiz_update_sumgrades($quiz);
|
||||
|
||||
$DB->execute('UPDATE {quiz_slots} SET page = page + 1 WHERE quizid = ? AND slot > ?',
|
||||
array($quiz->id, $slot));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save changes to question instance
|
||||
* Change the max mark for a slot.
|
||||
*
|
||||
* Saves changes to the question grades in the quiz_question_instances table.
|
||||
* Saves changes to the question grades in the quiz_slots table and any
|
||||
* corresponding question_attempts.
|
||||
* It does not update 'sumgrades' in the quiz table.
|
||||
*
|
||||
* @param float $maxmark the maximal grade for the question.
|
||||
* @param int $questionid the question id.
|
||||
* @param stdClass $quiz the quiz settings.
|
||||
* @param stdClass $slot row from the quiz_slots table.
|
||||
* @param float $maxmark the new maxmark.
|
||||
*/
|
||||
function quiz_update_question_instance($maxmark, $questionid, $quiz) {
|
||||
function quiz_update_slot_maxmark($slot, $maxmark) {
|
||||
global $DB;
|
||||
$instance = $DB->get_record('quiz_question_instances', array('quizid' => $quiz->id,
|
||||
'questionid' => $questionid));
|
||||
$slot = quiz_get_slot_for_question($quiz, $questionid);
|
||||
|
||||
if (!$instance || !$slot) {
|
||||
throw new coding_exception('Attempt to change the max mark of a quesion not in the quiz.');
|
||||
}
|
||||
|
||||
if (abs($maxmark - $instance->maxmark) < 1e-7) {
|
||||
if (abs($maxmark - $slot->maxmark) < 1e-7) {
|
||||
// Grade has not changed. Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
$instance->maxmark = $maxmark;
|
||||
$DB->update_record('quiz_question_instances', $instance);
|
||||
question_engine::set_max_mark_in_attempts(new qubaids_for_quiz($quiz->id),
|
||||
$slot, $maxmark);
|
||||
}
|
||||
|
||||
// Private function used by the following two.
|
||||
function _quiz_move_question($layout, $questionid, $shift) {
|
||||
if (!$questionid || !($shift == 1 || $shift == -1)) {
|
||||
return $layout;
|
||||
}
|
||||
|
||||
$questionids = explode(',', $layout);
|
||||
$key = array_search($questionid, $questionids);
|
||||
if ($key === false) {
|
||||
return $layout;
|
||||
}
|
||||
|
||||
$otherkey = $key + $shift;
|
||||
if ($otherkey < 0 || $otherkey >= count($questionids) - 1) {
|
||||
return $layout;
|
||||
}
|
||||
|
||||
$temp = $questionids[$otherkey];
|
||||
$questionids[$otherkey] = $questionids[$key];
|
||||
$questionids[$key] = $temp;
|
||||
|
||||
return implode(',', $questionids);
|
||||
$slot->maxmark = $maxmark;
|
||||
$DB->update_record('quiz_slots', $slot);
|
||||
question_engine::set_max_mark_in_attempts(new qubaids_for_quiz($slot->quizid),
|
||||
$slot->slot, $maxmark);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a particular question one space earlier in the $quiz->questions list.
|
||||
* If that is not possible, do nothing.
|
||||
* @param string $layout the existinng layout, $quiz->questions.
|
||||
* @param int $questionid the id of a question.
|
||||
* @return the updated layout
|
||||
* Private function used by the following two.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param int $slotnumber the slot to move up.
|
||||
* @param int $shift +1 means move down, -1 means move up.
|
||||
*/
|
||||
function quiz_move_question_up($layout, $questionid) {
|
||||
return _quiz_move_question($layout, $questionid, -1);
|
||||
function _quiz_move_question($quiz, $slotnumber, $shift) {
|
||||
global $DB;
|
||||
|
||||
if (!$slotnumber || !($shift == 1 || $shift == -1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$slot = $DB->get_record('quiz_slots',
|
||||
array('quizid' => $quiz->id, 'slot' => $slotnumber));
|
||||
if (!$slot) {
|
||||
return;
|
||||
}
|
||||
|
||||
$otherslot = $DB->get_record('quiz_slots',
|
||||
array('quizid' => $quiz->id, 'slot' => $slotnumber + $shift));
|
||||
if (!$otherslot) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($otherslot->page != $slot->page) {
|
||||
$DB->set_field('quiz_slots', 'page', $slot->page + $shift, array('id' => $slot->id));
|
||||
return;
|
||||
}
|
||||
|
||||
$trans = $DB->start_delegated_transaction();
|
||||
$DB->set_field('quiz_slots', 'slot', -1, array('id' => $slot->id));
|
||||
$DB->set_field('quiz_slots', 'slot', $slot->slot, array('id' => $otherslot->id));
|
||||
$DB->set_field('quiz_slots', 'slot', $otherslot->slot, array('id' => $slot->id));
|
||||
$trans->allow_commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a particular question one space later in the $quiz->questions list.
|
||||
* Move a particular question one space earlier in the quiz.
|
||||
* If that is not possible, do nothing.
|
||||
* @param string $layout the existinng layout, $quiz->questions.
|
||||
* @param int $questionid the id of a question.
|
||||
* @return the updated layout
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param int $slot the slot to move up.
|
||||
*/
|
||||
function quiz_move_question_down($layout, $questionid) {
|
||||
return _quiz_move_question($layout, $questionid, +1);
|
||||
function quiz_move_question_up($quiz, $slot) {
|
||||
_quiz_move_question($quiz, $slot, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a particular question one space later in the quiz.
|
||||
* If that is not possible, do nothing.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param int $slot the slot to move down.
|
||||
*/
|
||||
function quiz_move_question_down($quiz, $slot) {
|
||||
return _quiz_move_question($quiz, $slot, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of quiz questions for the edit.php main view for edit
|
||||
* ($reordertool = false) and order and paging ($reordertool = true) tabs
|
||||
*
|
||||
* @param object $quiz This is not the standard quiz object used elsewhere but
|
||||
* it contains the quiz layout in $quiz->questions and the grades in
|
||||
* $quiz->grades
|
||||
* @param object $quiz The quiz settings.
|
||||
* @param moodle_url $pageurl The url of the current page with the parameters required
|
||||
* for links returning to the current page, as a moodle_url object
|
||||
* @param bool $allowdelete Indicates whether the delete icons should be displayed
|
||||
@@ -389,21 +373,14 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
$strtype = get_string('type', 'quiz');
|
||||
$strpreview = get_string('preview', 'quiz');
|
||||
|
||||
if ($quiz->questions) {
|
||||
list($usql, $params) = $DB->get_in_or_equal(explode(',', $quiz->questions));
|
||||
$params[] = $quiz->id;
|
||||
$questions = $DB->get_records_sql("SELECT q.*, qc.contextid, qqi.maxmark
|
||||
FROM {question} q
|
||||
JOIN {question_categories} qc ON qc.id = q.category
|
||||
JOIN {quiz_question_instances} qqi ON qqi.questionid = q.id
|
||||
WHERE q.id $usql AND qqi.quizid = ?", $params);
|
||||
} else {
|
||||
$questions = array();
|
||||
}
|
||||
$questions = $DB->get_records_sql("SELECT slot.slot, q.*, qc.contextid, slot.page, slot.maxmark
|
||||
FROM {quiz_slots} slot
|
||||
LEFT JOIN {question} q ON q.id = slot.questionid
|
||||
LEFT JOIN {question_categories} qc ON qc.id = q.category
|
||||
WHERE slot.quizid = ?
|
||||
ORDER BY slot.slot", array($quiz->id));
|
||||
|
||||
$layout = quiz_clean_layout($quiz->questions);
|
||||
$order = explode(',', $layout);
|
||||
$lastindex = count($order) - 1;
|
||||
$lastindex = count($questions) - 1;
|
||||
|
||||
$disabled = '';
|
||||
$pagingdisabled = '';
|
||||
@@ -468,6 +445,18 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
echo $reordercontrolstop;
|
||||
}
|
||||
|
||||
// Build fake order for backwards compatibility.
|
||||
$currentpage = 1;
|
||||
$order = array();
|
||||
foreach ($questions as $question) {
|
||||
while ($question->page > $currentpage) {
|
||||
$currentpage += 1;
|
||||
$order[] = 0;
|
||||
}
|
||||
$order[] = $question->slot;
|
||||
}
|
||||
$order[] = 0;
|
||||
|
||||
// The current question ordinal (no descriptions).
|
||||
$qno = 1;
|
||||
// The current question (includes questions and descriptions).
|
||||
@@ -476,28 +465,26 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
$pagecount = 0;
|
||||
|
||||
$pageopen = false;
|
||||
$lastslot = 0;
|
||||
|
||||
$returnurl = $pageurl->out_as_local_url(false);
|
||||
$questiontotalcount = count($order);
|
||||
|
||||
foreach ($order as $count => $qnum) {
|
||||
foreach ($order as $count => $qnum) { // Note: $qnum is acutally slot number, if it is not 0.
|
||||
|
||||
$reordercheckbox = '';
|
||||
$reordercheckboxlabel = '';
|
||||
$reordercheckboxlabelclose = '';
|
||||
|
||||
// If the questiontype is missing change the question type.
|
||||
if ($qnum && !array_key_exists($qnum, $questions)) {
|
||||
$fakequestion = new stdClass();
|
||||
$fakequestion->id = $qnum;
|
||||
$fakequestion->category = 0;
|
||||
$fakequestion->qtype = 'missingtype';
|
||||
$fakequestion->name = get_string('missingquestion', 'quiz');
|
||||
$fakequestion->questiontext = ' ';
|
||||
$fakequestion->questiontextformat = FORMAT_HTML;
|
||||
$fakequestion->length = 1;
|
||||
$questions[$qnum] = $fakequestion;
|
||||
$quiz->grades[$qnum] = 0;
|
||||
if ($qnum && $questions[$qnum]->qtype === null) {
|
||||
$questions[$qnum]->id = $qnum;
|
||||
$questions[$qnum]->category = 0;
|
||||
$questions[$qnum]->qtype = 'missingtype';
|
||||
$questions[$qnum]->name = get_string('missingquestion', 'quiz');
|
||||
$questions[$qnum]->questiontext = ' ';
|
||||
$questions[$qnum]->questiontextformat = FORMAT_HTML;
|
||||
$questions[$qnum]->length = 1;
|
||||
|
||||
} else if ($qnum && !question_bank::qtype_exists($questions[$qnum]->qtype)) {
|
||||
$questions[$qnum]->qtype = 'missingtype';
|
||||
@@ -521,7 +508,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
if ($allowdelete) {
|
||||
echo '<div class="quizpagedelete">';
|
||||
echo $OUTPUT->action_icon($pageurl->out(true,
|
||||
array('deleteemptypage' => $count - 1, 'sesskey'=>sesskey())),
|
||||
array('deleteemptypage' => $pagecount, 'sesskey' => sesskey())),
|
||||
new pix_icon('t/delete', $strremove),
|
||||
new component_action('click',
|
||||
'M.core_scroll_manager.save_scroll_action'),
|
||||
@@ -550,9 +537,9 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
$reordercheckboxlabel = '';
|
||||
$reordercheckboxlabelclose = '';
|
||||
if ($reordertool) {
|
||||
$reordercheckbox = '<input type="checkbox" name="s' . $question->id .
|
||||
'" id="s' . $question->id . '" />';
|
||||
$reordercheckboxlabel = '<label for="s' . $question->id . '">';
|
||||
$reordercheckbox = '<input type="checkbox" name="s' . $question->slot .
|
||||
'" id="s' . $question->slot . '" />';
|
||||
$reordercheckboxlabel = '<label for="s' . $question->slot . '">';
|
||||
$reordercheckboxlabelclose = '</label>';
|
||||
}
|
||||
if ($question->length == 0) {
|
||||
@@ -582,7 +569,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
$upbuttonclass = 'upwithoutdown';
|
||||
}
|
||||
echo $OUTPUT->action_icon($pageurl->out(true,
|
||||
array('up' => $question->id, 'sesskey'=>sesskey())),
|
||||
array('up' => $question->slot, 'sesskey' => sesskey())),
|
||||
new pix_icon('t/up', $strmoveup),
|
||||
new component_action('click',
|
||||
'M.core_scroll_manager.save_scroll_action'),
|
||||
@@ -590,10 +577,10 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
}
|
||||
|
||||
}
|
||||
if ($count < $lastindex - 1) {
|
||||
if ($count < $questiontotalcount - 2) {
|
||||
if (!$hasattempts) {
|
||||
echo $OUTPUT->action_icon($pageurl->out(true,
|
||||
array('down' => $question->id, 'sesskey'=>sesskey())),
|
||||
array('down' => $question->slot, 'sesskey' => sesskey())),
|
||||
new pix_icon('t/down', $strmovedown),
|
||||
new component_action('click',
|
||||
'M.core_scroll_manager.save_scroll_action'),
|
||||
@@ -605,7 +592,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
// Remove from quiz, not question delete.
|
||||
if (!$hasattempts) {
|
||||
echo $OUTPUT->action_icon($pageurl->out(true,
|
||||
array('remove' => $question->id, 'sesskey'=>sesskey())),
|
||||
array('remove' => $question->slot, 'sesskey' => sesskey())),
|
||||
new pix_icon('t/delete', $strremove),
|
||||
new component_action('click',
|
||||
'M.core_scroll_manager.save_scroll_action'),
|
||||
@@ -619,15 +606,15 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
<div class="points">
|
||||
<form method="post" action="edit.php" class="quizsavegradesform"><div>
|
||||
<fieldset class="invisiblefieldset" style="display: block;">
|
||||
<label for="<?php echo "inputq$question->id" ?>"><?php echo $strmaxmark; ?></label>:<br />
|
||||
<label for="<?php echo 'inputq' . $question->slot; ?>"><?php echo $strmaxmark; ?></label>:<br />
|
||||
<input type="hidden" name="sesskey" value="<?php echo sesskey() ?>" />
|
||||
<?php echo html_writer::input_hidden_params($pageurl); ?>
|
||||
<input type="hidden" name="savechanges" value="save" />
|
||||
<?php
|
||||
echo '<input type="text" name="g' . $question->id .
|
||||
'" id="inputq' . $question->id .
|
||||
echo '<input type="text" name="g' . $question->slot .
|
||||
'" id="inputq' . $question->slot .
|
||||
'" size="' . ($quiz->decimalpoints + 2) .
|
||||
'" value="' . (0 + $quiz->grades[$qnum]) .
|
||||
'" value="' . (0 + $question->maxmark) .
|
||||
'" tabindex="' . ($lastindex + $qno) . '" />';
|
||||
?>
|
||||
<input type="submit" class="pointssubmitbutton" value="<?php echo $strsave; ?>" />
|
||||
@@ -650,9 +637,9 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
?>
|
||||
<div class="qorder">
|
||||
<?php
|
||||
echo '<label class="accesshide" for="o' . $question->id . '">' .
|
||||
echo '<label class="accesshide" for="o' . $question->slot . '">' .
|
||||
get_string('questionposition', 'quiz', $qnodisplay) . '</label>';
|
||||
echo '<input type="text" name="o' . $question->id .
|
||||
echo '<input type="text" name="o' . $question->slot .
|
||||
'" id="o' . $question->id . '"' .
|
||||
'" size="2" value="' . (10*$count + 10) .
|
||||
'" tabindex="' . ($lastindex + $qno) . '" />';
|
||||
@@ -705,7 +692,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
echo $OUTPUT->container_start('addpage');
|
||||
$url = new moodle_url($pageurl->out_omit_querystring(),
|
||||
array('cmid' => $quiz->cmid, 'courseid' => $quiz->course,
|
||||
'addpage' => $count, 'sesskey' => sesskey()));
|
||||
'addpage' => $pagecount, 'sesskey' => sesskey()));
|
||||
echo $OUTPUT->single_button($url, get_string('addpagehere', 'quiz'), 'post',
|
||||
array('disabled' => $hasattempts,
|
||||
'actions' => array(new component_action('click',
|
||||
@@ -728,9 +715,7 @@ function quiz_print_question_list($quiz, $pageurl, $allowdelete, $reordertool,
|
||||
* Print all the controls for adding questions directly into the
|
||||
* specific page in the edit tab of edit.php
|
||||
*
|
||||
* @param object $quiz This is not the standard quiz object used elsewhere but
|
||||
* it contains the quiz layout in $quiz->questions and the grades in
|
||||
* $quiz->grades
|
||||
* @param object $quiz The quiz settings.
|
||||
* @param moodle_url $pageurl The url of the current page with the parameters required
|
||||
* for links returning to the current page, as a moodle_url object
|
||||
* @param int $page the current page number.
|
||||
@@ -934,7 +919,7 @@ function quiz_print_singlequestion_reordertool($question, $returnurl, $quiz) {
|
||||
* @param object $questionurl The url of the question editing page as a moodle_url object
|
||||
* @param object $quiz The quiz in the context of which the question is being displayed
|
||||
*/
|
||||
function quiz_print_randomquestion_reordertool(&$question, &$pageurl, &$quiz) {
|
||||
function quiz_print_randomquestion_reordertool($question, $pageurl, $quiz) {
|
||||
global $DB, $OUTPUT;
|
||||
|
||||
// Load the category, and the number of available questions in it.
|
||||
@@ -1311,7 +1296,7 @@ function quiz_print_grading_form($quiz, $pageurl, $tabindex) {
|
||||
* @param object $quiz The quiz object of the quiz in question
|
||||
*/
|
||||
function quiz_print_status_bar($quiz) {
|
||||
global $CFG;
|
||||
global $DB;
|
||||
|
||||
$bits = array();
|
||||
|
||||
@@ -1320,7 +1305,7 @@ function quiz_print_status_bar($quiz) {
|
||||
array('class' => 'totalpoints'));
|
||||
|
||||
$bits[] = html_writer::tag('span',
|
||||
get_string('numquestionsx', 'quiz', quiz_number_of_questions_in_quiz($quiz->questions)),
|
||||
get_string('numquestionsx', 'quiz', $DB->count_records('quiz_slots', array('quizid' => $quiz->id))),
|
||||
array('class' => 'numberofquestions'));
|
||||
|
||||
$timenow = time();
|
||||
|
||||
+7
-10
@@ -81,7 +81,6 @@ function quiz_add_instance($quiz) {
|
||||
|
||||
// Process the options from the form.
|
||||
$quiz->created = time();
|
||||
$quiz->questions = '';
|
||||
$result = quiz_process_options($quiz);
|
||||
if ($result && is_string($result)) {
|
||||
return $result;
|
||||
@@ -122,13 +121,6 @@ function quiz_update_instance($quiz, $mform) {
|
||||
$quiz->sumgrades = $oldquiz->sumgrades;
|
||||
$quiz->grade = $oldquiz->grade;
|
||||
|
||||
// Repaginate, if asked to.
|
||||
if (!$quiz->shufflequestions && !empty($quiz->repaginatenow)) {
|
||||
$quiz->questions = quiz_repaginate(quiz_clean_layout($oldquiz->questions, true),
|
||||
$quiz->questionsperpage);
|
||||
}
|
||||
unset($quiz->repaginatenow);
|
||||
|
||||
// Update the database.
|
||||
$quiz->id = $quiz->instance;
|
||||
$DB->update_record('quiz', $quiz);
|
||||
@@ -151,6 +143,11 @@ function quiz_update_instance($quiz, $mform) {
|
||||
// Delete any previous preview attempts.
|
||||
quiz_delete_previews($quiz);
|
||||
|
||||
// Repaginate, if asked to.
|
||||
if (!$quiz->shufflequestions && !empty($quiz->repaginatenow)) {
|
||||
quiz_repaginate_questions($quiz->id, $quiz->questionsperpage);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -170,7 +167,7 @@ function quiz_delete_instance($id) {
|
||||
quiz_delete_all_attempts($quiz);
|
||||
quiz_delete_all_overrides($quiz);
|
||||
|
||||
$DB->delete_records('quiz_question_instances', array('quizid' => $quiz->id));
|
||||
$DB->delete_records('quiz_slots', array('quizid' => $quiz->id));
|
||||
$DB->delete_records('quiz_feedback', array('quizid' => $quiz->id));
|
||||
|
||||
$events = $DB->get_records('event', array('modulename' => 'quiz', 'instance' => $quiz->id));
|
||||
@@ -1288,7 +1285,7 @@ function quiz_questions_in_use($questionids) {
|
||||
global $DB, $CFG;
|
||||
require_once($CFG->libdir . '/questionlib.php');
|
||||
list($test, $params) = $DB->get_in_or_equal($questionids);
|
||||
return $DB->record_exists_select('quiz_question_instances',
|
||||
return $DB->record_exists_select('quiz_slots',
|
||||
'questionid ' . $test, $params) || question_engine::questions_in_use(
|
||||
$questionids, new qubaid_join('{quiz_attempts} quiza',
|
||||
'quiza.uniqueid', 'quiza.preview = 0'));
|
||||
|
||||
+51
-194
@@ -110,10 +110,7 @@ function quiz_create_attempt(quiz $quizobj, $attemptnumber, $lastattempt, $timen
|
||||
$attempt->quiz = $quiz->id;
|
||||
$attempt->userid = $userid;
|
||||
$attempt->preview = 0;
|
||||
$attempt->layout = quiz_clean_layout($quiz->questions, true);
|
||||
if ($quiz->shufflequestions) {
|
||||
$attempt->layout = quiz_repaginate($attempt->layout, $quiz->questionsperpage, true);
|
||||
}
|
||||
$attempt->layout = '';
|
||||
} else {
|
||||
// Build on last attempt.
|
||||
if (empty($lastattempt)) {
|
||||
@@ -165,9 +162,8 @@ function quiz_start_new_attempt($quizobj, $quba, $attempt, $attemptnumber, $time
|
||||
$quizobj->load_questions();
|
||||
|
||||
// Add them all to the $quba.
|
||||
$idstoslots = array();
|
||||
$questionsinuse = array_keys($quizobj->get_questions());
|
||||
foreach ($quizobj->get_questions() as $i => $questiondata) {
|
||||
foreach ($quizobj->get_questions() as $questiondata) {
|
||||
if ($questiondata->qtype != 'random') {
|
||||
if (!$quizobj->get_quiz()->shuffleanswers) {
|
||||
$questiondata->options->shuffleanswers = false;
|
||||
@@ -189,7 +185,7 @@ function quiz_start_new_attempt($quizobj, $quba, $attempt, $attemptnumber, $time
|
||||
}
|
||||
}
|
||||
|
||||
$idstoslots[$i] = $quba->add_question($question, $questiondata->maxmark);
|
||||
$quba->add_question($question, $questiondata->maxmark);
|
||||
$questionsinuse[] = $question->id;
|
||||
}
|
||||
|
||||
@@ -211,16 +207,36 @@ function quiz_start_new_attempt($quizobj, $quba, $attempt, $attemptnumber, $time
|
||||
|
||||
$quba->start_all_questions($variantstrategy, $timenow);
|
||||
|
||||
// Update attempt layout.
|
||||
$newlayout = array();
|
||||
foreach (explode(',', $attempt->layout) as $qid) {
|
||||
if ($qid != 0) {
|
||||
$newlayout[] = $idstoslots[$qid];
|
||||
} else {
|
||||
$newlayout[] = 0;
|
||||
// Work out the attempt layout.
|
||||
$layout = array();
|
||||
if ($quizobj->get_quiz()->shufflequestions) {
|
||||
$slots = $quba->get_slots();
|
||||
shuffle($slots);
|
||||
|
||||
$questionsonthispage = 0;
|
||||
foreach ($slots as $slot) {
|
||||
if ($questionsonthispage && $questionsonthispage == $quizobj->get_quiz()->questionsperpage) {
|
||||
$layout[] = 0;
|
||||
$questionsonthispage = 0;
|
||||
}
|
||||
$layout[] = $slot;
|
||||
$questionsonthispage += 1;
|
||||
}
|
||||
|
||||
} else {
|
||||
$currentpage = null;
|
||||
foreach ($quizobj->get_questions() as $slot) {
|
||||
if ($currentpage !== null && $slot->page != $currentpage) {
|
||||
$layout[] = 0;
|
||||
}
|
||||
$layout[] = $slot->slot;
|
||||
$currentpage = $slot->page;
|
||||
}
|
||||
}
|
||||
$attempt->layout = implode(',', $newlayout);
|
||||
|
||||
$layout[] = 0;
|
||||
$attempt->layout = implode(',', $layout);
|
||||
|
||||
return $attempt;
|
||||
}
|
||||
|
||||
@@ -386,127 +402,35 @@ function quiz_has_attempts($quizid) {
|
||||
// Functions to do with quiz layout and pages //////////////////////////////////
|
||||
|
||||
/**
|
||||
* Returns a comma separated list of question ids for the quiz
|
||||
*
|
||||
* @param string $layout The string representing the quiz layout. Each page is
|
||||
* represented as a comma separated list of question ids and 0 indicating
|
||||
* page breaks. So 5,2,0,3,0 means questions 5 and 2 on page 1 and question
|
||||
* 3 on page 2
|
||||
* @return string comma separated list of question ids, without page breaks.
|
||||
* Repaginate the questions in a quiz
|
||||
* @param int $quizid the id of the quiz to repaginate.
|
||||
* @param int $slotsperpage number of items to put on each page. 0 means unlimited.
|
||||
*/
|
||||
function quiz_questions_in_quiz($layout) {
|
||||
$questions = str_replace(',0', '', quiz_clean_layout($layout, true));
|
||||
if ($questions === '0') {
|
||||
return '';
|
||||
} else {
|
||||
return $questions;
|
||||
}
|
||||
}
|
||||
function quiz_repaginate_questions($quizid, $slotsperpage) {
|
||||
global $DB;
|
||||
$trans = $DB->start_delegated_transaction();
|
||||
|
||||
/**
|
||||
* Returns the number of pages in a quiz layout
|
||||
*
|
||||
* @param string $layout The string representing the quiz layout. Always ends in ,0
|
||||
* @return int The number of pages in the quiz.
|
||||
*/
|
||||
function quiz_number_of_pages($layout) {
|
||||
return substr_count(',' . $layout, ',0');
|
||||
}
|
||||
$slots = $DB->get_records('quiz_slots', array('quizid' => $quizid),
|
||||
'slot');
|
||||
|
||||
/**
|
||||
* Returns the number of questions in the quiz layout
|
||||
*
|
||||
* @param string $layout the string representing the quiz layout.
|
||||
* @return int The number of questions in the quiz.
|
||||
*/
|
||||
function quiz_number_of_questions_in_quiz($layout) {
|
||||
$layout = quiz_questions_in_quiz(quiz_clean_layout($layout));
|
||||
$count = substr_count($layout, ',');
|
||||
if ($layout !== '') {
|
||||
$count++;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-paginates the quiz layout
|
||||
*
|
||||
* @param string $layout The string representing the quiz layout. If there is
|
||||
* if there is any doubt about the quality of the input data, call
|
||||
* quiz_clean_layout before you call this function.
|
||||
* @param int $perpage The number of questions per page
|
||||
* @param bool $shuffle Should the questions be reordered randomly?
|
||||
* @return string the new layout string
|
||||
*/
|
||||
function quiz_repaginate($layout, $perpage, $shuffle = false) {
|
||||
$questions = quiz_questions_in_quiz($layout);
|
||||
if (!$questions) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
$questions = explode(',', quiz_questions_in_quiz($layout));
|
||||
if ($shuffle) {
|
||||
shuffle($questions);
|
||||
}
|
||||
|
||||
$onthispage = 0;
|
||||
$layout = array();
|
||||
foreach ($questions as $question) {
|
||||
if ($perpage and $onthispage >= $perpage) {
|
||||
$layout[] = 0;
|
||||
$onthispage = 0;
|
||||
$currentpage = 1;
|
||||
$slotsonthispage = 0;
|
||||
foreach ($slots as $slot) {
|
||||
if ($slotsonthispage && $slotsonthispage == $slotsperpage) {
|
||||
$currentpage += 1;
|
||||
$slotsonthispage = 0;
|
||||
}
|
||||
$layout[] = $question;
|
||||
$onthispage += 1;
|
||||
if ($slot->page != $currentpage) {
|
||||
$DB->set_field('quiz_slots', 'page', $currentpage, array('id' => $slot->id));
|
||||
}
|
||||
$slotsonthispage += 1;
|
||||
}
|
||||
|
||||
$layout[] = 0;
|
||||
return implode(',', $layout);
|
||||
$trans->allow_commit();
|
||||
}
|
||||
|
||||
// Functions to do with quiz grades ////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Creates an array of maximum grades for a quiz
|
||||
*
|
||||
* The grades are extracted from the quiz_question_instances table.
|
||||
* @param object $quiz The quiz settings.
|
||||
* @return array of grades indexed by question id. These are the maximum
|
||||
* possible grades that students can achieve for each of the questions.
|
||||
*/
|
||||
function quiz_get_all_question_grades($quiz) {
|
||||
global $CFG, $DB;
|
||||
|
||||
$questionlist = quiz_questions_in_quiz($quiz->questions);
|
||||
if (empty($questionlist)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$params = array($quiz->id);
|
||||
$wheresql = '';
|
||||
if (!is_null($questionlist)) {
|
||||
list($usql, $question_params) = $DB->get_in_or_equal(explode(',', $questionlist));
|
||||
$wheresql = ' AND questionid ' . $usql;
|
||||
$params = array_merge($params, $question_params);
|
||||
}
|
||||
|
||||
$instances = $DB->get_records_sql("SELECT questionid, maxmark, id
|
||||
FROM {quiz_question_instances}
|
||||
WHERE quizid = ?{$wheresql}", $params);
|
||||
|
||||
$list = explode(',', $questionlist);
|
||||
$grades = array();
|
||||
|
||||
foreach ($list as $qid) {
|
||||
if (isset($instances[$qid])) {
|
||||
$grades[$qid] = $instances[$qid]->maxmark;
|
||||
} else {
|
||||
$grades[$qid] = 1;
|
||||
}
|
||||
}
|
||||
return $grades;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the raw grade stored in $attempt into a grade out of the maximum
|
||||
* grade for this quiz.
|
||||
@@ -602,7 +526,7 @@ function quiz_update_sumgrades($quiz) {
|
||||
$sql = 'UPDATE {quiz}
|
||||
SET sumgrades = COALESCE((
|
||||
SELECT SUM(maxmark)
|
||||
FROM {quiz_question_instances}
|
||||
FROM {quiz_slots}
|
||||
WHERE quizid = {quiz}.id
|
||||
), 0)
|
||||
WHERE id = ?';
|
||||
@@ -1425,73 +1349,6 @@ function quiz_get_combined_reviewoptions($quiz, $attempts) {
|
||||
return array($someoptions, $alloptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean the question layout from various possible anomalies:
|
||||
* - Remove consecutive ","'s
|
||||
* - Remove duplicate question id's
|
||||
* - Remove extra "," from beginning and end
|
||||
* - Finally, add a ",0" in the end if there is none
|
||||
*
|
||||
* @param $string $layout the quiz layout to clean up, usually from $quiz->questions.
|
||||
* @param bool $removeemptypages If true, remove empty pages from the quiz. False by default.
|
||||
* @return $string the cleaned-up layout
|
||||
*/
|
||||
function quiz_clean_layout($layout, $removeemptypages = false) {
|
||||
// Remove repeated ','s. This can happen when a restore fails to find the right
|
||||
// id to relink to.
|
||||
$layout = preg_replace('/,{2,}/', ',', trim($layout, ','));
|
||||
|
||||
// Remove duplicate question ids.
|
||||
$layout = explode(',', $layout);
|
||||
$cleanerlayout = array();
|
||||
$seen = array();
|
||||
foreach ($layout as $item) {
|
||||
if ($item == 0) {
|
||||
$cleanerlayout[] = '0';
|
||||
} else if (!in_array($item, $seen)) {
|
||||
$cleanerlayout[] = $item;
|
||||
$seen[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
if ($removeemptypages) {
|
||||
// Avoid duplicate page breaks.
|
||||
$layout = $cleanerlayout;
|
||||
$cleanerlayout = array();
|
||||
$stripfollowingbreaks = true; // Ensure breaks are stripped from the start.
|
||||
foreach ($layout as $item) {
|
||||
if ($stripfollowingbreaks && $item == 0) {
|
||||
continue;
|
||||
}
|
||||
$cleanerlayout[] = $item;
|
||||
$stripfollowingbreaks = $item == 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a page break at the end if there is none.
|
||||
if (end($cleanerlayout) !== '0') {
|
||||
$cleanerlayout[] = '0';
|
||||
}
|
||||
|
||||
return implode(',', $cleanerlayout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the slot for a question with a particular id.
|
||||
* @param object $quiz the quiz settings.
|
||||
* @param int $questionid the of a question in the quiz.
|
||||
* @return int the corresponding slot. Null if the question is not in the quiz.
|
||||
*/
|
||||
function quiz_get_slot_for_question($quiz, $questionid) {
|
||||
$questionids = quiz_questions_in_quiz($quiz->questions);
|
||||
foreach (explode(',', $questionids) as $key => $id) {
|
||||
if ($id == $questionid) {
|
||||
return $key + 1;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Functions for sending notification messages /////////////////////////////////
|
||||
|
||||
/**
|
||||
|
||||
@@ -123,9 +123,9 @@ class quiz_grading_report extends quiz_default_report {
|
||||
$this->currentgroup, '', false);
|
||||
}
|
||||
|
||||
$questionsinquiz = quiz_questions_in_quiz($quiz->questions);
|
||||
$hasquestions = quiz_has_questions($quiz->id);
|
||||
$counts = null;
|
||||
if ($slot && $questionsinquiz) {
|
||||
if ($slot && $hasquestions) {
|
||||
// Make sure there is something to do.
|
||||
$statecounts = $this->get_question_state_summary(array($slot));
|
||||
foreach ($statecounts as $record) {
|
||||
@@ -144,7 +144,7 @@ class quiz_grading_report extends quiz_default_report {
|
||||
$this->print_header_and_tabs($cm, $course, $quiz, 'grading');
|
||||
|
||||
// What sort of page to display?
|
||||
if (!$questionsinquiz) {
|
||||
if (!$hasquestions) {
|
||||
echo quiz_no_questions_message($quiz, $cm, $this->context);
|
||||
|
||||
} else if (!$slot) {
|
||||
|
||||
@@ -102,7 +102,7 @@ class quiz_overview_report extends quiz_attempts_report {
|
||||
}
|
||||
}
|
||||
|
||||
$hasquestions = quiz_questions_in_quiz($quiz->questions);
|
||||
$hasquestions = quiz_has_questions($quiz->id);
|
||||
if (!$table->is_downloading()) {
|
||||
if (!$hasquestions) {
|
||||
echo quiz_no_questions_message($quiz, $cm, $this->context);
|
||||
|
||||
@@ -77,6 +77,15 @@ function quiz_report_unindex($datum) {
|
||||
return $datumunkeyed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are there any questions in this quiz?
|
||||
* @param int $quizid the quiz id.
|
||||
*/
|
||||
function quiz_has_questions($quizid) {
|
||||
global $DB;
|
||||
return $DB->record_exists('quiz_slots', array('quizid' => $quizid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the slots of real questions (not descriptions) in this quiz, in order.
|
||||
* @param object $quiz the quiz.
|
||||
@@ -86,41 +95,23 @@ function quiz_report_unindex($datum) {
|
||||
function quiz_report_get_significant_questions($quiz) {
|
||||
global $DB;
|
||||
|
||||
$questionids = quiz_questions_in_quiz($quiz->questions);
|
||||
if (empty($questionids)) {
|
||||
return array();
|
||||
}
|
||||
$qsbyslot = $DB->get_records_sql("
|
||||
SELECT slot.slot,
|
||||
q.id,
|
||||
q.length,
|
||||
slot.maxmark
|
||||
|
||||
list($usql, $params) = $DB->get_in_or_equal(explode(',', $questionids));
|
||||
$params[] = $quiz->id;
|
||||
$questions = $DB->get_records_sql("
|
||||
SELECT
|
||||
q.id,
|
||||
q.length,
|
||||
qqi.maxmark
|
||||
FROM {question} q
|
||||
JOIN {quiz_slots} slot ON slot.questionid = q.id
|
||||
|
||||
FROM {question} q
|
||||
JOIN {quiz_question_instances} qqi ON qqi.questionid = q.id
|
||||
WHERE slot.quizid = ?
|
||||
AND q.length > 0
|
||||
|
||||
WHERE
|
||||
q.id $usql AND
|
||||
qqi.quizid = ? AND
|
||||
q.length > 0", $params);
|
||||
ORDER BY slot.slot", array($quiz->id));
|
||||
|
||||
$qsbyslot = array();
|
||||
$number = 1;
|
||||
foreach (explode(',', $questionids) as $key => $id) {
|
||||
if (!array_key_exists($id, $questions)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$slot = $key + 1;
|
||||
$question = $questions[$id];
|
||||
$question->slot = $slot;
|
||||
foreach ($qsbyslot as $question) {
|
||||
$question->number = $number;
|
||||
|
||||
$qsbyslot[$slot] = $question;
|
||||
|
||||
$number += $question->length;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ class quiz_responses_report extends quiz_attempts_report {
|
||||
}
|
||||
}
|
||||
|
||||
$hasquestions = quiz_questions_in_quiz($quiz->questions);
|
||||
$hasquestions = quiz_has_questions($quiz->id);
|
||||
if (!$table->is_downloading()) {
|
||||
if (!$hasquestions) {
|
||||
echo quiz_no_questions_message($quiz, $cm, $this->context);
|
||||
|
||||
@@ -59,7 +59,7 @@ class quiz_statistics_report extends quiz_default_report {
|
||||
|
||||
$this->context = context_module::instance($cm->id);
|
||||
|
||||
if (!quiz_questions_in_quiz($quiz->questions)) {
|
||||
if (!quiz_has_questions($quiz->id)) {
|
||||
$this->print_header_and_tabs($cm, $course, $quiz, 'statistics');
|
||||
echo quiz_no_questions_message($quiz, $cm, $this->context);
|
||||
return true;
|
||||
|
||||
@@ -113,10 +113,7 @@ class mod_quiz_attempt_walkthrough_from_csv_testcase extends advanced_testcase {
|
||||
$this->randqids = array();
|
||||
foreach ($slots as $slotno => $slotquestion) {
|
||||
if ($slotquestion['type'] !== 'random') {
|
||||
quiz_add_quiz_question($slotquestion['id'], $this->quiz, 0);
|
||||
// Setting default mark above does not affect the grade for multi-answer question type (and maybe others??).
|
||||
// Set the mark again just to be sure.
|
||||
quiz_update_question_instance($slotquestion['mark'], $slotquestion['id'], $this->quiz);
|
||||
quiz_add_quiz_question($slotquestion['id'], $this->quiz, 0, $slotquestion['mark']);
|
||||
} else {
|
||||
quiz_add_random_questions($this->quiz, 0, $slotquestion['catid'], 1, 0);
|
||||
$this->randqids[$slotno] = $qidsbycat[$slotquestion['catid']];
|
||||
|
||||
@@ -79,6 +79,7 @@ class mod_quiz_attempt_walkthrough_testcase extends advanced_testcase {
|
||||
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $user1->id);
|
||||
|
||||
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
|
||||
$this->assertEquals('1,2,0', $attempt->layout);
|
||||
|
||||
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||
|
||||
@@ -132,7 +133,7 @@ class mod_quiz_attempt_walkthrough_testcase extends advanced_testcase {
|
||||
// Make a quiz.
|
||||
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
|
||||
|
||||
$quiz = $quizgenerator->create_instance(array('course'=>$SITE->id, 'questionsperpage' => 0, 'grade' => 100.0,
|
||||
$quiz = $quizgenerator->create_instance(array('course' => $SITE->id, 'questionsperpage' => 2, 'grade' => 100.0,
|
||||
'sumgrades' => 4));
|
||||
|
||||
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
@@ -174,6 +175,7 @@ class mod_quiz_attempt_walkthrough_testcase extends advanced_testcase {
|
||||
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow);
|
||||
|
||||
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow, array(1 => $randomqidtoselect));
|
||||
$this->assertEquals('1,2,0,3,4,0', $attempt->layout);
|
||||
|
||||
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||
|
||||
@@ -266,8 +268,10 @@ class mod_quiz_attempt_walkthrough_testcase extends advanced_testcase {
|
||||
|
||||
$timenow = time();
|
||||
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow);
|
||||
|
||||
// Select variant.
|
||||
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow, array(), array(1 => $variantno));
|
||||
$this->assertEquals('1,0', $attempt->layout);
|
||||
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||
|
||||
// Process some responses from the student.
|
||||
|
||||
@@ -37,52 +37,6 @@ require_once($CFG->dirroot . '/mod/quiz/editlib.php');
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_quiz_editlib_testcase extends basic_testcase {
|
||||
public function test_quiz_move_question_up() {
|
||||
$this->assertEquals(quiz_move_question_up('0', 123), '0');
|
||||
$this->assertEquals(quiz_move_question_up('1,2,0', 1), '1,2,0');
|
||||
$this->assertEquals(quiz_move_question_up('1,2,0', 0), '1,2,0');
|
||||
$this->assertEquals(quiz_move_question_up('1,2,0', 2), '2,1,0');
|
||||
$this->assertEquals(quiz_move_question_up('1,2,0,3,4,0', 3), '1,2,3,0,4,0');
|
||||
$this->assertEquals(quiz_move_question_up('1,2,3,0,4,0', 4), '1,2,3,4,0,0');
|
||||
}
|
||||
|
||||
public function test_quiz_move_question_down() {
|
||||
$this->assertEquals(quiz_move_question_down('0', 123), '0');
|
||||
$this->assertEquals(quiz_move_question_down('1,2,0', 2), '1,2,0');
|
||||
$this->assertEquals(quiz_move_question_down('1,2,0', 0), '1,2,0');
|
||||
$this->assertEquals(quiz_move_question_down('1,2,0', 1), '2,1,0');
|
||||
$this->assertEquals(quiz_move_question_down('1,2,0,3,4,0', 2), '1,0,2,3,4,0');
|
||||
$this->assertEquals(quiz_move_question_down('1,0,2,3,0,4,0', 1), '0,1,2,3,0,4,0');
|
||||
}
|
||||
|
||||
public function test_quiz_delete_empty_page() {
|
||||
$this->assertEquals(quiz_delete_empty_page('0', 0), '0');
|
||||
$this->assertEquals(quiz_delete_empty_page('1,2,0', 2), '1,2,0');
|
||||
$this->assertEquals(quiz_delete_empty_page('0,1,2,0', -1), '1,2,0');
|
||||
$this->assertEquals(quiz_delete_empty_page('0,1,2,0', 0), '0,1,2,0');
|
||||
$this->assertEquals(quiz_delete_empty_page('1,2,0', 3), '1,2,0');
|
||||
$this->assertEquals(quiz_delete_empty_page('1,2,0', -1), '1,2,0');
|
||||
$this->assertEquals(quiz_delete_empty_page('1,2,0,0', 2), '1,2,0');
|
||||
$this->assertEquals(quiz_delete_empty_page('1,2,0,0', 1), '1,2,0,0');
|
||||
$this->assertEquals(quiz_delete_empty_page('1,2,0,0,3,4,0', 2), '1,2,0,3,4,0');
|
||||
$this->assertEquals(quiz_delete_empty_page('0,0,1,2,0', 0), '0,1,2,0');
|
||||
}
|
||||
|
||||
public function test_quiz_add_page_break_after() {
|
||||
$this->assertEquals(quiz_add_page_break_after('0', 1), '0');
|
||||
$this->assertEquals(quiz_add_page_break_after('1,2,0', 1), '1,0,2,0');
|
||||
$this->assertEquals(quiz_add_page_break_after('1,2,0', 2), '1,2,0,0');
|
||||
$this->assertEquals(quiz_add_page_break_after('1,2,0', 0), '1,2,0');
|
||||
}
|
||||
|
||||
public function test_quiz_add_page_break_at() {
|
||||
$this->assertEquals(quiz_add_page_break_at('0', 0), '0,0');
|
||||
$this->assertEquals(quiz_add_page_break_at('1,2,0', 0), '0,1,2,0');
|
||||
$this->assertEquals(quiz_add_page_break_at('1,2,0', 1), '1,0,2,0');
|
||||
$this->assertEquals(quiz_add_page_break_at('1,2,0', 2), '1,2,0,0');
|
||||
$this->assertEquals(quiz_add_page_break_at('1,2,0', 3), '1,2,0');
|
||||
}
|
||||
|
||||
public function test_quiz_question_tostring() {
|
||||
$question = new stdClass();
|
||||
$question->qtype = 'multichoice';
|
||||
|
||||
@@ -52,7 +52,6 @@ class mod_quiz_generator extends testing_module_generator {
|
||||
'questionsperpage' => 1,
|
||||
'shufflequestions' => 0,
|
||||
'shuffleanswers' => 1,
|
||||
'questions' => '',
|
||||
'sumgrades' => 0,
|
||||
'grade' => 0,
|
||||
'timecreated' => time(),
|
||||
|
||||
@@ -37,96 +37,6 @@ require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_quiz_locallib_testcase extends basic_testcase {
|
||||
public function test_quiz_questions_in_quiz() {
|
||||
$this->assertEquals(quiz_questions_in_quiz(''), '');
|
||||
$this->assertEquals(quiz_questions_in_quiz('0'), '');
|
||||
$this->assertEquals(quiz_questions_in_quiz('0,0'), '');
|
||||
$this->assertEquals(quiz_questions_in_quiz('0,0,0'), '');
|
||||
$this->assertEquals(quiz_questions_in_quiz('1'), '1');
|
||||
$this->assertEquals(quiz_questions_in_quiz('1,2'), '1,2');
|
||||
$this->assertEquals(quiz_questions_in_quiz('1,0,2'), '1,2');
|
||||
$this->assertEquals(quiz_questions_in_quiz('0,1,0,0,2,0'), '1,2');
|
||||
}
|
||||
|
||||
public function test_quiz_number_of_pages() {
|
||||
$this->assertEquals(quiz_number_of_pages('0'), 1);
|
||||
$this->assertEquals(quiz_number_of_pages('0,0'), 2);
|
||||
$this->assertEquals(quiz_number_of_pages('0,0,0'), 3);
|
||||
$this->assertEquals(quiz_number_of_pages('1,0'), 1);
|
||||
$this->assertEquals(quiz_number_of_pages('1,2,0'), 1);
|
||||
$this->assertEquals(quiz_number_of_pages('1,0,2,0'), 2);
|
||||
$this->assertEquals(quiz_number_of_pages('1,2,3,0'), 1);
|
||||
$this->assertEquals(quiz_number_of_pages('1,2,3,0'), 1);
|
||||
$this->assertEquals(quiz_number_of_pages('0,1,0,0,2,0'), 4);
|
||||
}
|
||||
|
||||
public function test_quiz_number_of_questions_in_quiz() {
|
||||
$this->assertEquals(quiz_number_of_questions_in_quiz('0'), 0);
|
||||
$this->assertEquals(quiz_number_of_questions_in_quiz('0,0'), 0);
|
||||
$this->assertEquals(quiz_number_of_questions_in_quiz('0,0,0'), 0);
|
||||
$this->assertEquals(quiz_number_of_questions_in_quiz('1,0'), 1);
|
||||
$this->assertEquals(quiz_number_of_questions_in_quiz('1,2,0'), 2);
|
||||
$this->assertEquals(quiz_number_of_questions_in_quiz('1,0,2,0'), 2);
|
||||
$this->assertEquals(quiz_number_of_questions_in_quiz('1,2,3,0'), 3);
|
||||
$this->assertEquals(quiz_number_of_questions_in_quiz('1,2,3,0'), 3);
|
||||
$this->assertEquals(quiz_number_of_questions_in_quiz('0,1,0,0,2,0'), 2);
|
||||
$this->assertEquals(quiz_number_of_questions_in_quiz('10,,0,0'), 1);
|
||||
}
|
||||
|
||||
public function test_quiz_clean_layout() {
|
||||
// Without stripping empty pages.
|
||||
$this->assertEquals(quiz_clean_layout(',,1,,,2,,'), '1,2,0');
|
||||
$this->assertEquals(quiz_clean_layout(''), '0');
|
||||
$this->assertEquals(quiz_clean_layout('0'), '0');
|
||||
$this->assertEquals(quiz_clean_layout('0,0'), '0,0');
|
||||
$this->assertEquals(quiz_clean_layout('0,0,0'), '0,0,0');
|
||||
$this->assertEquals(quiz_clean_layout('1'), '1,0');
|
||||
$this->assertEquals(quiz_clean_layout('1,2'), '1,2,0');
|
||||
$this->assertEquals(quiz_clean_layout('1,0,2'), '1,0,2,0');
|
||||
$this->assertEquals(quiz_clean_layout('0,1,0,0,2,0'), '0,1,0,0,2,0');
|
||||
|
||||
// With stripping empty pages.
|
||||
$this->assertEquals(quiz_clean_layout('', true), '0');
|
||||
$this->assertEquals(quiz_clean_layout('0', true), '0');
|
||||
$this->assertEquals(quiz_clean_layout('0,0', true), '0');
|
||||
$this->assertEquals(quiz_clean_layout('0,0,0', true), '0');
|
||||
$this->assertEquals(quiz_clean_layout('1', true), '1,0');
|
||||
$this->assertEquals(quiz_clean_layout('1,2', true), '1,2,0');
|
||||
$this->assertEquals(quiz_clean_layout('1,0,2', true), '1,0,2,0');
|
||||
$this->assertEquals(quiz_clean_layout('0,1,0,0,2,0', true), '1,0,2,0');
|
||||
}
|
||||
|
||||
public function test_quiz_repaginate() {
|
||||
// Test starting with 1 question per page.
|
||||
$this->assertEquals(quiz_repaginate('1,0,2,0,3,0', 0), '1,2,3,0');
|
||||
$this->assertEquals(quiz_repaginate('1,0,2,0,3,0', 3), '1,2,3,0');
|
||||
$this->assertEquals(quiz_repaginate('1,0,2,0,3,0', 2), '1,2,0,3,0');
|
||||
$this->assertEquals(quiz_repaginate('1,0,2,0,3,0', 1), '1,0,2,0,3,0');
|
||||
|
||||
// Test starting with all on one page page.
|
||||
$this->assertEquals(quiz_repaginate('1,2,3,0', 0), '1,2,3,0');
|
||||
$this->assertEquals(quiz_repaginate('1,2,3,0', 3), '1,2,3,0');
|
||||
$this->assertEquals(quiz_repaginate('1,2,3,0', 2), '1,2,0,3,0');
|
||||
$this->assertEquals(quiz_repaginate('1,2,3,0', 1), '1,0,2,0,3,0');
|
||||
|
||||
// Test single question case.
|
||||
$this->assertEquals(quiz_repaginate('100,0', 0), '100,0');
|
||||
$this->assertEquals(quiz_repaginate('100,0', 1), '100,0');
|
||||
|
||||
// No questions case.
|
||||
$this->assertEquals(quiz_repaginate('0', 0), '0');
|
||||
|
||||
// Test empty pages are removed.
|
||||
$this->assertEquals(quiz_repaginate('1,2,3,0,0,0', 0), '1,2,3,0');
|
||||
$this->assertEquals(quiz_repaginate('1,0,0,0,2,3,0', 0), '1,2,3,0');
|
||||
$this->assertEquals(quiz_repaginate('0,0,0,1,2,3,0', 0), '1,2,3,0');
|
||||
|
||||
// Test shuffle option.
|
||||
$this->assertTrue(in_array(quiz_repaginate('1,2,0', 0, true),
|
||||
array('1,2,0', '2,1,0')));
|
||||
$this->assertTrue(in_array(quiz_repaginate('1,2,0', 1, true),
|
||||
array('1,0,2,0', '2,0,1,0')));
|
||||
}
|
||||
|
||||
public function test_quiz_rescale_grade() {
|
||||
$quiz = new stdClass();
|
||||
@@ -145,13 +55,6 @@ class mod_quiz_locallib_testcase extends basic_testcase {
|
||||
format_float(0.247, 3));
|
||||
}
|
||||
|
||||
public function test_quiz_get_slot_for_question() {
|
||||
$quiz = new stdClass();
|
||||
$quiz->questions = '1,2,0,7,0';
|
||||
$this->assertEquals(1, quiz_get_slot_for_question($quiz, 1));
|
||||
$this->assertEquals(3, quiz_get_slot_for_question($quiz, 7));
|
||||
}
|
||||
|
||||
public function test_quiz_attempt_state_in_progress() {
|
||||
$attempt = new stdClass();
|
||||
$attempt->state = quiz_attempt::IN_PROGRESS;
|
||||
|
||||
@@ -42,7 +42,6 @@ class mod_quiz_class_testcase extends basic_testcase {
|
||||
$quiz->reviewattempt = 0x10010;
|
||||
$quiz->timeclose = 0;
|
||||
$quiz->attempts = 0;
|
||||
$quiz->questions = '1,2,0,3,4,0';
|
||||
|
||||
$cm = new stdClass();
|
||||
$cm->id = 123;
|
||||
@@ -65,19 +64,4 @@ class mod_quiz_class_testcase extends basic_testcase {
|
||||
$this->assertEquals(get_string('noreviewuntil', 'quiz', userdate($closetime)),
|
||||
$quizobj->cannot_review_message(mod_quiz_display_options::LATER_WHILE_OPEN));
|
||||
}
|
||||
|
||||
public function test_empty_quiz() {
|
||||
$quiz = new stdClass();
|
||||
$quiz->reviewattempt = 0x10010;
|
||||
$quiz->timeclose = 0;
|
||||
$quiz->attempts = 0;
|
||||
$quiz->questions = '0';
|
||||
|
||||
$cm = new stdClass();
|
||||
$cm->id = 123;
|
||||
|
||||
$quizobj = new quiz($quiz, $cm, new stdClass(), false);
|
||||
|
||||
$this->assertFalse($quizobj->has_questions());
|
||||
}
|
||||
}
|
||||
|
||||
+24
-2
@@ -2,14 +2,36 @@ This files describes API changes in the quiz code.
|
||||
|
||||
=== 2.7 ===
|
||||
|
||||
* The columns of the quiz_question_instances table have been renamed to match
|
||||
the coding guidelines. Specifically
|
||||
* The old quiz.questions database column (comma-separated list of question ids)
|
||||
is gone, and instead the quiz_question_instances table has been renamed to
|
||||
to quiz_slots. Some of the columns of that table have been renamed to match
|
||||
the coding guidelines. Specifically:
|
||||
quiz -> quizid
|
||||
question -> questionid
|
||||
grade -> maxmark
|
||||
also there are two new columns:
|
||||
slot - numbers the questions in the quiz in order, as on the edit quiz page.
|
||||
page - new way to determine which question is on which page.
|
||||
naturally, other parts of the code and APIs have been updated to reflect that
|
||||
change.
|
||||
|
||||
* The following functions, which were part of the internal workings of the quiz,
|
||||
have been removed.
|
||||
quiz_get_slot_for_question
|
||||
quiz_number_of_questions_in_quiz
|
||||
quiz_repaginate (there is now a quiz_repaginate_questions with a different API).
|
||||
quiz_add_page_break_at (see quiz_add_page_break_after_slot)
|
||||
quiz_add_page_break_after (see quiz_add_page_break_after_slot)
|
||||
quiz_number_of_pages
|
||||
quiz_remove_question (see quiz_remove_slot)
|
||||
quiz_update_question_instance (see quiz_update_slot_maxmark)
|
||||
|
||||
* The following internal functions have had their API changed.
|
||||
quiz_delete_empty_page: has had its arguments changed to $quiz and $pagenumber.
|
||||
quiz_has_question_use: now takes $quiz and $slot, not $questionid.
|
||||
|
||||
=== 2.6 ===
|
||||
|
||||
* As part of improving the page usability and accessibility, we updated the
|
||||
heading levels for quiz module so it has a proper nesting. (MDL-41615)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2014021300; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2014022008; // The current module version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2013110500; // Requires this Moodle version.
|
||||
$plugin->component = 'mod_quiz'; // Full name of the plugin (used for diagnostics).
|
||||
$plugin->cron = 60;
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ if ($quiz->attempts != 1) {
|
||||
}
|
||||
|
||||
// Determine wheter a start attempt button should be displayed.
|
||||
$viewobj->quizhasquestions = (bool) quiz_clean_layout($quiz->questions, true);
|
||||
$viewobj->quizhasquestions = $quizobj->has_questions();
|
||||
$viewobj->preventmessages = array();
|
||||
if (!$viewobj->quizhasquestions) {
|
||||
$viewobj->buttontext = '';
|
||||
|
||||
@@ -286,10 +286,10 @@ class question_engine_upgrade_question_loader {
|
||||
|
||||
if ($quizid) {
|
||||
$question = $DB->get_record_sql("
|
||||
SELECT q.*, qqi.maxmark
|
||||
SELECT q.*, slot.maxmark
|
||||
FROM {question} q
|
||||
JOIN {quiz_question_instances} qqi ON qqi.questionid = q.id
|
||||
WHERE q.id = ? AND qqi.quizid = ?", array($questionid, $quizid));
|
||||
JOIN {quiz_slots} slot ON slot.questionid = q.id
|
||||
WHERE q.id = ? AND slot.quizid = ?", array($questionid, $quizid));
|
||||
} else {
|
||||
$question = $DB->get_record('question', array('id' => $questionid));
|
||||
}
|
||||
|
||||
@@ -1706,12 +1706,12 @@ class qtype_calculated extends question_type {
|
||||
$line++;
|
||||
$text .= "<td align=\"left\" style=\"white-space:nowrap;\">$qu->name</td>";
|
||||
// TODO MDL-43779 should not have quiz-specific code here.
|
||||
$nbofquiz = $DB->count_records('quiz_question_instances', array('questionid' => $qu->id));
|
||||
$nbofquiz = $DB->count_records('quiz_slots', array('questionid' => $qu->id));
|
||||
$nbofattempts = $DB->count_records_sql("
|
||||
SELECT count(1)
|
||||
FROM {quiz_question_instances} qqi
|
||||
JOIN {quiz_attempts} quiza ON quiza.quiz = qqi.quizid
|
||||
WHERE qqi.questionid = ?
|
||||
FROM {quiz_slots} slot
|
||||
JOIN {quiz_attempts} quiza ON quiza.quiz = slot.quizid
|
||||
WHERE slot.questionid = ?
|
||||
AND quiza.preview = 0", array($qu->id));
|
||||
if ($nbofquiz > 0) {
|
||||
$text .= "<td align=\"center\">$nbofquiz</td>";
|
||||
|
||||
@@ -55,7 +55,6 @@ class qtype_calculated_attempt_upgrader_test extends question_attempt_upgrader_t
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '16,0,17,0,18,0',
|
||||
'sumgrades' => '3.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -254,7 +253,6 @@ Remember to type a unit.',
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '16,0,17,0,18,0',
|
||||
'sumgrades' => '3.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -496,7 +494,6 @@ Remember to type a unit.',
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '16,0,17,0,18,0',
|
||||
'sumgrades' => '3.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
|
||||
@@ -55,7 +55,6 @@ class qtype_calculatedmulti_attempt_upgrader_test extends question_attempt_upgra
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '16,0,17,0,18,0',
|
||||
'sumgrades' => '3.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -276,7 +275,6 @@ class qtype_calculatedmulti_attempt_upgrader_test extends question_attempt_upgra
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '16,0,17,0,18,0',
|
||||
'sumgrades' => '3.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -517,7 +515,6 @@ class qtype_calculatedmulti_attempt_upgrader_test extends question_attempt_upgra
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '16,0,17,0,18,0',
|
||||
'sumgrades' => '3.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
|
||||
@@ -55,7 +55,6 @@ class qtype_calculatedsimple_attempt_upgrader_test extends question_attempt_upgr
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '16,0,17,0,18,0',
|
||||
'sumgrades' => '3.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -241,7 +240,6 @@ class qtype_calculatedsimple_attempt_upgrader_test extends question_attempt_upgr
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '16,0,17,0,18,0',
|
||||
'sumgrades' => '3.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -467,7 +465,6 @@ class qtype_calculatedsimple_attempt_upgrader_test extends question_attempt_upgr
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '16,0,17,0,18,0',
|
||||
'sumgrades' => '3.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
|
||||
@@ -60,7 +60,6 @@ class qtype_description_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '4940,0,5043,0,4945,0,4942,0,5566,0',
|
||||
'sumgrades' => '5',
|
||||
'grade' => '10',
|
||||
'timecreated' => '0',
|
||||
@@ -206,7 +205,6 @@ class qtype_description_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '8492,0,8487,8488,8489,8490,0,8441,0,8443,0,8486,0,8444,0,8445,0,8494,0,8446,8429,0,8447,8430,0,8448,8431,0,8449,8432,0,8450,8433,0,8451,8434,0,8452,8435,0,8453,8436,0,8454,8437,0,8493,0,8455,8456,8457,0,8458,8459,8460,0,8461,8462,8463,8438,0,8464,8465,8466,0,8467,8468,8469,0,8470,8471,8472,8439,0,8473,8440,0,8474,8475,8476,0,8477,8478,8479,0,8480,8481,8482,0,8483,8484,8485,8442,0',
|
||||
'sumgrades' => '0',
|
||||
'grade' => '10',
|
||||
'timecreated' => '0',
|
||||
@@ -355,7 +353,6 @@ class qtype_description_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '8691,0,8690,8692,8693,8694,0,8695,0,8696,0,8697,0,8698,0,8699,0,8700,0,8701,8702,0,8703,8704,0,8705,8706,0,8707,8708,0,8709,8710,0,8711,8712,0,8713,8714,0,8715,8716,0,8717,8718,0,8719,0,8720,8721,8722,0,8723,8724,8725,0,8726,8727,8728,8729,0,8730,8731,8732,0,8733,8734,8735,0,8736,8737,8738,8739,0,8740,8741,0,8742,8743,8744,0,8745,8746,8747,0,8748,8749,8750,0,8752,8751,8753,8754,0',
|
||||
'sumgrades' => '0',
|
||||
'grade' => '10',
|
||||
'timecreated' => '0',
|
||||
|
||||
@@ -64,7 +64,6 @@ class qtype_essay_attempt_upgrader_test extends question_attempt_upgrader_test_b
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '90042,0,90043,0,90045,0,90052,0,90053,0,90054,0,90055,0,90056,0,90057,0,90058,0,90059,0,90046,0,90044,0,90047,0,90048,0,90049,0',
|
||||
'sumgrades' => '100',
|
||||
'grade' => '100',
|
||||
'timecreated' => '0',
|
||||
@@ -295,7 +294,6 @@ class qtype_essay_attempt_upgrader_test extends question_attempt_upgrader_test_b
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '3664,3716,0,3663,3717,0,3718,3719,0,3720,0,3733,3727,0,3728,3730,0,3731,3732,0,3726,3729,0',
|
||||
'sumgrades' => '0',
|
||||
'grade' => '0',
|
||||
'timecreated' => '0',
|
||||
@@ -475,7 +473,6 @@ class qtype_essay_attempt_upgrader_test extends question_attempt_upgrader_test_b
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '3664,3716,0,3663,3717,0,3718,3719,0,3720,0,3733,3727,0,3728,3730,0,3731,3732,0,3726,3729,0',
|
||||
'sumgrades' => '0',
|
||||
'grade' => '0',
|
||||
'timecreated' => '0',
|
||||
|
||||
@@ -59,7 +59,6 @@ class qtype_match_attempt_upgrader_test extends question_attempt_upgrader_test_b
|
||||
'questionsperpage' => '2',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '689,690,0,691,692,0,693,694,0,695,696,0,697,698,0',
|
||||
'sumgrades' => '48',
|
||||
'grade' => '48',
|
||||
'timecreated' => '0',
|
||||
@@ -317,7 +316,6 @@ class qtype_match_attempt_upgrader_test extends question_attempt_upgrader_test_b
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '509,0,510,0,511,0,738,0,514,0',
|
||||
'sumgrades' => '5',
|
||||
'grade' => '10',
|
||||
'timecreated' => '0',
|
||||
@@ -527,7 +525,6 @@ class qtype_match_attempt_upgrader_test extends question_attempt_upgrader_test_b
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '11163,0,11164,0,11165,0,11135,0,11166,0',
|
||||
'sumgrades' => '5',
|
||||
'grade' => '10',
|
||||
'timecreated' => '0',
|
||||
|
||||
@@ -65,13 +65,13 @@ class qtype_multianswer_edit_form extends question_edit_form {
|
||||
if (isset($question->id) && $question->id != 0) {
|
||||
// TODO MDL-43779 should not have quiz-specific code here.
|
||||
$this->savedquestiondisplay = fullclone($question);
|
||||
$this->nb_of_quiz = $DB->count_records('quiz_question_instances', array('questionid' => $question->id));
|
||||
$this->nb_of_quiz = $DB->count_records('quiz_slots', array('questionid' => $question->id));
|
||||
$this->used_in_quiz = $this->nb_of_quiz > 0;
|
||||
$this->nb_of_attempts = $DB->count_records_sql("
|
||||
SELECT count(1)
|
||||
FROM {quiz_question_instances} qqi
|
||||
JOIN {quiz_attempts} quiza ON quiza.quiz = qqi.quizid
|
||||
WHERE qqi.questionid = ?
|
||||
FROM {quiz_slots} slot
|
||||
JOIN {quiz_attempts} quiza ON quiza.quiz = slot.quizid
|
||||
WHERE slot.questionid = ?
|
||||
AND quiza.preview = 0", array($question->id));
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ class qtype_multianswer_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '28,19,0',
|
||||
'sumgrades' => '14.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -260,7 +259,6 @@ class qtype_multianswer_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '28,19,0',
|
||||
'sumgrades' => '14.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -443,7 +441,6 @@ class qtype_multianswer_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '28,19,0',
|
||||
'sumgrades' => '14.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -668,7 +665,6 @@ class qtype_multianswer_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '28,19,0',
|
||||
'sumgrades' => '14.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -1354,7 +1350,6 @@ b) What grade would you give it? _____',
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '28,19,0',
|
||||
'sumgrades' => '14.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -2012,7 +2007,6 @@ b) What grade would you give it? _____',
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '28,19,0',
|
||||
'sumgrades' => '14.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
|
||||
@@ -59,7 +59,6 @@ class qtype_multichoice_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '0',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '1,0',
|
||||
'timecreated' => '0',
|
||||
'timemodified' => '1278603396',
|
||||
'timelimit' => '0',
|
||||
@@ -296,7 +295,6 @@ class qtype_multichoice_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '0',
|
||||
'shufflequestions' => '1',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '71,72,73,89,74,75,76,92,77,78,79,93,80,90,81,91,94,95,82,83,84,85,86,87,88,0',
|
||||
'sumgrades' => '25',
|
||||
'grade' => '25',
|
||||
'timecreated' => '0',
|
||||
@@ -464,7 +462,6 @@ class qtype_multichoice_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '0',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '218,221,219,220,223,224,222,0',
|
||||
'sumgrades' => '7',
|
||||
'grade' => '0',
|
||||
'timecreated' => '0',
|
||||
@@ -659,7 +656,6 @@ class qtype_multichoice_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '2',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '2855,2856,0,2857,2858,0,2859,2860,0,2861,2862,0,2863,2864,0,2865,0',
|
||||
'sumgrades' => '59',
|
||||
'grade' => '59',
|
||||
'timecreated' => '0',
|
||||
@@ -898,7 +894,6 @@ class qtype_multichoice_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '0',
|
||||
'shufflequestions' => '1',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '71,72,73,89,74,75,76,92,77,78,79,93,80,90,81,91,94,95,82,83,84,85,86,87,88,0',
|
||||
'sumgrades' => '25',
|
||||
'grade' => '25',
|
||||
'timecreated' => '0',
|
||||
@@ -1032,7 +1027,6 @@ public function test_multichoice_deferredfeedback_qsession140() {
|
||||
'questionsperpage' => '2',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '178,179,0,180,181,0,182,183,0,184,185,0,189,187,0',
|
||||
'sumgrades' => '10',
|
||||
'grade' => '10',
|
||||
'timecreated' => '0',
|
||||
@@ -1236,7 +1230,6 @@ public function test_multichoice_deferredfeedback_qsession140() {
|
||||
'questionsperpage' => '2',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '26132,26128,0,26143,26140,0,26144,26141,0,26145,26142,0,26126,26127,0,26134,26135,0,26131,26133,0,26130,26129,0,26136,26137,0,26139,26138,0',
|
||||
'sumgrades' => '0',
|
||||
'grade' => '0',
|
||||
'timecreated' => '0',
|
||||
@@ -1712,7 +1705,6 @@ public function test_multichoice_deferredfeedback_qsession140() {
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '161,0,160,0,162,0,163,0,164,0,165,0,166,0,190,0,191,0,192,0,193,0,194,0,195,0,196,0,197,0,198,0,168,0,169,0,170,0,171,0,172,0,173,0,174,0,175,0,176,0,177,0,178,0,179,0,180,0,181,0',
|
||||
'sumgrades' => '30',
|
||||
'grade' => '10',
|
||||
'timecreated' => '0',
|
||||
@@ -1950,7 +1942,6 @@ public function test_multichoice_deferredfeedback_qsession140() {
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '89002,0,89040,0,89042,0,89043,0,89044,0,89045,0,89046,0,89047,0',
|
||||
'sumgrades' => '8',
|
||||
'grade' => '8',
|
||||
'timecreated' => '0',
|
||||
@@ -2154,7 +2145,6 @@ public function test_multichoice_deferredfeedback_qsession140() {
|
||||
'questionsperpage' => '2',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '3859,3860,0,3861,3862,0,3863,3864,0,3865,3866,0,3867,3868,0',
|
||||
'sumgrades' => '50',
|
||||
'grade' => '50',
|
||||
'timecreated' => '0',
|
||||
@@ -2376,7 +2366,6 @@ public function test_multichoice_deferredfeedback_qsession140() {
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '242,0,243,0,244,0,245,0,246,0,247,0',
|
||||
'sumgrades' => '0',
|
||||
'grade' => '0',
|
||||
'timecreated' => '0',
|
||||
@@ -2585,7 +2574,6 @@ public function test_multichoice_deferredfeedback_qsession140() {
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '242,0,243,0,244,0,245,0,246,0,247,0',
|
||||
'sumgrades' => '0',
|
||||
'grade' => '0',
|
||||
'timecreated' => '0',
|
||||
|
||||
@@ -60,7 +60,6 @@ class qtype_numerical_attempt_upgrader_test extends question_attempt_upgrader_te
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '4184,0,4185,0,4154,0,4186,0,4187,0,4188,0,4189,0,4190,0,4162,0,4191,0,4192,0,4193,0,4254,0,4195,0,4196,0,4163,0,4197,0,4198,0,4199,0,4164,0,4200,0,4165,0,4201,0,4202,0,4166,0,4203,0,4204,0,4205,0,4167,0,4155,0,4168,0,4206,0,4207,0,4208,0,4209,0,4210,0,4211,0,4212,0,4213,0,4214,0,4156,0,4215,0,4216,0,4217,0,4169,0,4170,0,4157,0,4218,0,4219,0,4220,0,4171,0,4221,0,4172,0,4222,0,4223,0,4224,0,4225,0,4226,0,4227,0,4228,0,4173,0,4229,0,4230,0,4231,0,4232,0,4174,0,4233,0,4234,0,4235,0,4236,0,4237,0,4238,0,4239,0,4240,0,4241,0,4242,0,4158,0,4243,0,4244,0,4245,0,4246,0,4159,0,4175,0,4247,0,4176,0,4248,0,4177,0,4160,0,4249,0,4178,0,4250,0,4161,0,4251,0,4179,0,4252,0,4180,0,4181,0,4182,0,4253,0,4183,0',
|
||||
'sumgrades' => '100',
|
||||
'grade' => '100',
|
||||
'timecreated' => '0',
|
||||
@@ -283,7 +282,6 @@ class qtype_numerical_attempt_upgrader_test extends question_attempt_upgrader_te
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '6,12,13,15,14,0',
|
||||
'sumgrades' => '5.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
|
||||
@@ -62,7 +62,6 @@ class qtype_random_attempt_upgrader_test extends question_attempt_upgrader_test_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '28698,0,34245,0,34248,0,35005,0,35009,0,35013,0',
|
||||
'sumgrades' => '5',
|
||||
'grade' => '100',
|
||||
'timecreated' => '0',
|
||||
@@ -289,7 +288,6 @@ class qtype_random_attempt_upgrader_test extends question_attempt_upgrader_test_
|
||||
'questionsperpage' => '3',
|
||||
'shufflequestions' => '1',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '101983,101984,101985,0,101986,101987,101988,0,101989,101990,101991,0,101992,101993,101994,0,101995,101996,101997,0',
|
||||
'sumgrades' => '15',
|
||||
'grade' => '10',
|
||||
'timecreated' => '0',
|
||||
@@ -538,7 +536,6 @@ class qtype_random_attempt_upgrader_test extends question_attempt_upgrader_test_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '68646,0,81245,0,81246,0,81247,0,81248,0,81249,0,81250,0,82795,0,82797,0,82798,0,82799,0,82800,0,82801,0,82802,0,82803,0,82804,0,82805,0,82806,0,82807,0',
|
||||
'sumgrades' => '18',
|
||||
'grade' => '18',
|
||||
'timecreated' => '0',
|
||||
@@ -805,7 +802,6 @@ class qtype_random_attempt_upgrader_test extends question_attempt_upgrader_test_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '6,12,13,15,14,0',
|
||||
'sumgrades' => '5.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
|
||||
@@ -60,7 +60,6 @@ class qtype_shortanswer_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '4184,0,4185,0,4154,0,4186,0,4187,0,4188,0,4189,0,4190,0,4162,0,4191,0,4192,0,4193,0,4254,0,4195,0,4196,0,4163,0,4197,0,4198,0,4199,0,4164,0,4200,0,4165,0,4201,0,4202,0,4166,0,4203,0,4204,0,4205,0,4167,0,4155,0,4168,0,4206,0,4207,0,4208,0,4209,0,4210,0,4211,0,4212,0,4213,0,4214,0,4156,0,4215,0,4216,0,4217,0,4169,0,4170,0,4157,0,4218,0,4219,0,4220,0,4171,0,4221,0,4172,0,4222,0,4223,0,4224,0,4225,0,4226,0,4227,0,4228,0,4173,0,4229,0,4230,0,4231,0,4232,0,4174,0,4233,0,4234,0,4235,0,4236,0,4237,0,4238,0,4239,0,4240,0,4241,0,4242,0,4158,0,4243,0,4244,0,4245,0,4246,0,4159,0,4175,0,4247,0,4176,0,4248,0,4177,0,4160,0,4249,0,4178,0,4250,0,4161,0,4251,0,4179,0,4252,0,4180,0,4181,0,4182,0,4253,0,4183,0',
|
||||
'sumgrades' => '100',
|
||||
'grade' => '100',
|
||||
'timecreated' => '0',
|
||||
@@ -271,7 +270,6 @@ class qtype_shortanswer_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '0',
|
||||
'shufflequestions' => '1',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '10218,10216,10219,10220,10217,0',
|
||||
'sumgrades' => '5',
|
||||
'grade' => '5',
|
||||
'timecreated' => '0',
|
||||
@@ -444,7 +442,6 @@ class qtype_shortanswer_attempt_upgrader_test extends question_attempt_upgrader_
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '4184,0,4185,0,4154,0,4186,0,4187,0,4188,0,4189,0,4190,0,4162,0,4191,0,4192,0,4193,0,4254,0,4195,0,4196,0,4163,0,4197,0,4198,0,4199,0,4164,0,4200,0,4165,0,4201,0,4202,0,4166,0,4203,0,4204,0,4205,0,4167,0,4155,0,4168,0,4206,0,4207,0,4208,0,4209,0,4210,0,4211,0,4212,0,4213,0,4214,0,4156,0,4215,0,4216,0,4217,0,4169,0,4170,0,4157,0,4218,0,4219,0,4220,0,4171,0,4221,0,4172,0,4222,0,4223,0,4224,0,4225,0,4226,0,4227,0,4228,0,4173,0,4229,0,4230,0,4231,0,4232,0,4174,0,4233,0,4234,0,4235,0,4236,0,4237,0,4238,0,4239,0,4240,0,4241,0,4242,0,4158,0,4243,0,4244,0,4245,0,4246,0,4159,0,4175,0,4247,0,4176,0,4248,0,4177,0,4160,0,4249,0,4178,0,4250,0,4161,0,4251,0,4179,0,4252,0,4180,0,4181,0,4182,0,4253,0,4183,0',
|
||||
'sumgrades' => '100',
|
||||
'grade' => '100',
|
||||
'timecreated' => '0',
|
||||
|
||||
@@ -60,7 +60,6 @@ class qtype_truefalse_attempt_upgrader_test extends question_attempt_upgrader_te
|
||||
'showblocks' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '0',
|
||||
'questions' => '3859,3860,0,3861,3862,0,3863,3864,0,3865,3866,0,3867,3868,0',
|
||||
'sumgrades' => '50',
|
||||
'grade' => '50',
|
||||
'timecreated' => '0',
|
||||
@@ -250,7 +249,6 @@ class qtype_truefalse_attempt_upgrader_test extends question_attempt_upgrader_te
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '9043,0,9057,0,9062,0,9241,0',
|
||||
'sumgrades' => '4',
|
||||
'grade' => '4',
|
||||
'timecreated' => '0',
|
||||
@@ -429,7 +427,6 @@ class qtype_truefalse_attempt_upgrader_test extends question_attempt_upgrader_te
|
||||
'questionsperpage' => '0',
|
||||
'shufflequestions' => '1',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '96,98,104,106,101,108,111,102,113,0',
|
||||
'sumgrades' => '9',
|
||||
'grade' => '9',
|
||||
'timecreated' => '0',
|
||||
@@ -603,7 +600,6 @@ class qtype_truefalse_attempt_upgrader_test extends question_attempt_upgrader_te
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '30,0',
|
||||
'sumgrades' => '10.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -794,7 +790,6 @@ class qtype_truefalse_attempt_upgrader_test extends question_attempt_upgrader_te
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '30,0',
|
||||
'sumgrades' => '10.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
@@ -991,7 +986,6 @@ class qtype_truefalse_attempt_upgrader_test extends question_attempt_upgrader_te
|
||||
'questionsperpage' => '1',
|
||||
'shufflequestions' => '0',
|
||||
'shuffleanswers' => '1',
|
||||
'questions' => '1,0',
|
||||
'sumgrades' => '1.00000',
|
||||
'grade' => '10.00000',
|
||||
'timecreated' => '0',
|
||||
|
||||
Reference in New Issue
Block a user