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 core\context_helper; 22 use core_reportbuilder\local\entities\base; 23 use core_reportbuilder\local\filters\{select, text}; 24 use core_reportbuilder\local\report\{column, filter}; 25 use html_writer; 26 use lang_string; 27 use stdClass; 28 29 /** 30 * Context entity 31 * 32 * @package core 33 * @copyright 2023 Paul Holden <paulh@moodle.com> 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class context extends base { 37 38 /** 39 * Database tables that this entity uses and their default aliases 40 * 41 * @return array 42 */ 43 protected function get_default_table_aliases(): array { 44 return ['context' => 'ctx']; 45 } 46 47 /** 48 * The default title for this entity in the list of columns/conditions/filters in the report builder 49 * 50 * @return lang_string 51 */ 52 protected function get_default_entity_title(): lang_string { 53 return new lang_string('context'); 54 } 55 56 /** 57 * Initialise the entity 58 * 59 * @return base 60 */ 61 public function initialise(): base { 62 $columns = $this->get_all_columns(); 63 foreach ($columns as $column) { 64 $this->add_column($column); 65 } 66 67 // All the filters defined by the entity can also be used as conditions. 68 $filters = $this->get_all_filters(); 69 foreach ($filters as $filter) { 70 $this 71 ->add_filter($filter) 72 ->add_condition($filter); 73 } 74 75 return $this; 76 } 77 78 /** 79 * Returns list of all available columns 80 * 81 * @return column[] 82 */ 83 protected function get_all_columns(): array { 84 global $DB; 85 86 $contextalias = $this->get_table_alias('context'); 87 88 // Name. 89 $columns[] = (new column( 90 'name', 91 new lang_string('contextname'), 92 $this->get_entity_name() 93 )) 94 ->add_joins($this->get_joins()) 95 ->set_type(column::TYPE_TEXT) 96 ->add_fields(context_helper::get_preload_record_columns_sql($contextalias)) 97 // Sorting may not order alphabetically, but will at least group contexts together. 98 ->set_is_sortable(true) 99 ->add_callback(static function($contextid, stdClass $context): string { 100 if ($contextid === null) { 101 return ''; 102 } 103 104 context_helper::preload_from_record($context); 105 return context_helper::instance_by_id($contextid)->get_context_name(); 106 }); 107 108 // Link. 109 $columns[] = (new column( 110 'link', 111 new lang_string('contexturl'), 112 $this->get_entity_name() 113 )) 114 ->add_joins($this->get_joins()) 115 ->set_type(column::TYPE_TEXT) 116 ->add_fields(context_helper::get_preload_record_columns_sql($contextalias)) 117 // Sorting may not order alphabetically, but will at least group contexts together. 118 ->set_is_sortable(true) 119 ->add_callback(static function($contextid, stdClass $context): string { 120 if ($contextid === null) { 121 return ''; 122 } 123 124 context_helper::preload_from_record($context); 125 $context = context_helper::instance_by_id($contextid); 126 127 return html_writer::link($context->get_url(), $context->get_context_name()); 128 }); 129 130 // Level. 131 $columns[] = (new column( 132 'level', 133 new lang_string('contextlevel'), 134 $this->get_entity_name() 135 )) 136 ->add_joins($this->get_joins()) 137 ->set_type(column::TYPE_INTEGER) 138 ->add_fields("{$contextalias}.contextlevel") 139 ->set_is_sortable(true) 140 // It doesn't make sense to offer integer aggregation methods for this column. 141 ->set_disabled_aggregation(['avg', 'max', 'min', 'sum']) 142 ->add_callback(static function(?int $level): string { 143 if ($level === null) { 144 return ''; 145 } 146 147 return context_helper::get_level_name($level); 148 }); 149 150 // Path. 151 $columns[] = (new column( 152 'path', 153 new lang_string('path'), 154 $this->get_entity_name() 155 )) 156 ->add_joins($this->get_joins()) 157 ->set_type(column::TYPE_TEXT) 158 ->add_field("{$contextalias}.path") 159 ->set_is_sortable(true); 160 161 // Parent (note we select the parent path in SQL, so that aggregation/grouping is on the parent data itself). 162 $columns[] = (new column( 163 'parent', 164 new lang_string('contextparent'), 165 $this->get_entity_name() 166 )) 167 ->add_joins($this->get_joins()) 168 ->set_type(column::TYPE_TEXT) 169 // The "path" column looks like "/1/2/3", for context ID 3. In order to select/group by the parent context, we 170 // concatenate a trailing slash (to prevent partial matches, e.g. "/1/2/31"), then replace "/3/" with empty string. 171 ->add_field(" 172 REPLACE( 173 " . $DB->sql_concat("{$contextalias}.path", "'/'") . ", 174 " . $DB->sql_concat("'/'", $DB->sql_cast_to_char("{$contextalias}.id"), "'/'") . ", 175 '' 176 )", 'parent' 177 ) 178 // Sorting may not order alphabetically, but will at least group contexts together. 179 ->set_is_sortable(true) 180 ->add_callback(static function (?string $parent): string { 181 182 // System level (no parent) or null. 183 if ($parent === '' || $parent === null) { 184 return ''; 185 } 186 187 $contextids = explode('/', $parent); 188 $contextid = (int) array_pop($contextids); 189 190 return context_helper::instance_by_id($contextid)->get_context_name(); 191 }); 192 193 return $columns; 194 } 195 196 /** 197 * Return list of all available filters 198 * 199 * @return filter[] 200 */ 201 protected function get_all_filters(): array { 202 $contextalias = $this->get_table_alias('context'); 203 204 // Level. 205 $filters[] = (new filter( 206 select::class, 207 'level', 208 new lang_string('contextlevel'), 209 $this->get_entity_name(), 210 "{$contextalias}.contextlevel" 211 )) 212 ->add_joins($this->get_joins()) 213 ->set_options_callback(static function(): array { 214 $levels = context_helper::get_all_levels(); 215 216 return array_map(static function(string $levelclass): string { 217 return $levelclass::get_level_name(); 218 }, $levels); 219 }); 220 221 // Path. 222 $filters[] = (new filter( 223 text::class, 224 'path', 225 new lang_string('path'), 226 $this->get_entity_name(), 227 "{$contextalias}.path" 228 )) 229 ->add_joins($this->get_joins()); 230 231 return $filters; 232 } 233 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body