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\entities; 20 21 use context_course; 22 use context_helper; 23 use core_reportbuilder\local\filters\boolean_select; 24 use core_reportbuilder\local\filters\course_selector; 25 use core_reportbuilder\local\filters\date; 26 use core_reportbuilder\local\filters\select; 27 use core_reportbuilder\local\filters\text; 28 use core_reportbuilder\local\helpers\custom_fields; 29 use core_reportbuilder\local\helpers\format; 30 use core_reportbuilder\local\report\column; 31 use core_reportbuilder\local\report\filter; 32 use html_writer; 33 use lang_string; 34 use stdClass; 35 36 defined('MOODLE_INTERNAL') || die(); 37 38 global $CFG; 39 require_once($CFG->dirroot . '/course/lib.php'); 40 41 /** 42 * Course entity class implementation 43 * 44 * This entity defines all the course columns and filters to be used in any report. 45 * 46 * @package core_reportbuilder 47 * @copyright 2021 Sara Arjona <sara@moodle.com> based on Marina Glancy code. 48 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 49 */ 50 class course extends base { 51 52 /** 53 * Database tables that this entity uses and their default aliases. 54 * 55 * @return array 56 */ 57 protected function get_default_table_aliases(): array { 58 return [ 59 'course' => 'c', 60 'context' => 'cctx', 61 ]; 62 } 63 64 /** 65 * The default machine-readable name for this entity that will be used in the internal names of the columns/filters. 66 * 67 * @return string 68 */ 69 protected function get_default_entity_name(): string { 70 return 'course'; 71 } 72 73 /** 74 * The default title for this entity in the list of columns/filters in the report builder. 75 * 76 * @return lang_string 77 */ 78 protected function get_default_entity_title(): lang_string { 79 return new lang_string('entitycourse', 'core_reportbuilder'); 80 } 81 82 /** 83 * Get custom fields helper 84 * 85 * @return custom_fields 86 */ 87 protected function get_custom_fields(): custom_fields { 88 $customfields = new custom_fields($this->get_table_alias('course') . '.id', $this->get_entity_name(), 89 'core_course', 'course'); 90 $customfields->add_joins($this->get_joins()); 91 return $customfields; 92 } 93 94 /** 95 * Initialise the entity, adding all course and custom course fields 96 * 97 * @return base 98 */ 99 public function initialise(): base { 100 $customfields = $this->get_custom_fields(); 101 102 $columns = array_merge($this->get_all_columns(), $customfields->get_columns()); 103 foreach ($columns as $column) { 104 $this->add_column($column); 105 } 106 107 $filters = array_merge($this->get_all_filters(), $customfields->get_filters()); 108 foreach ($filters as $filter) { 109 $this 110 ->add_condition($filter) 111 ->add_filter($filter); 112 } 113 114 return $this; 115 } 116 117 /** 118 * Course fields. 119 * 120 * @return array 121 */ 122 protected function get_course_fields(): array { 123 return [ 124 'fullname' => new lang_string('fullnamecourse'), 125 'shortname' => new lang_string('shortnamecourse'), 126 'idnumber' => new lang_string('idnumbercourse'), 127 'summary' => new lang_string('coursesummary'), 128 'format' => new lang_string('format'), 129 'startdate' => new lang_string('startdate'), 130 'enddate' => new lang_string('enddate'), 131 'visible' => new lang_string('coursevisibility'), 132 'groupmode' => new lang_string('groupmode', 'group'), 133 'groupmodeforce' => new lang_string('groupmodeforce', 'group'), 134 'lang' => new lang_string('forcelanguage'), 135 'calendartype' => new lang_string('forcecalendartype', 'calendar'), 136 'theme' => new lang_string('forcetheme'), 137 'enablecompletion' => new lang_string('enablecompletion', 'completion'), 138 'downloadcontent' => new lang_string('downloadcoursecontent', 'course'), 139 ]; 140 } 141 142 /** 143 * Check if this field is sortable 144 * 145 * @param string $fieldname 146 * @return bool 147 */ 148 protected function is_sortable(string $fieldname): bool { 149 // Some columns can't be sorted, like longtext or images. 150 $nonsortable = [ 151 'summary', 152 ]; 153 154 return !in_array($fieldname, $nonsortable); 155 } 156 157 /** 158 * Return appropriate column type for given user field 159 * 160 * @param string $coursefield 161 * @return int 162 */ 163 protected function get_course_field_type(string $coursefield): int { 164 switch ($coursefield) { 165 case 'downloadcontent': 166 case 'enablecompletion': 167 case 'groupmodeforce': 168 case 'visible': 169 $fieldtype = column::TYPE_BOOLEAN; 170 break; 171 case 'startdate': 172 case 'enddate': 173 $fieldtype = column::TYPE_TIMESTAMP; 174 break; 175 case 'summary': 176 $fieldtype = column::TYPE_LONGTEXT; 177 break; 178 case 'groupmode': 179 $fieldtype = column::TYPE_INTEGER; 180 break; 181 case 'calendartype': 182 case 'idnumber': 183 case 'format': 184 case 'fullname': 185 case 'lang': 186 case 'shortname': 187 case 'theme': 188 default: 189 $fieldtype = column::TYPE_TEXT; 190 break; 191 } 192 193 return $fieldtype; 194 } 195 196 /** 197 * Returns list of all available columns. 198 * 199 * These are all the columns available to use in any report that uses this entity. 200 * 201 * @return column[] 202 */ 203 protected function get_all_columns(): array { 204 global $DB; 205 206 $coursefields = $this->get_course_fields(); 207 $tablealias = $this->get_table_alias('course'); 208 $contexttablealias = $this->get_table_alias('context'); 209 210 // Columns course full name with link, course short name with link and course id with link. 211 $fields = [ 212 'coursefullnamewithlink' => 'fullname', 213 'courseshortnamewithlink' => 'shortname', 214 'courseidnumberewithlink' => 'idnumber', 215 ]; 216 foreach ($fields as $key => $field) { 217 $column = (new column( 218 $key, 219 new lang_string($key, 'core_reportbuilder'), 220 $this->get_entity_name() 221 )) 222 ->add_joins($this->get_joins()) 223 ->set_type(column::TYPE_TEXT) 224 ->add_fields("{$tablealias}.{$field} as $key, {$tablealias}.id") 225 ->set_is_sortable(true) 226 ->add_callback(static function(?string $value, stdClass $row): string { 227 if ($value === null) { 228 return ''; 229 } 230 231 context_helper::preload_from_record($row); 232 233 return html_writer::link(course_get_url($row->id), 234 format_string($value, true, ['context' => context_course::instance($row->id)])); 235 }); 236 237 // Join on the context table so that we can use it for formatting these columns later. 238 if ($key === 'coursefullnamewithlink') { 239 $join = "LEFT JOIN {context} {$contexttablealias} 240 ON {$contexttablealias}.contextlevel = " . CONTEXT_COURSE . " 241 AND {$contexttablealias}.instanceid = {$tablealias}.id"; 242 243 $column->add_join($join) 244 ->add_fields(context_helper::get_preload_record_columns_sql($contexttablealias)); 245 } 246 247 $columns[] = $column; 248 } 249 250 foreach ($coursefields as $coursefield => $coursefieldlang) { 251 $columntype = $this->get_course_field_type($coursefield); 252 253 $columnfieldsql = "{$tablealias}.{$coursefield}"; 254 if ($columntype === column::TYPE_LONGTEXT && $DB->get_dbfamily() === 'oracle') { 255 $columnfieldsql = $DB->sql_order_by_text($columnfieldsql, 1024); 256 } 257 258 $column = (new column( 259 $coursefield, 260 $coursefieldlang, 261 $this->get_entity_name() 262 )) 263 ->add_joins($this->get_joins()) 264 ->set_type($columntype) 265 ->add_field($columnfieldsql, $coursefield) 266 ->add_callback([$this, 'format'], $coursefield) 267 ->set_is_sortable($this->is_sortable($coursefield)); 268 269 // Join on the context table so that we can use it for formatting these columns later. 270 if ($coursefield === 'summary' || $coursefield === 'shortname' || $coursefield === 'fullname') { 271 $join = "LEFT JOIN {context} {$contexttablealias} 272 ON {$contexttablealias}.contextlevel = " . CONTEXT_COURSE . " 273 AND {$contexttablealias}.instanceid = {$tablealias}.id"; 274 275 $column->add_join($join) 276 ->add_field("{$tablealias}.id", 'courseid') 277 ->add_fields(context_helper::get_preload_record_columns_sql($contexttablealias)); 278 } 279 280 $columns[] = $column; 281 } 282 283 return $columns; 284 } 285 286 /** 287 * Returns list of all available filters 288 * 289 * @return array 290 */ 291 protected function get_all_filters(): array { 292 global $DB; 293 294 $filters = []; 295 $tablealias = $this->get_table_alias('course'); 296 297 $fields = $this->get_course_fields(); 298 foreach ($fields as $field => $name) { 299 // Filtering isn't supported for LONGTEXT fields on Oracle. 300 if ($this->get_course_field_type($field) === column::TYPE_LONGTEXT && 301 $DB->get_dbfamily() === 'oracle') { 302 303 continue; 304 } 305 306 $optionscallback = [static::class, 'get_options_for_' . $field]; 307 if (is_callable($optionscallback)) { 308 $filterclass = select::class; 309 } else if ($this->get_course_field_type($field) === column::TYPE_BOOLEAN) { 310 $filterclass = boolean_select::class; 311 } else if ($this->get_course_field_type($field) === column::TYPE_TIMESTAMP) { 312 $filterclass = date::class; 313 } else { 314 $filterclass = text::class; 315 } 316 317 $filter = (new filter( 318 $filterclass, 319 $field, 320 $name, 321 $this->get_entity_name(), 322 "{$tablealias}.$field" 323 )) 324 ->add_joins($this->get_joins()); 325 326 // Populate filter options by callback, if available. 327 if (is_callable($optionscallback)) { 328 $filter->set_options_callback($optionscallback); 329 } 330 331 $filters[] = $filter; 332 } 333 334 // We add our own custom course selector filter. 335 $filters[] = (new filter( 336 course_selector::class, 337 'courseselector', 338 new lang_string('courseselect', 'core_reportbuilder'), 339 $this->get_entity_name(), 340 "{$tablealias}.id" 341 )) 342 ->add_joins($this->get_joins()); 343 344 return $filters; 345 } 346 347 /** 348 * Gets list of options if the filter supports it 349 * 350 * @param string $fieldname 351 * @return null|array 352 */ 353 protected function get_options_for(string $fieldname): ?array { 354 static $cached = []; 355 if (!array_key_exists($fieldname, $cached)) { 356 $callable = [static::class, 'get_options_for_' . $fieldname]; 357 if (is_callable($callable)) { 358 $cached[$fieldname] = $callable(); 359 } else { 360 $cached[$fieldname] = null; 361 } 362 } 363 return $cached[$fieldname]; 364 } 365 366 /** 367 * List of options for the field groupmode. 368 * 369 * @return array 370 */ 371 public static function get_options_for_groupmode(): array { 372 return [ 373 NOGROUPS => get_string('groupsnone', 'group'), 374 SEPARATEGROUPS => get_string('groupsseparate', 'group'), 375 VISIBLEGROUPS => get_string('groupsvisible', 'group'), 376 ]; 377 } 378 379 /** 380 * List of options for the field format. 381 * 382 * @return array 383 */ 384 public static function get_options_for_format(): array { 385 global $CFG; 386 require_once($CFG->dirroot.'/course/lib.php'); 387 388 $options = []; 389 390 $courseformats = get_sorted_course_formats(true); 391 foreach ($courseformats as $courseformat) { 392 $options[$courseformat] = get_string('pluginname', "format_{$courseformat}"); 393 } 394 395 return $options; 396 } 397 398 /** 399 * List of options for the field theme. 400 * 401 * @return array 402 */ 403 public static function get_options_for_theme(): array { 404 $options = []; 405 406 $themeobjects = get_list_of_themes(); 407 foreach ($themeobjects as $key => $theme) { 408 if (empty($theme->hidefromselector)) { 409 $options[$key] = get_string('pluginname', "theme_{$theme->name}"); 410 } 411 } 412 413 return $options; 414 } 415 416 /** 417 * List of options for the field lang. 418 * 419 * @return array 420 */ 421 public static function get_options_for_lang(): array { 422 return get_string_manager()->get_list_of_translations(); 423 } 424 425 /** 426 * List of options for the field. 427 * 428 * @return array 429 */ 430 public static function get_options_for_calendartype(): array { 431 return \core_calendar\type_factory::get_list_of_calendar_types(); 432 } 433 434 /** 435 * Formats the course field for display. 436 * 437 * @param mixed $value Current field value. 438 * @param stdClass $row Complete row. 439 * @param string $fieldname Name of the field to format. 440 * @return string 441 */ 442 public function format($value, stdClass $row, string $fieldname): string { 443 if ($this->get_course_field_type($fieldname) === column::TYPE_TIMESTAMP) { 444 return format::userdate($value, $row); 445 } 446 447 $options = $this->get_options_for($fieldname); 448 if ($options !== null && array_key_exists($value, $options)) { 449 return $options[$value]; 450 } 451 452 if ($this->get_course_field_type($fieldname) === column::TYPE_BOOLEAN) { 453 return format::boolean_as_text($value); 454 } 455 456 if (in_array($fieldname, ['fullname', 'shortname'])) { 457 if (!$row->courseid) { 458 return ''; 459 } 460 context_helper::preload_from_record($row); 461 $context = context_course::instance($row->courseid); 462 return format_string($value, true, ['context' => $context->id, 'escape' => false]); 463 } 464 465 if (in_array($fieldname, ['summary'])) { 466 if (!$row->courseid) { 467 return ''; 468 } 469 context_helper::preload_from_record($row); 470 $context = context_course::instance($row->courseid); 471 $summary = file_rewrite_pluginfile_urls($row->summary, 'pluginfile.php', $context->id, 'course', 'summary', null); 472 return format_text($summary); 473 } 474 475 return s($value); 476 } 477 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body