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 qbank_tagquestion; 18 19 use core\output\datafilter; 20 use core_question\local\bank\condition; 21 22 /** 23 * Question bank search class to allow searching/filtering by tags on a question. 24 * 25 * @package qbank_tagquestion 26 * @copyright 2018 Ryan Wyllie <ryan@moodle.com> 27 * @author 2021 Safat Shahin <safatshahin@catalyst-au.net> 28 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 */ 30 class tag_condition extends condition { 31 32 /** @var int The default filter type */ 33 const JOINTYPE_DEFAULT = datafilter::JOINTYPE_ALL; 34 35 /** @var array Contexts to be used. */ 36 protected $contexts = []; 37 38 /** @var array List of IDs for tags that have been selected in the form. */ 39 protected $selectedtagids = []; 40 41 /** 42 * Tag condition constructor. It uses the qbank object and initialises all the its required information 43 * to be passed as a part of condition to get the questions. 44 * 45 * @param null $qbank qbank view 46 */ 47 public function __construct($qbank = null) { 48 if (is_null($qbank)) { 49 return; 50 } 51 parent::__construct($qbank); 52 $cat = $qbank->get_pagevars('cat'); 53 if (is_array($cat)) { 54 foreach ($cat as $value) { 55 [, $contextid] = explode(',', $value); 56 $catcontext = \context::instance_by_id($contextid); 57 $this->contexts[] = $catcontext; 58 } 59 } else { 60 [, $contextid] = explode(',', $qbank->get_pagevars('cat')); 61 $catcontext = \context::instance_by_id($contextid); 62 $this->contexts[] = $catcontext; 63 } 64 $thiscontext = $qbank->get_most_specific_context(); 65 $this->contexts[] = $thiscontext; 66 $this->selectedtagids = $this->filter->values ?? []; 67 } 68 69 public static function get_condition_key() { 70 return 'qtagids'; 71 } 72 73 /** 74 * Print HTML to display the list of tags to filter by. 75 */ 76 public function display_options() { 77 global $PAGE; 78 79 $tags = \core_tag_tag::get_tags_by_area_in_contexts('core_question', 'question', $this->contexts); 80 $tagoptions = array_map(function($tag) { 81 return [ 82 'id' => $tag->id, 83 'name' => $tag->name, 84 'selected' => in_array($tag->id, $this->selectedtagids) 85 ]; 86 }, array_values($tags)); 87 $context = [ 88 'tagoptions' => $tagoptions 89 ]; 90 91 return $PAGE->get_renderer('qbank_tagquestion')->render_tag_condition($context); 92 } 93 94 /** 95 * Build query from filter value 96 * 97 * @param array $filter filter properties 98 * @return array where sql and params 99 */ 100 public static function build_query_from_filter(array $filter): array { 101 global $DB; 102 103 // Remove empty string. 104 $filter['values'] = array_filter($filter['values']); 105 106 $selectedtagids = $filter['values'] ?? []; 107 108 $params = []; 109 $where = ''; 110 $jointype = $filter['jointype'] ?? self::JOINTYPE_DEFAULT; 111 // If some tags have been selected then we need to filter 112 // the question list by the selected tags. 113 if ($selectedtagids) { 114 // We treat each additional tag as an AND condition rather than 115 // an OR condition. 116 // 117 // For example, if the user filters by the tags "foo" and "bar" then 118 // we reduce the question list to questions that are tagged with both 119 // "foo" AND "bar". Any question that does not have ALL of the specified 120 // tags will be omitted. 121 $equal = !($jointype === datafilter::JOINTYPE_NONE); 122 [$tagsql, $tagparams] = $DB->get_in_or_equal($selectedtagids, SQL_PARAMS_NAMED, 'param', $equal); 123 $tagparams['tagcount'] = count($selectedtagids); 124 $tagparams['questionitemtype'] = 'question'; 125 $tagparams['questioncomponent'] = 'core_question'; 126 $params = $tagparams; 127 $where = "q.id IN (SELECT ti.itemid 128 FROM {tag_instance} ti 129 WHERE ti.itemtype = :questionitemtype 130 AND ti.component = :questioncomponent 131 AND ti.tagid {$tagsql} 132 GROUP BY ti.itemid "; 133 if ($jointype === datafilter::JOINTYPE_ALL) { 134 $where .= "HAVING COUNT(itemid) = :tagcount "; 135 } 136 $where .= ") "; 137 138 } 139 140 return [$where, $params]; 141 } 142 143 144 public function get_title() { 145 return get_string('tag', 'tag'); 146 } 147 148 public function get_filter_class() { 149 return null; 150 } 151 152 public function allow_custom() { 153 return false; 154 } 155 156 public function get_initial_values() { 157 $tags = \core_tag_tag::get_tags_by_area_in_contexts('core_question', 'question', $this->contexts); 158 $values = []; 159 foreach ($tags as $tag) { 160 $values[] = [ 161 'value' => $tag->id, 162 'title' => html_entity_decode($tag->name), 163 'selected' => in_array($tag->id, $this->selectedtagids) 164 ]; 165 } 166 return $values; 167 } 168 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body