diff --git a/public/mod/quiz/classes/question/bank/custom_view.php b/public/mod/quiz/classes/question/bank/custom_view.php index 567a9ebbda9..a1b1065d586 100644 --- a/public/mod/quiz/classes/question/bank/custom_view.php +++ b/public/mod/quiz/classes/question/bank/custom_view.php @@ -243,20 +243,11 @@ class custom_view extends \core_question\local\bank\view { } // Build the where clause. - $latestversion = 'qv.version = (SELECT MAX(v.version) - FROM {question_versions} v - JOIN {question_bank_entries} be - ON be.id = v.questionbankentryid - WHERE be.id = qbe.id AND v.status <> :substatus)'; - // An additional condition is required in the subquery to account for scenarios // where the latest version is hidden. This ensures we retrieve the previous // "Ready" version instead of the hidden latest version. $onlyready = '((qv.status = :status))'; - $this->sqlparams = [ - 'status' => question_version_status::QUESTION_STATUS_READY, - 'substatus' => question_version_status::QUESTION_STATUS_HIDDEN, - ]; + $this->sqlparams['status'] = question_version_status::QUESTION_STATUS_READY; $conditions = []; foreach ($this->searchconditions as $searchcondition) { if ($searchcondition->where()) { @@ -266,13 +257,20 @@ class custom_view extends \core_question\local\bank\view { $this->sqlparams = array_merge($this->sqlparams, $searchcondition->params()); } } - $majorconditions = ['q.parent = 0', $latestversion, $onlyready]; + $latestversion = 'qv2.questionbankentryid IS NULL'; + $majorconditions = ['q.parent = :parent', $latestversion, $onlyready]; + $this->sqlparams = array_merge(['parent' => 0], $this->sqlparams); // Get higher level filter condition. $jointype = isset($this->pagevars['jointype']) ? (int)$this->pagevars['jointype'] : condition::JOINTYPE_DEFAULT; $nonecondition = ($jointype === datafilter::JOINTYPE_NONE) ? ' NOT ' : ''; $separator = ($jointype === datafilter::JOINTYPE_ALL) ? ' AND ' : ' OR '; // Build the SQL. - $sql = ' FROM {question} q ' . implode(' ', $joins); + $this->sqlparams['substatus'] = question_version_status::QUESTION_STATUS_HIDDEN; + $sql = ' FROM {question} q ' . implode(' ', $joins) . + ' LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid + AND qv2.version > qv.version + AND qv2.status <> :substatus + )'; $sql .= ' WHERE ' . implode(' AND ', $majorconditions); if (!empty($conditions)) { $sql .= ' AND ' . $nonecondition . ' ( '; diff --git a/public/mod/quiz/classes/question/bank/filter/custom_category_condition_helper.php b/public/mod/quiz/classes/question/bank/filter/custom_category_condition_helper.php index e651b4339bf..1666d45e7da 100644 --- a/public/mod/quiz/classes/question/bank/filter/custom_category_condition_helper.php +++ b/public/mod/quiz/classes/question/bank/filter/custom_category_condition_helper.php @@ -110,25 +110,33 @@ class custom_category_condition_helper extends \qbank_managecategories\helper { 'status' => question_version_status::QUESTION_STATUS_READY, 'substatus' => question_version_status::QUESTION_STATUS_HIDDEN, ]; + + $additionaljoins = ""; + $additionalwhere = ""; + + if ($showallversions === 0) { + $additionaljoin = " LEFT JOIN {question_versions} qv2 + ON ( qv2.questionbankentryid = qv.questionbankentryid + AND qv2.version > qv.version + AND qv2.status <> :substatus + )"; + $additionalwhere = " AND qv2.questionbankentryid IS NULL"; + } + $sql = "SELECT c.*, - (SELECT COUNT(1) - FROM {question} q - JOIN {question_versions} qv ON qv.questionid = q.id - JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid - WHERE q.parent = '0' - $statuscondition + (SELECT COUNT(1) + FROM {question} q + JOIN {question_versions} qv ON qv.questionid = q.id + $additionaljoin + JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid + WHERE q.parent = '0' + $statuscondition AND c.id = qbe.questioncategoryid - AND ($showallversions = 1 - OR (qv.version = (SELECT MAX(v.version) - FROM {question_versions} v - JOIN {question_bank_entries} be ON be.id = v.questionbankentryid - WHERE be.id = qbe.id AND v.status <> :substatus) - ) - ) - ) AS questioncount - FROM {question_categories} c - WHERE c.contextid IN ($contexts) $topwhere - ORDER BY $sortorder"; + $additionalwhere + ) AS questioncount + FROM {question_categories} c + WHERE c.contextid IN ($contexts) $topwhere + ORDER BY $sortorder"; return $DB->get_records_sql($sql, $params); } diff --git a/public/question/classes/local/bank/view.php b/public/question/classes/local/bank/view.php index c87441c2631..cc116e96e54 100644 --- a/public/question/classes/local/bank/view.php +++ b/public/question/classes/local/bank/view.php @@ -750,22 +750,23 @@ class view { $extracondition = ''; if (!$showhiddenquestion) { // If Show hidden question option is off, then we need get the latest version that is not hidden. - $extracondition = ' AND v.status <> :hiddenstatus'; + $extracondition = ' AND qv2.status <> :hiddenstatus'; $this->sqlparams = array_merge($this->sqlparams, ['hiddenstatus' => question_version_status::QUESTION_STATUS_HIDDEN]); } - $latestversion = "qv.version = (SELECT MAX(v.version) - FROM {question_versions} v - JOIN {question_bank_entries} be - ON be.id = v.questionbankentryid - WHERE be.id = qbe.id $extracondition)"; // Get higher level filter condition. $jointype = isset($this->pagevars['jointype']) ? (int)$this->pagevars['jointype'] : condition::JOINTYPE_DEFAULT; $nonecondition = ($jointype === datafilter::JOINTYPE_NONE) ? ' NOT ' : ''; $separator = ($jointype === datafilter::JOINTYPE_ALL) ? ' AND ' : ' OR '; // Build the SQL. - $sql = ' FROM {question} q ' . implode(' ', $joins); - $sql .= ' WHERE q.parent = 0 AND ' . $latestversion; + $sql = ' FROM {question} q ' . implode(' ', $joins) . + ' LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid + AND qv2.version > qv.version' . + $extracondition . + ')' . + ' WHERE q.parent = :parent + AND qv2.questionbankentryid IS NULL'; + $this->sqlparams = array_merge(['parent' => 0], $this->sqlparams); if (!empty($conditions)) { $sql .= ' AND ' . $nonecondition . ' ( '; $sql .= implode($separator, $conditions); diff --git a/public/question/classes/output/question_category_selector.php b/public/question/classes/output/question_category_selector.php index c53407a66fb..8220c2c2191 100644 --- a/public/question/classes/output/question_category_selector.php +++ b/public/question/classes/output/question_category_selector.php @@ -94,21 +94,21 @@ class question_category_selector implements renderable, templatable { $topwhere = $top ? '' : 'AND c.parent <> 0'; $statuscondition = "AND (qv.status = '" . question_version_status::QUESTION_STATUS_READY . "' " . " OR qv.status = '" . question_version_status::QUESTION_STATUS_DRAFT . "' )"; - $substatuscondition = "AND v.status <> '" . question_version_status::QUESTION_STATUS_HIDDEN . "' "; + $substatuscondition = "AND qv2.status <> '" . question_version_status::QUESTION_STATUS_HIDDEN . "' "; $sql = "SELECT c.*, (SELECT COUNT(1) FROM {question} q JOIN {question_versions} qv ON qv.questionid = q.id + LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid + AND qv2.version > qv.version + $substatuscondition + ) JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid WHERE q.parent = '0' $statuscondition AND c.id = qbe.questioncategoryid AND ({$showallversions} = 1 - OR (qv.version = (SELECT MAX(v.version) - FROM {question_versions} v - JOIN {question_bank_entries} be ON be.id = v.questionbankentryid - WHERE be.id = qbe.id $substatuscondition) - ) + OR qv2.questionbankentryid IS NULL ) ) AS questioncount FROM {question_categories} c diff --git a/public/question/editlib.php b/public/question/editlib.php index e3afa5198e4..9f29c22f3db 100644 --- a/public/question/editlib.php +++ b/public/question/editlib.php @@ -81,22 +81,28 @@ function get_questions_category(object $category, bool $noparent, bool $recurse list($usql, $params) = $DB->get_in_or_equal($categorylist); // Get the latest version of a question. - $version = ''; - if ($latestversion) { - $version = 'AND (qv.version = (SELECT MAX(v.version) - FROM {question_versions} v - JOIN {question_bank_entries} be - ON be.id = v.questionbankentryid - WHERE be.id = qbe.id) OR qv.version is null)'; - } - $questions = $DB->get_records_sql("SELECT q.*, qv.status, qc.id AS category - FROM {question} q - JOIN {question_versions} qv ON qv.questionid = q.id - JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid - JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid - WHERE qc.id {$usql} {$npsql} {$version} - ORDER BY qc.id, q.qtype, q.name", $params); + $sql = "SELECT q.*, qv.status, qc.id AS category + FROM {question} q + JOIN {question_versions} qv ON qv.questionid = q.id"; + if ($latestversion) { + $sql .= " LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid + AND qv2.version > qv.version + )"; + } + + $sql .= " JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid + JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid + WHERE qc.id {$usql} {$npsql}"; + + if ($latestversion) { + $sql .= " AND (qv2.questionbankentryid IS NULL + OR qv.version IS NULL)"; + } + + $sql .= " ORDER BY qc.id, q.qtype, q.name"; + + $questions = $DB->get_records_sql($sql, $params); // Iterate through questions, getting stuff we need. $qresults = []; foreach ($questions as $question) { diff --git a/public/question/engine/bank.php b/public/question/engine/bank.php index fcf37fdedd3..9df936892fd 100644 --- a/public/question/engine/bank.php +++ b/public/question/engine/bank.php @@ -643,16 +643,15 @@ class question_finder implements data_source_interface { $sql = "SELECT q.id, q.id AS id2 FROM {question} q JOIN {question_versions} qv ON qv.questionid = q.id + LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid + AND qv2.version > qv.version + AND qv2.status = :readystatusqv + ) JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid WHERE qbe.questioncategoryid {$qcsql} AND q.parent = 0 AND qv.status = :readystatus - AND qv.version = (SELECT MAX(v.version) - FROM {question_versions} v - JOIN {question_bank_entries} be - ON be.id = v.questionbankentryid - WHERE be.id = qbe.id - AND v.status = :readystatusqv) + AND qv2.questionbankentryid IS NULL {$extraconditions}"; return $DB->get_records_sql_menu($sql, $qcparams + $extraparams); diff --git a/public/question/engine/tests/questionbank_test.php b/public/question/engine/tests/questionbank_test.php index 2e603f7e83a..d5dfc8f15a8 100644 --- a/public/question/engine/tests/questionbank_test.php +++ b/public/question/engine/tests/questionbank_test.php @@ -120,7 +120,7 @@ final class questionbank_test extends \advanced_testcase { $q1v3 = $questiongenerator->update_question($q1v2, null, ['name' => 'Q1V3']); // The second question has the first version in status draft, the second version in status ready, // and third version in status draft. - $q2v1 = $questiongenerator->create_question('numerical', null, ['name' => 'Q2V2', 'category' => $cat->id, + $q2v1 = $questiongenerator->create_question('numerical', null, ['name' => 'Q2V1', 'category' => $cat->id, 'status' => question_version_status::QUESTION_STATUS_DRAFT, ]); $q2v2 = $questiongenerator->update_question($q2v1, null, ['name' => 'Q2V2', 'status' => question_version_status::QUESTION_STATUS_READY, ]); @@ -133,11 +133,19 @@ final class questionbank_test extends \advanced_testcase { 'status' => question_version_status::QUESTION_STATUS_DRAFT, ]); $q3v3 = $questiongenerator->update_question($q3v2, null, ['name' => 'Q3V3', 'status' => question_version_status::QUESTION_STATUS_DRAFT]); + // The fourth question has the first and the second versions in status ready, and the third version in status draft. + $q4v1 = $questiongenerator->create_question('essay', null, ['name' => 'Q4V1', 'category' => $cat->id, + 'status' => question_version_status::QUESTION_STATUS_READY, ]); + $q4v2 = $questiongenerator->update_question($q4v1, null, ['name' => 'Q4V2', + 'status' => question_version_status::QUESTION_STATUS_READY, ]); + $q4v3 = $questiongenerator->update_question($q4v2, null, ['name' => 'Q4V3', + 'status' => question_version_status::QUESTION_STATUS_DRAFT]); // Test the returned array of questions in that category is the desired one with version three of the first - // question, version two of the second question, and the third question omitted completely since there are - // only draft versions. - $this->assertEquals([$q1v3->id => $q1v3->id, $q2v2->id => $q2v2->id], + // question, version two of the second question, the third question omitted completely since there are + // only draft versions, and the version two of the fourth question. + $this->assertEquals( + [$q1v3->id => $q1v3->id, $q2v2->id => $q2v2->id, $q4v2->id => $q4v2->id], question_bank::get_finder()->get_questions_from_categories([$cat->id], "")); } } diff --git a/public/report/questioninstances/index.php b/public/report/questioninstances/index.php index 2d549d8482e..e13ecd1a694 100644 --- a/public/report/questioninstances/index.php +++ b/public/report/questioninstances/index.php @@ -104,15 +104,14 @@ if ($requestedqtype) { FROM (SELECT qv.id as versionid, qc.contextid, 1 AS numquestions FROM {question} q JOIN {question_versions} qv ON qv.questionid = q.id + LEFT JOIN {question_versions} qv2 ON ( qv2.questionbankentryid = qv.questionbankentryid + AND qv2.version > qv.version + ) JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid JOIN {context} con ON con.id = qc.contextid $sqlqtypetest - AND qv.version = (SELECT MAX(v.version) - FROM {question_versions} v - JOIN {question_bank_entries} be - ON be.id = v.questionbankentryid - WHERE be.id = qbe.id) + AND qv2.questionbankentryid IS NULL AND (q.parent = 0 OR q.parent = q.id)) data GROUP BY data.contextid, data.versionid) result JOIN {context} con ON con.id = result.contextid