Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]
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 /** 18 * Search area base class for areas working at module level. 19 * 20 * @package core_search 21 * @copyright 2015 David Monllao {@link http://www.davidmonllao.com} 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace core_search; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 /** 30 * Base implementation for search areas working at module level. 31 * 32 * Even if the search area works at multiple levels, if module is one of these levels 33 * it should extend this class, as this class provides helper methods for module level search management. 34 * 35 * @package core_search 36 * @copyright 2015 David Monllao {@link http://www.davidmonllao.com} 37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 38 */ 39 abstract class base_mod extends base { 40 41 /** 42 * The context levels the search area is working on. 43 * 44 * This can be overwriten by the search area if it works at multiple 45 * levels. 46 * 47 * @var array 48 */ 49 protected static $levels = [CONTEXT_MODULE]; 50 51 /** 52 * Returns the module name. 53 * 54 * @return string 55 */ 56 protected function get_module_name() { 57 return substr($this->componentname, 4); 58 } 59 60 /** 61 * Gets the course module for the required instanceid + modulename. 62 * 63 * The returned data depends on the logged user, when calling this through 64 * self::get_document the admin user is used so everything would be returned. 65 * 66 * No need more internal caching here, modinfo is already cached. 67 * 68 * @throws \dml_missing_record_exception 69 * @param string $modulename The module name 70 * @param int $instanceid Module instance id (depends on the module) 71 * @param int $courseid Helps speeding up things 72 * @return \cm_info 73 */ 74 protected function get_cm($modulename, $instanceid, $courseid) { 75 $modinfo = get_fast_modinfo($courseid); 76 77 // Hopefully not many, they are indexed by cmid. 78 $instances = $modinfo->get_instances_of($modulename); 79 foreach ($instances as $cminfo) { 80 if ($cminfo->instance == $instanceid) { 81 return $cminfo; 82 } 83 } 84 85 // Nothing found. 86 throw new \dml_missing_record_exception($modulename); 87 } 88 89 /** 90 * Helper function that gets SQL useful for restricting a search query given a passed-in 91 * context. 92 * 93 * The SQL returned will be zero or more JOIN statements, surrounded by whitespace, which act 94 * as restrictions on the query based on the rows in a module table. 95 * 96 * You can pass in a null or system context, which will both return an empty string and no 97 * params. 98 * 99 * Returns an array with two nulls if there can be no results for the activity within this 100 * context (e.g. it is a block context). 101 * 102 * If named parameters are used, these will be named gcrs0, gcrs1, etc. The table aliases used 103 * in SQL also all begin with gcrs, to avoid conflicts. 104 * 105 * @param \context|null $context Context to restrict the query 106 * @param string $modname Name of module e.g. 'forum' 107 * @param string $modtable Alias of table containing module id 108 * @param int $paramtype Type of SQL parameters to use (default question mark) 109 * @return array Array with SQL and parameters; both null if no need to query 110 * @throws \coding_exception If called with invalid params 111 */ 112 protected function get_context_restriction_sql(?\context $context, $modname, $modtable, 113 $paramtype = SQL_PARAMS_QM) { 114 global $DB; 115 116 if (!$context) { 117 return ['', []]; 118 } 119 120 switch ($paramtype) { 121 case SQL_PARAMS_QM: 122 $param1 = '?'; 123 $param2 = '?'; 124 $param3 = '?'; 125 $key1 = 0; 126 $key2 = 1; 127 $key3 = 2; 128 break; 129 case SQL_PARAMS_NAMED: 130 $param1 = ':gcrs0'; 131 $param2 = ':gcrs1'; 132 $param3 = ':gcrs2'; 133 $key1 = 'gcrs0'; 134 $key2 = 'gcrs1'; 135 $key3 = 'gcrs2'; 136 break; 137 default: 138 throw new \coding_exception('Unexpected $paramtype: ' . $paramtype); 139 } 140 141 $params = []; 142 switch ($context->contextlevel) { 143 case CONTEXT_SYSTEM: 144 $sql = ''; 145 break; 146 147 case CONTEXT_COURSECAT: 148 // Find all activities of this type within the specified category or any 149 // sub-category. 150 $pathmatch = $DB->sql_like('gcrscc2.path', $DB->sql_concat('gcrscc1.path', $param3)); 151 $sql = " JOIN {course_modules} gcrscm ON gcrscm.instance = $modtable.id 152 AND gcrscm.module = (SELECT id FROM {modules} WHERE name = $param1) 153 JOIN {course} gcrsc ON gcrsc.id = gcrscm.course 154 JOIN {course_categories} gcrscc1 ON gcrscc1.id = $param2 155 JOIN {course_categories} gcrscc2 ON gcrscc2.id = gcrsc.category AND 156 (gcrscc2.id = gcrscc1.id OR $pathmatch) "; 157 $params[$key1] = $modname; 158 $params[$key2] = $context->instanceid; 159 // Note: This param is a bit annoying as it obviously never changes, but sql_like 160 // throws a debug warning if you pass it anything with quotes in, so it has to be 161 // a bound parameter. 162 $params[$key3] = '/%'; 163 break; 164 165 case CONTEXT_COURSE: 166 // Find all activities of this type within the course. 167 $sql = " JOIN {course_modules} gcrscm ON gcrscm.instance = $modtable.id 168 AND gcrscm.course = $param1 169 AND gcrscm.module = (SELECT id FROM {modules} WHERE name = $param2) "; 170 $params[$key1] = $context->instanceid; 171 $params[$key2] = $modname; 172 break; 173 174 case CONTEXT_MODULE: 175 // Find only the specified activity of this type. 176 $sql = " JOIN {course_modules} gcrscm ON gcrscm.instance = $modtable.id 177 AND gcrscm.id = $param1 178 AND gcrscm.module = (SELECT id FROM {modules} WHERE name = $param2) "; 179 $params[$key1] = $context->instanceid; 180 $params[$key2] = $modname; 181 break; 182 183 case CONTEXT_BLOCK: 184 case CONTEXT_USER: 185 // These contexts cannot contain any activities, so return null. 186 return [null, null]; 187 188 default: 189 throw new \coding_exception('Unexpected contextlevel: ' . $context->contextlevel); 190 } 191 192 return [$sql, $params]; 193 } 194 195 /** 196 * This can be used in subclasses to change ordering within the get_contexts_to_reindex 197 * function. 198 * 199 * It returns 2 values: 200 * - Extra SQL joins (tables course_modules 'cm' and context 'x' already exist). 201 * - An ORDER BY value which must use aggregate functions, by default 'MAX(cm.added) DESC'. 202 * 203 * Note the query already includes a GROUP BY on the context fields, so if your joins result 204 * in multiple rows, you can use aggregate functions in the ORDER BY. See forum for an example. 205 * 206 * @return string[] Array with 2 elements; extra joins for the query, and ORDER BY value 207 */ 208 protected function get_contexts_to_reindex_extra_sql() { 209 return ['', 'MAX(cm.added) DESC']; 210 } 211 212 /** 213 * Gets a list of all contexts to reindex when reindexing this search area. 214 * 215 * For modules, the default is to return all contexts for modules of that type, in order of 216 * time added (most recent first). 217 * 218 * @return \Iterator Iterator of contexts to reindex 219 * @throws \moodle_exception If any DB error 220 */ 221 public function get_contexts_to_reindex() { 222 global $DB; 223 224 list ($extrajoins, $dborder) = $this->get_contexts_to_reindex_extra_sql(); 225 $contexts = []; 226 $selectcolumns = \context_helper::get_preload_record_columns_sql('x'); 227 $groupbycolumns = ''; 228 foreach (\context_helper::get_preload_record_columns('x') as $column => $thing) { 229 if ($groupbycolumns !== '') { 230 $groupbycolumns .= ','; 231 } 232 $groupbycolumns .= $column; 233 } 234 $rs = $DB->get_recordset_sql(" 235 SELECT $selectcolumns 236 FROM {course_modules} cm 237 JOIN {context} x ON x.instanceid = cm.id AND x.contextlevel = ? 238 $extrajoins 239 WHERE cm.module = (SELECT id FROM {modules} WHERE name = ?) 240 GROUP BY $groupbycolumns 241 ORDER BY $dborder", [CONTEXT_MODULE, $this->get_module_name()]); 242 return new \core\dml\recordset_walk($rs, function($rec) { 243 $id = $rec->ctxid; 244 \context_helper::preload_from_record($rec); 245 return \context::instance_by_id($id); 246 }); 247 } 248 249 /** 250 * Indicates whether this search area may restrict access by group. 251 * 252 * This should return true if the search area (sometimes) sets the 'groupid' schema field, and 253 * false if it never sets that field. 254 * 255 * (If this function returns false, but the field is set, then results may be restricted 256 * unintentionally.) 257 * 258 * If this returns true, the search engine will automatically apply group restrictions in some 259 * cases (by default, where a module is configured to use separate groups). See function 260 * restrict_cm_access_by_group(). 261 * 262 * @return bool 263 */ 264 public function supports_group_restriction() { 265 return false; 266 } 267 268 /** 269 * Checks whether the content of this search area should be restricted by group for a 270 * specific module. Called at query time. 271 * 272 * The default behaviour simply checks if the effective group mode is SEPARATEGROUPS, which 273 * is probably correct for most cases. 274 * 275 * If restricted by group, the search query will (where supported by the engine) filter out 276 * results for groups the user does not belong to, unless the user has 'access all groups' 277 * for the activity. This affects only documents which set the 'groupid' field; results with no 278 * groupid will not be restricted. 279 * 280 * Even if you return true to this function, you may still need to do group access checks in 281 * check_access, because the search engine may not support group restrictions. 282 * 283 * @param \cm_info $cm 284 * @return bool True to restrict by group 285 */ 286 public function restrict_cm_access_by_group(\cm_info $cm) { 287 return $cm->effectivegroupmode == SEPARATEGROUPS; 288 } 289 290 /** 291 * Returns an icon instance for the document. 292 * 293 * @param \core_search\document $doc 294 * @return \core_search\document_icon 295 */ 296 public function get_doc_icon(document $doc) : document_icon { 297 return new document_icon('icon', $this->get_module_name()); 298 } 299 300 /** 301 * Returns a list of category names associated with the area. 302 * 303 * @return array 304 */ 305 public function get_category_names() { 306 return [manager::SEARCH_AREA_CATEGORY_COURSE_CONTENT]; 307 } 308 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body