| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 | // This file is part of Moodle - http://moodle.org/
 | 
        
           |  |  | 3 | //
 | 
        
           |  |  | 4 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 5 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 6 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 7 | // (at your option) any later version.
 | 
        
           |  |  | 8 | //
 | 
        
           |  |  | 9 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 12 | // GNU General Public License for more details.
 | 
        
           |  |  | 13 | //
 | 
        
           |  |  | 14 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 15 | // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
        
           |  |  | 16 |   | 
        
           |  |  | 17 | namespace core_question;
 | 
        
           |  |  | 18 |   | 
        
           |  |  | 19 | use core_question\local\bank\question_version_status;
 | 
        
           |  |  | 20 |   | 
        
           |  |  | 21 | /**
 | 
        
           |  |  | 22 |  * This class should provide an API for managing question_references.
 | 
        
           |  |  | 23 |  *
 | 
        
           |  |  | 24 |  * Unfortunately, question_references were introduced in the DB structure
 | 
        
           |  |  | 25 |  * without an nice API. This class is being added later, and is currently
 | 
        
           |  |  | 26 |  * terribly incomplete, but hopefully it can be improved in time.
 | 
        
           |  |  | 27 |  *
 | 
        
           |  |  | 28 |  * @package    core_question
 | 
        
           |  |  | 29 |  * @copyright  2023 The Open University
 | 
        
           |  |  | 30 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 31 |  */
 | 
        
           |  |  | 32 | class question_reference_manager {
 | 
        
           |  |  | 33 |     /**
 | 
        
           |  |  | 34 |      * Return a list of those questions from the list passed in, which are referenced.
 | 
        
           |  |  | 35 |      *
 | 
        
           |  |  | 36 |      * A question is referenced if either:
 | 
        
           |  |  | 37 |      * - There is a question_reference pointing at exactly that version of that question; or
 | 
        
           |  |  | 38 |      * - There is an 'always latest' reference, and the question id is the latest non-draft version
 | 
        
           |  |  | 39 |      *   of that question_bank_entry.
 | 
        
           |  |  | 40 |      *
 | 
        
           |  |  | 41 |      * @param array $questionids a list of question ids to check.
 | 
        
           |  |  | 42 |      * @return array a list of the question ids from the input array which are referenced.
 | 
        
           |  |  | 43 |      */
 | 
        
           |  |  | 44 |     public static function questions_with_references(array $questionids): array {
 | 
        
           |  |  | 45 |         global $DB;
 | 
        
           |  |  | 46 |   | 
        
           |  |  | 47 |         if (empty($questionids)) {
 | 
        
           |  |  | 48 |             return [];
 | 
        
           |  |  | 49 |         }
 | 
        
           |  |  | 50 |   | 
        
           |  |  | 51 |         [$qidtest, $params] = $DB->get_in_or_equal($questionids, SQL_PARAMS_NAMED, 'outerqid');
 | 
        
           |  |  | 52 |         [$lqidtest, $lparams] = $DB->get_in_or_equal($questionids, SQL_PARAMS_NAMED, 'innerqid');
 | 
        
           |  |  | 53 |   | 
        
           |  |  | 54 |         return $DB->get_fieldset_sql("
 | 
        
           |  |  | 55 |             SELECT qv.questionid
 | 
        
           |  |  | 56 |   | 
        
           |  |  | 57 |               FROM {question_versions} qv
 | 
        
           |  |  | 58 |   | 
        
           |  |  | 59 |          -- This is a performant to get the latest non-draft version for each
 | 
        
           |  |  | 60 |          -- question_bank_entry that relates to one of our questionids.
 | 
        
           |  |  | 61 |          LEFT JOIN (
 | 
        
           |  |  | 62 |                        SELECT lqv.questionbankentryid,
 | 
        
           |  |  | 63 |                               MAX(lv.version) AS latestusableversion
 | 
        
           |  |  | 64 |                          FROM {question_versions} lqv
 | 
        
           |  |  | 65 |                          JOIN {question_versions} lv ON lv.questionbankentryid = lqv.questionbankentryid
 | 
        
           |  |  | 66 |                         WHERE lqv.questionid $lqidtest
 | 
        
           |  |  | 67 |                           AND lv.status <> :draft
 | 
        
           |  |  | 68 |                      GROUP BY lqv.questionbankentryid
 | 
        
           |  |  | 69 |                    ) latestversions ON latestversions.questionbankentryid = qv.questionbankentryid
 | 
        
           |  |  | 70 |   | 
        
           |  |  | 71 |               JOIN {question_references} qr ON qr.questionbankentryid = qv.questionbankentryid
 | 
        
           |  |  | 72 |                        AND (qr.version = qv.version OR qr.version IS NULL AND qv.version = latestversions.latestusableversion)
 | 
        
           |  |  | 73 |   | 
        
           |  |  | 74 |              WHERE qv.questionid $qidtest
 | 
        
           |  |  | 75 |             ", array_merge($params, $lparams, ['draft' => question_version_status::QUESTION_STATUS_DRAFT]));
 | 
        
           |  |  | 76 |     }
 | 
        
           |  |  | 77 |   | 
        
           |  |  | 78 |     /**
 | 
        
           |  |  | 79 |      * This will transform set reference filter conditions to use the new filter structure.
 | 
        
           |  |  | 80 |      *
 | 
        
           |  |  | 81 |      * Previously filterconditions had questioncategoryid, includesubcategories and tags options.
 | 
        
           |  |  | 82 |      * These have been replaced by the new category and tags filters. This function convers the old
 | 
        
           |  |  | 83 |      * pre-4.3 filter condition structure to the new one.
 | 
        
           |  |  | 84 |      *
 | 
        
           |  |  | 85 |      * @param array $filtercondition Pre-4.3 filter condition.
 | 
        
           |  |  | 86 |      * @return array Post-4.3 filter condition.
 | 
        
           |  |  | 87 |      */
 | 
        
           |  |  | 88 |     public static function convert_legacy_set_reference_filter_condition(array $filtercondition): array {
 | 
        
           | 1441 | ariadna | 89 |         global $DB;
 | 
        
           | 1 | efrain | 90 |         if (!isset($filtercondition['filter'])) {
 | 
        
           |  |  | 91 |             $filtercondition['filter'] = [];
 | 
        
           |  |  | 92 |   | 
        
           |  |  | 93 |             // Question category filter.
 | 
        
           |  |  | 94 |             if (isset($filtercondition['questioncategoryid'])) {
 | 
        
           |  |  | 95 |                 $filtercondition['filter']['category'] = [
 | 
        
           |  |  | 96 |                     'jointype' => \qbank_managecategories\category_condition::JOINTYPE_DEFAULT,
 | 
        
           |  |  | 97 |                     'values' => [$filtercondition['questioncategoryid']],
 | 
        
           |  |  | 98 |                     'filteroptions' => ['includesubcategories' => $filtercondition['includingsubcategories']],
 | 
        
           |  |  | 99 |                 ];
 | 
        
           |  |  | 100 |             }
 | 
        
           |  |  | 101 |   | 
        
           |  |  | 102 |             // Tag filters.
 | 
        
           |  |  | 103 |             if (isset($filtercondition['tags'])) {
 | 
        
           |  |  | 104 |                 // Get the names of the tags in the condition. Find or create corresponding tags,
 | 
        
           |  |  | 105 |                 // and set their ids in the new condition.
 | 
        
           |  |  | 106 |                 $oldtags = array_map(fn($oldtag) => explode(',', $oldtag)[1], $filtercondition['tags']);
 | 
        
           | 1441 | ariadna | 107 |                 $questiontagcollid = \core_tag_area::get_collection('core_question', 'question');
 | 
        
           |  |  | 108 |                 $newtags = \core_tag_tag::create_if_missing($questiontagcollid, $oldtags);
 | 
        
           | 1 | efrain | 109 |                 $newtagids = array_map(fn($newtag) => $newtag->id, $newtags);
 | 
        
           |  |  | 110 |   | 
        
           |  |  | 111 |                 $filtercondition['filter']['qtagids'] = [
 | 
        
           |  |  | 112 |                     'jointype' => \qbank_tagquestion\tag_condition::JOINTYPE_DEFAULT,
 | 
        
           |  |  | 113 |                     'values' => array_values($newtagids),
 | 
        
           |  |  | 114 |                 ];
 | 
        
           |  |  | 115 |                 unset($filtercondition['tags']);
 | 
        
           |  |  | 116 |             }
 | 
        
           |  |  | 117 |             // Add additional default properties to the filtercondition.
 | 
        
           | 1441 | ariadna | 118 |             if (isset($filtercondition['questioncategoryid'])) {
 | 
        
           |  |  | 119 |                 $category = $DB->get_record('question_categories', ['id' => $filtercondition['questioncategoryid']]);
 | 
        
           |  |  | 120 |                 if ($category) {
 | 
        
           |  |  | 121 |                     $filtercondition['cat'] = "{$category->id},{$category->contextid}";
 | 
        
           |  |  | 122 |                 } else {
 | 
        
           |  |  | 123 |                     $filtercondition['cat'] = '';
 | 
        
           |  |  | 124 |                 }
 | 
        
           |  |  | 125 |             }
 | 
        
           | 1 | efrain | 126 |             $filtercondition['tabname'] = 'questions';
 | 
        
           |  |  | 127 |             $filtercondition['qpage'] = 0;
 | 
        
           |  |  | 128 |             $filtercondition['qperpage'] = 100;
 | 
        
           |  |  | 129 |             $filtercondition['jointype'] = \core\output\datafilter::JOINTYPE_ALL;
 | 
        
           | 1441 | ariadna | 130 |             unset($filtercondition['questioncategoryid']);
 | 
        
           |  |  | 131 |             unset($filtercondition['includingsubcategories']);
 | 
        
           | 1 | efrain | 132 |         } else if (isset($filtercondition['filter']['category']['includesubcategories'])) {
 | 
        
           |  |  | 133 |             $filtercondition['filter']['category']['filteroptions'] =
 | 
        
           |  |  | 134 |                 ['includesubcategories' => $filtercondition['filter']['category']['includesubcategories']];
 | 
        
           |  |  | 135 |             unset($filtercondition['filter']['category']['includesubcategories']);
 | 
        
           |  |  | 136 |         }
 | 
        
           |  |  | 137 |         return $filtercondition;
 | 
        
           |  |  | 138 |     }
 | 
        
           |  |  | 139 | }
 |