See Release Notes
Long Term Support Release
Differences Between: [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
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 declare(strict_types=1); 18 19 namespace core_reportbuilder\local\helpers; 20 21 use cache; 22 use context; 23 use context_system; 24 use core_collator; 25 use core_component; 26 use core_plugin_manager; 27 use core_reportbuilder\local\audiences\base; 28 use core_reportbuilder\local\models\audience as audience_model; 29 30 /** 31 * Class containing report audience helper methods 32 * 33 * @package core_reportbuilder 34 * @copyright 2021 David Matamoros <davidmc@moodle.com> 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 */ 37 class audience { 38 39 /** 40 * Return audience instances for a given report. Note that any records pointing to invalid audience types will be excluded 41 * 42 * @param int $reportid 43 * @return base[] 44 */ 45 public static function get_base_records(int $reportid): array { 46 $records = audience_model::get_records(['reportid' => $reportid], 'id'); 47 48 $instances = array_map(static function(audience_model $audience): ?base { 49 return base::instance(0, $audience->to_record()); 50 }, $records); 51 52 // Filter and remove null elements (invalid audience types). 53 return array_filter($instances); 54 } 55 56 /** 57 * Returns list of report IDs that the specified user can access, based on audience configuration. This can be expensive if the 58 * site has lots of reports, with lots of audiences, so we cache the result for the duration of the users session 59 * 60 * @param int|null $userid User ID to check, or the current user if omitted 61 * @return int[] 62 */ 63 public static function get_allowed_reports(?int $userid = null): array { 64 global $USER, $DB; 65 66 $userid = $userid ?: (int) $USER->id; 67 68 // Prepare cache, if we previously stored the users allowed reports then return that. 69 $cache = cache::make('core', 'reportbuilder_allowed_reports'); 70 $cachedreports = $cache->get($userid); 71 if ($cachedreports !== false) { 72 return $cachedreports; 73 } 74 75 $allowedreports = []; 76 $reportaudiences = []; 77 78 // Retrieve all audiences and group them by report for convenience. 79 $audiences = audience_model::get_records(); 80 foreach ($audiences as $audience) { 81 $reportaudiences[$audience->get('reportid')][] = $audience; 82 } 83 84 foreach ($reportaudiences as $reportid => $audiences) { 85 86 // Generate audience SQL based on those for the current report. 87 [$wheres, $params] = self::user_audience_sql($audiences); 88 if (count($wheres) === 0) { 89 continue; 90 } 91 92 $paramuserid = database::generate_param_name(); 93 $params[$paramuserid] = $userid; 94 95 $sql = "SELECT DISTINCT(u.id) 96 FROM {user} u 97 WHERE (" . implode(' OR ', $wheres) . ") 98 AND u.deleted = 0 99 AND u.id = :{$paramuserid}"; 100 101 // If we have a matching record, user can view the report. 102 if ($DB->record_exists_sql($sql, $params)) { 103 $allowedreports[] = $reportid; 104 } 105 } 106 107 // Store users allowed reports in cache. 108 $cache->set($userid, $allowedreports); 109 110 return $allowedreports; 111 } 112 113 /** 114 * Purge the audience cache of allowed reports 115 */ 116 public static function purge_caches(): void { 117 cache::make('core', 'reportbuilder_allowed_reports')->purge(); 118 } 119 120 /** 121 * Generate SQL select clause and params for selecting reports specified user can access, based on audience configuration 122 * 123 * @param string $reporttablealias 124 * @param int|null $userid User ID to check, or the current user if omitted 125 * @return array 126 */ 127 public static function user_reports_list_sql(string $reporttablealias, ?int $userid = null): array { 128 global $DB; 129 130 $allowedreports = self::get_allowed_reports($userid); 131 132 if (empty($allowedreports)) { 133 return ['1=0', []]; 134 } 135 136 // Get all sql audiences. 137 $prefix = database::generate_param_name() . '_'; 138 [$select, $params] = $DB->get_in_or_equal($allowedreports, SQL_PARAMS_NAMED, $prefix); 139 $sql = "{$reporttablealias}.id {$select}"; 140 141 return [$sql, $params]; 142 } 143 144 /** 145 * Return list of report ID's specified user can access, based on audience configuration 146 * 147 * @param int|null $userid User ID to check, or the current user if omitted 148 * @return int[] 149 */ 150 public static function user_reports_list(?int $userid = null): array { 151 global $DB; 152 153 [$select, $params] = self::user_reports_list_sql('rb', $userid); 154 $sql = "SELECT rb.id 155 FROM {reportbuilder_report} rb 156 WHERE {$select}"; 157 158 return $DB->get_fieldset_sql($sql, $params); 159 } 160 161 /** 162 * Returns SQL to limit the list of reports to those that the given user has access to 163 * 164 * - A user with 'editall' capability will have access to all reports 165 * - A user with 'edit' capability will have access to: 166 * - Those reports this user has created 167 * - Those reports this user is in audience of 168 * - A user with 'view' capability will have access to: 169 * - Those reports this user is in audience of 170 * 171 * @param string $reporttablealias 172 * @param int|null $userid User ID to check, or the current user if omitted 173 * @param context|null $context 174 * @return array 175 */ 176 public static function user_reports_list_access_sql( 177 string $reporttablealias, 178 ?int $userid = null, 179 ?context $context = null 180 ): array { 181 global $DB, $USER; 182 183 if ($context === null) { 184 $context = context_system::instance(); 185 } 186 187 // If user can't view all reports, limit the returned list to those reports they can see. 188 if (!has_capability('moodle/reportbuilder:editall', $context, $userid)) { 189 $reports = self::user_reports_list($userid); 190 191 [$paramprefix, $paramuserid] = database::generate_param_names(2); 192 [$reportselect, $params] = $DB->get_in_or_equal($reports, SQL_PARAMS_NAMED, "{$paramprefix}_", true, null); 193 194 $where = "{$reporttablealias}.id {$reportselect}"; 195 196 // User can also see any reports that they can edit. 197 if (has_capability('moodle/reportbuilder:edit', $context, $userid)) { 198 $where = "({$reporttablealias}.usercreated = :{$paramuserid} OR {$where})"; 199 $params[$paramuserid] = $userid ?? $USER->id; 200 } 201 202 return [$where, $params]; 203 } 204 205 return ['1=1', []]; 206 } 207 208 /** 209 * Return appropriate list of where clauses and params for given audiences 210 * 211 * @param audience_model[] $audiences 212 * @param string $usertablealias 213 * @return array[] [$wheres, $params] 214 */ 215 public static function user_audience_sql(array $audiences, string $usertablealias = 'u'): array { 216 $wheres = $params = []; 217 218 foreach ($audiences as $audience) { 219 if ($instance = base::instance(0, $audience->to_record())) { 220 $instancetablealias = database::generate_alias(); 221 [$instancejoin, $instancewhere, $instanceparams] = $instance->get_sql($instancetablealias); 222 223 $wheres[] = "{$usertablealias}.id IN ( 224 SELECT {$instancetablealias}.id 225 FROM {user} {$instancetablealias} 226 {$instancejoin} 227 WHERE {$instancewhere} 228 )"; 229 $params += $instanceparams; 230 } 231 } 232 233 return [$wheres, $params]; 234 } 235 236 /** 237 * Returns the list of audiences types in the system. 238 * 239 * @return array 240 */ 241 private static function get_audience_types(): array { 242 $sources = []; 243 244 $audiences = core_component::get_component_classes_in_namespace(null, 'reportbuilder\\audience'); 245 foreach ($audiences as $class => $path) { 246 $audienceclass = $class::instance(); 247 if (is_subclass_of($class, base::class) && $audienceclass->user_can_add()) { 248 [$component] = explode('\\', $class); 249 250 if ($plugininfo = core_plugin_manager::instance()->get_plugin_info($component)) { 251 $componentname = $plugininfo->displayname; 252 } else { 253 $componentname = get_string('site'); 254 } 255 256 $sources[$componentname][$class] = $audienceclass->get_name(); 257 } 258 } 259 260 return $sources; 261 } 262 263 /** 264 * Get all the audiences types the current user can add to, organised by categories. 265 * 266 * @return array 267 * 268 * @deprecated since Moodle 4.1 - please do not use this function any more, {@see custom_report_audience_cards_exporter} 269 */ 270 public static function get_all_audiences_menu_types(): array { 271 debugging('The function ' . __FUNCTION__ . '() is deprecated, please do not use it any more. ' . 272 'See \'custom_report_audience_cards_exporter\' class for replacement', DEBUG_DEVELOPER); 273 274 $menucardsarray = []; 275 $notavailablestr = get_string('notavailable', 'moodle'); 276 277 $audiencetypes = self::get_audience_types(); 278 $audiencetypeindex = 0; 279 foreach ($audiencetypes as $categoryname => $audience) { 280 $menucards = [ 281 'name' => $categoryname, 282 'key' => 'index' . ++$audiencetypeindex, 283 ]; 284 285 foreach ($audience as $classname => $name) { 286 $class = $classname::instance(); 287 $title = $class->is_available() ? get_string('addaudience', 'core_reportbuilder', $class->get_name()) : 288 $notavailablestr; 289 $menucard['title'] = $title; 290 $menucard['name'] = $class->get_name(); 291 $menucard['disabled'] = !$class->is_available(); 292 $menucard['identifier'] = get_class($class); 293 $menucard['action'] = 'add-audience'; 294 $menucards['items'][] = $menucard; 295 } 296 297 // Order audience types on each category alphabetically. 298 core_collator::asort_array_of_arrays_by_key($menucards['items'], 'name'); 299 $menucards['items'] = array_values($menucards['items']); 300 301 $menucardsarray[] = $menucards; 302 } 303 304 return $menucardsarray; 305 } 306 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body