Differences Between: [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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\systemreports; 20 21 use context_system; 22 use core_reportbuilder\local\helpers\audience; 23 use core_reportbuilder\local\helpers\database; 24 use html_writer; 25 use lang_string; 26 use moodle_url; 27 use pix_icon; 28 use stdClass; 29 use core_reportbuilder\datasource; 30 use core_reportbuilder\manager; 31 use core_reportbuilder\system_report; 32 use core_reportbuilder\local\entities\user; 33 use core_reportbuilder\local\filters\date; 34 use core_reportbuilder\local\filters\text; 35 use core_reportbuilder\local\filters\select; 36 use core_reportbuilder\local\helpers\format; 37 use core_reportbuilder\local\report\action; 38 use core_reportbuilder\local\report\column; 39 use core_reportbuilder\local\report\filter; 40 use core_reportbuilder\output\report_name_editable; 41 use core_reportbuilder\local\models\report; 42 use core_reportbuilder\permission; 43 44 /** 45 * Reports list 46 * 47 * @package core_reportbuilder 48 * @copyright 2021 David Matamoros <davidmc@moodle.com> 49 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 50 */ 51 class reports_list extends system_report { 52 53 /** 54 * The name of our internal report entity 55 * 56 * @return string 57 */ 58 private function get_report_entity_name(): string { 59 return 'report'; 60 } 61 62 /** 63 * Initialise the report 64 */ 65 protected function initialise(): void { 66 $this->set_main_table('reportbuilder_report', 'rb'); 67 $this->add_base_condition_simple('rb.type', self::TYPE_CUSTOM_REPORT); 68 69 // Select fields required for actions, permission checks, and row class callbacks. 70 $this->add_base_fields('rb.id, rb.name, rb.source, rb.type, rb.usercreated, rb.contextid'); 71 72 // If user can't view all reports, limit the returned list to those reports they can see. 73 [$where, $params] = $this->filter_by_allowed_reports_sql(); 74 if (!empty($where)) { 75 $this->add_base_condition_sql($where, $params); 76 } 77 78 // Join user entity for "User modified" column. 79 $entityuser = new user(); 80 $entityuseralias = $entityuser->get_table_alias('user'); 81 82 $this->add_entity($entityuser 83 ->add_join("JOIN {user} {$entityuseralias} ON {$entityuseralias}.id = rb.usermodified") 84 ); 85 86 // Define our internal entity for report elements. 87 $this->annotate_entity($this->get_report_entity_name(), 88 new lang_string('customreports', 'core_reportbuilder')); 89 90 $this->add_columns(); 91 $this->add_filters(); 92 $this->add_actions(); 93 94 $this->set_downloadable(false); 95 } 96 97 /** 98 * Ensure we can view the report 99 * 100 * @return bool 101 */ 102 protected function can_view(): bool { 103 return permission::can_view_reports_list(); 104 } 105 106 /** 107 * Dim the table row for invalid datasource 108 * 109 * @param stdClass $row 110 * @return string 111 */ 112 public function get_row_class(stdClass $row): string { 113 return $this->report_source_valid($row->source) ? '' : 'text-muted'; 114 } 115 116 /** 117 * Add columns to report 118 */ 119 protected function add_columns(): void { 120 $tablealias = $this->get_main_table_alias(); 121 122 // Report name column. 123 $this->add_column((new column( 124 'name', 125 new lang_string('name'), 126 $this->get_report_entity_name() 127 )) 128 ->set_type(column::TYPE_TEXT) 129 ->add_fields("{$tablealias}.name, {$tablealias}.id") 130 ->set_is_sortable(true) 131 ->add_callback(function(string $value, stdClass $row) { 132 global $PAGE; 133 134 $reportid = (int) $row->id; 135 136 $editable = new report_name_editable($reportid); 137 return $editable->render($PAGE->get_renderer('core')); 138 }) 139 ); 140 141 // Report source column. 142 $this->add_column((new column( 143 'source', 144 new lang_string('reportsource', 'core_reportbuilder'), 145 $this->get_report_entity_name() 146 )) 147 ->set_type(column::TYPE_TEXT) 148 ->add_fields("{$tablealias}.source") 149 ->set_is_sortable(true) 150 ->add_callback(function(string $value, stdClass $row) { 151 if (!$this->report_source_valid($value)) { 152 // Add danger badge if report source is not valid (either it's missing, or has errors). 153 return html_writer::span(get_string('errorsourceinvalid', 'core_reportbuilder'), 'badge badge-danger'); 154 } 155 156 return call_user_func([$value, 'get_name']); 157 }) 158 ); 159 160 // Time created column. 161 $this->add_column((new column( 162 'timecreated', 163 new lang_string('timecreated', 'core_reportbuilder'), 164 $this->get_report_entity_name() 165 )) 166 ->set_type(column::TYPE_TIMESTAMP) 167 ->add_fields("{$tablealias}.timecreated") 168 ->set_is_sortable(true) 169 ->add_callback([format::class, 'userdate']) 170 ); 171 172 // Time modified column. 173 $this->add_column((new column( 174 'timemodified', 175 new lang_string('timemodified', 'core_reportbuilder'), 176 $this->get_report_entity_name() 177 )) 178 ->set_type(column::TYPE_TIMESTAMP) 179 ->add_fields("{$tablealias}.timemodified") 180 ->set_is_sortable(true) 181 ->add_callback([format::class, 'userdate']) 182 ); 183 184 // The user who modified the report. 185 $this->add_column_from_entity('user:fullname') 186 ->set_title(new lang_string('usermodified', 'reportbuilder')); 187 188 // Initial sorting. 189 $this->set_initial_sort_column('report:timecreated', SORT_DESC); 190 } 191 192 /** 193 * Add filters to report 194 */ 195 protected function add_filters(): void { 196 $tablealias = $this->get_main_table_alias(); 197 198 // Name filter. 199 $this->add_filter((new filter( 200 text::class, 201 'name', 202 new lang_string('name'), 203 $this->get_report_entity_name(), 204 "{$tablealias}.name" 205 ))); 206 207 // Source filter. 208 $this->add_filter((new filter( 209 select::class, 210 'source', 211 new lang_string('reportsource', 'core_reportbuilder'), 212 $this->get_report_entity_name(), 213 "{$tablealias}.source" 214 )) 215 ->set_options_callback(static function(): array { 216 return manager::get_report_datasources(); 217 }) 218 ); 219 220 // Time created filter. 221 $this->add_filter((new filter( 222 date::class, 223 'timecreated', 224 new lang_string('timecreated', 'core_reportbuilder'), 225 $this->get_report_entity_name(), 226 "{$tablealias}.timecreated" 227 )) 228 ->set_limited_operators([ 229 date::DATE_ANY, 230 date::DATE_RANGE, 231 ]) 232 ); 233 } 234 235 /** 236 * Add actions to report 237 */ 238 protected function add_actions(): void { 239 // Edit content action. 240 $this->add_action((new action( 241 new moodle_url('/reportbuilder/edit.php', ['id' => ':id']), 242 new pix_icon('t/right', ''), 243 [], 244 false, 245 new lang_string('editreportcontent', 'core_reportbuilder') 246 )) 247 ->add_callback(function(stdClass $row): bool { 248 return $this->report_source_valid($row->source) && permission::can_edit_report(new report(0, $row)); 249 }) 250 ); 251 252 // Edit details action. 253 $this->add_action((new action( 254 new moodle_url('#'), 255 new pix_icon('t/edit', ''), 256 ['data-action' => 'report-edit', 'data-report-id' => ':id'], 257 false, 258 new lang_string('editreportdetails', 'core_reportbuilder') 259 )) 260 ->add_callback(function(stdClass $row): bool { 261 return $this->report_source_valid($row->source) && permission::can_edit_report(new report(0, $row)); 262 }) 263 ); 264 265 // Preview action. 266 $this->add_action((new action( 267 new moodle_url('/reportbuilder/view.php', ['id' => ':id']), 268 new pix_icon('i/search', ''), 269 [], 270 false, 271 new lang_string('viewreport', 'core_reportbuilder') 272 )) 273 ->add_callback(function(stdClass $row): bool { 274 // We check this only to give the action to editors, because normal users can just click on the report name. 275 return $this->report_source_valid($row->source) && permission::can_edit_report(new report(0, $row)); 276 }) 277 ); 278 279 // Delete action. 280 $this->add_action((new action( 281 new moodle_url('#'), 282 new pix_icon('t/delete', ''), 283 ['data-action' => 'report-delete', 'data-report-id' => ':id', 'data-report-name' => ':name'], 284 false, 285 new lang_string('deletereport', 'core_reportbuilder') 286 )) 287 ->add_callback(function(stdClass $row): bool { 288 289 // Ensure data name attribute is properly formatted. 290 $report = new report(0, $row); 291 $row->name = $report->get_formatted_name(); 292 293 // We don't check whether report is valid to ensure editor can always delete them. 294 return permission::can_edit_report($report); 295 }) 296 ); 297 } 298 299 /** 300 * Helper to determine whether given report source is valid (it both exists, and is available) 301 * 302 * @param string $source 303 * @return bool 304 */ 305 private function report_source_valid(string $source): bool { 306 return manager::report_source_exists($source, datasource::class) && manager::report_source_available($source); 307 } 308 309 /** 310 * Filters the list of reports to return only the ones the user has access to 311 * 312 * - A user with 'editall' capability will have access to all reports. 313 * - A user with 'edit' capability will have access to: 314 * - Those reports this user has created. 315 * - Those reports this user is in audience of. 316 * - A user with 'view' capability will have access to: 317 * - Those reports this user is in audience of. 318 * 319 * @return array 320 */ 321 private function filter_by_allowed_reports_sql(): array { 322 global $DB, $USER; 323 324 // If user can't view all reports, limit the returned list to those reports they can see. 325 if (!has_capability('moodle/reportbuilder:editall', context_system::instance())) { 326 $reports = audience::user_reports_list(); 327 328 if (has_capability('moodle/reportbuilder:edit', context_system::instance())) { 329 // User can always see own reports and also those reports user is in audience of. 330 $paramuserid = database::generate_param_name(); 331 332 if (empty($reports)) { 333 return ["rb.usercreated = :{$paramuserid}", [$paramuserid => $USER->id]]; 334 } 335 336 $prefix = database::generate_param_name() . '_'; 337 [$where, $params] = $DB->get_in_or_equal($reports, SQL_PARAMS_NAMED, $prefix); 338 339 $params = array_merge($params, [$paramuserid => $USER->id]); 340 341 return ["(rb.usercreated = :{$paramuserid} OR rb.id {$where})", $params]; 342 343 } 344 345 // User has view capability. User can only see those reports user is in audience of. 346 if (empty($reports)) { 347 return ['1=2', []]; 348 } 349 350 $prefix = database::generate_param_name() . '_'; 351 [$where, $params] = $DB->get_in_or_equal($reports, SQL_PARAMS_NAMED, $prefix); 352 return ["rb.id {$where}", $params]; 353 } 354 355 return ['', []]; 356 } 357 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body