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 namespace mod_lti\reportbuilder\local\systemreports; 18 19 use core_reportbuilder\local\helpers\database; 20 use core_reportbuilder\local\report\column; 21 use mod_lti\reportbuilder\local\entities\tool_types; 22 use core_reportbuilder\system_report; 23 24 /** 25 * Course external tools list system report class implementation. 26 * 27 * @package mod_lti 28 * @copyright 2023 Jake Dallimore <jrhdallimore@gmail.com> 29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 */ 31 class course_external_tools_list extends system_report { 32 33 /** @var \stdClass the course to constrain the report to. */ 34 protected \stdClass $course; 35 36 /** @var int the usage count for the tool represented in a row, and set by row_callback(). */ 37 protected int $perrowtoolusage = 0; 38 39 /** 40 * Initialise report, we need to set the main table, load our entities and set columns/filters 41 */ 42 protected function initialise(): void { 43 global $DB, $CFG; 44 require_once($CFG->dirroot . '/mod/lti/locallib.php'); 45 46 $this->course = get_course($this->get_context()->instanceid); 47 48 // Our main entity, it contains all the column definitions that we need. 49 $entitymain = new tool_types(); 50 $entitymainalias = $entitymain->get_table_alias('lti_types'); 51 52 $this->set_main_table('lti_types', $entitymainalias); 53 $this->add_entity($entitymain); 54 55 // Now we can call our helper methods to add the content we want to include in the report. 56 $this->add_columns($entitymain); 57 $this->add_filters(); 58 $this->add_actions(); 59 60 // We need id and course in the actions column, without entity prefixes, so add these here. 61 // We also need access to the tool usage count in a few places (the usage column as well as the actions column). 62 $ti = database::generate_param_name(); // Tool instance param. 63 $this->add_base_fields("{$entitymainalias}.id, {$entitymainalias}.course, ". 64 "(SELECT COUNT($ti.id) 65 FROM {lti} $ti 66 WHERE $ti.typeid = {$entitymainalias}.id) AS toolusage"); 67 68 // Join the types_categories table, to include only tools available to the current course's category. 69 $cattablealias = database::generate_alias(); 70 $joinsql = "LEFT JOIN {lti_types_categories} {$cattablealias} 71 ON ({$cattablealias}.typeid = {$entitymainalias}.id)"; 72 $this->add_join($joinsql); 73 74 // Scope the report to the course context and include only those tools available to the category. 75 $paramprefix = database::generate_param_name(); 76 $coursevisibleparam = database::generate_param_name(); 77 $categoryparam = database::generate_param_name(); 78 $toolstateparam = database::generate_param_name(); 79 [$insql, $params] = $DB->get_in_or_equal([get_site()->id, $this->course->id], SQL_PARAMS_NAMED, "{$paramprefix}_"); 80 $wheresql = "{$entitymainalias}.course {$insql} ". 81 "AND {$entitymainalias}.coursevisible NOT IN (:{$coursevisibleparam}) ". 82 "AND ({$cattablealias}.id IS NULL OR {$cattablealias}.categoryid = :{$categoryparam}) ". 83 "AND {$entitymainalias}.state = :{$toolstateparam}"; 84 $params = array_merge( 85 $params, 86 [ 87 $coursevisibleparam => LTI_COURSEVISIBLE_NO, 88 $categoryparam => $this->course->category, 89 $toolstateparam => LTI_TOOL_STATE_CONFIGURED 90 ] 91 ); 92 $this->add_base_condition_sql($wheresql, $params); 93 94 $this->set_downloadable(false, get_string('pluginname', 'mod_lti')); 95 $this->set_default_per_page(10); 96 $this->set_default_no_results_notice(null); 97 } 98 99 /** 100 * Validates access to view this report 101 * 102 * @return bool 103 */ 104 protected function can_view(): bool { 105 return has_capability('mod/lti:addpreconfiguredinstance', $this->get_context()); 106 } 107 108 public function row_callback(\stdClass $row): void { 109 $this->perrowtoolusage = $row->toolusage; 110 } 111 112 /** 113 * Adds the columns we want to display in the report. 114 * 115 * They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their 116 * unique identifier 117 * @param tool_types $tooltypesentity 118 * @return void 119 */ 120 protected function add_columns(tool_types $tooltypesentity): void { 121 $entitymainalias = $tooltypesentity->get_table_alias('lti_types'); 122 123 $columns = [ 124 'tool_types:name', 125 'tool_types:description', 126 ]; 127 128 $this->add_columns_from_entities($columns); 129 130 // Tool usage column using a custom SQL subquery (defined in initialise method) to count tool instances within the course. 131 // TODO: This should be replaced with proper column aggregation once that's added to system_report instances in MDL-76392. 132 $this->add_column(new column( 133 'usage', 134 new \lang_string('usage', 'mod_lti'), 135 $tooltypesentity->get_entity_name() 136 )) 137 ->set_type(column::TYPE_INTEGER) 138 ->set_is_sortable(true) 139 ->add_field("{$entitymainalias}.id") 140 ->add_callback(fn() => $this->perrowtoolusage); 141 142 // Enable toggle column. 143 $this->add_column((new column( 144 'showinactivitychooser', 145 new \lang_string('showinactivitychooser', 'mod_lti'), 146 $tooltypesentity->get_entity_name() 147 )) 148 // Site tools can be overridden on course level. 149 ->add_join("LEFT JOIN {lti_coursevisible} lc ON lc.typeid = {$entitymainalias}.id AND lc.courseid = " . $this->course->id) 150 ->set_type(column::TYPE_INTEGER) 151 ->add_fields("{$entitymainalias}.id, {$entitymainalias}.coursevisible, lc.coursevisible as coursevisibleoverridden") 152 ->set_is_sortable(false) 153 ->set_callback(function(int $id, \stdClass $row): string { 154 global $PAGE; 155 $coursevisible = $row->coursevisible; 156 $courseid = $this->course->id; 157 if (!empty($row->coursevisibleoverridden)) { 158 $coursevisible = $row->coursevisibleoverridden; 159 } 160 161 if ($coursevisible == LTI_COURSEVISIBLE_ACTIVITYCHOOSER) { 162 $coursevisible = true; 163 } else { 164 $coursevisible = false; 165 } 166 167 $renderer = $PAGE->get_renderer('core_reportbuilder'); 168 $attributes = [ 169 ['name' => 'id', 'value' => $row->id], 170 ['name' => 'courseid', 'value' => $courseid], 171 ['name' => 'action', 'value' => 'showinactivitychooser-toggle'], 172 ['name' => 'state', 'value' => $coursevisible], 173 ]; 174 $label = $coursevisible ? get_string('dontshowinactivitychooser', 'mod_lti') 175 : get_string('showinactivitychooser', 'mod_lti'); 176 177 $disabled = !has_capability('mod/lti:addcoursetool', \context_course::instance($courseid)); 178 179 return $renderer->render_from_template('core/toggle', [ 180 'id' => 'showinactivitychooser-toggle-' . $row->id, 181 'checked' => $coursevisible, 182 'disabled' => $disabled, 183 'dataattributes' => $attributes, 184 'label' => $label, 185 'labelclasses' => 'sr-only' 186 ]); 187 }) 188 ); 189 190 // Attempt to create a dummy actions column, working around the limitations of the official actions feature. 191 $this->add_column(new column( 192 'actions', new \lang_string('actions'), 193 $tooltypesentity->get_entity_name() 194 )) 195 ->set_type(column::TYPE_TEXT) 196 ->set_is_sortable(false) 197 ->add_fields("{$entitymainalias}.id, {$entitymainalias}.course, {$entitymainalias}.name") 198 ->add_callback(function($field, $row) { 199 global $OUTPUT; 200 201 // Lock actions for site-level preconfigured tools. 202 if (get_site()->id == $row->course) { 203 return \html_writer::div( 204 \html_writer::div( 205 $OUTPUT->pix_icon('t/locked', get_string('courseexternaltoolsnoeditpermissions', 'mod_lti') 206 ), 'tool-action-icon-container'), 'd-flex justify-content-end' 207 ); 208 } 209 210 // Lock actions when the user can't add course tools. 211 if (!has_capability('mod/lti:addcoursetool', \context_course::instance($row->course))) { 212 return \html_writer::div( 213 \html_writer::div( 214 $OUTPUT->pix_icon('t/locked', get_string('courseexternaltoolsnoeditpermissions', 'mod_lti') 215 ), 'tool-action-icon-container'), 'd-flex justify-content-end' 216 ); 217 } 218 219 // Build and display an action menu. 220 $menu = new \action_menu(); 221 $menu->set_menu_trigger($OUTPUT->pix_icon('i/moremenu', get_string('actions', 'core')), 222 'btn btn-icon d-flex align-items-center justify-content-center'); // TODO check 'actions' lang string with UX. 223 224 $menu->add(new \action_menu_link( 225 new \moodle_url('/mod/lti/coursetooledit.php', ['course' => $row->course, 'typeid' => $row->id]), 226 null, 227 get_string('edit', 'core'), 228 null 229 )); 230 231 $menu->add(new \action_menu_link( 232 new \moodle_url('#'), 233 null, 234 get_string('delete', 'core'), 235 null, 236 [ 237 'data-action' => 'course-tool-delete', 238 'data-course-tool-id' => $row->id, 239 'data-course-tool-name' => $row->name, 240 'data-course-tool-usage' => $this->perrowtoolusage 241 ], 242 )); 243 244 return $OUTPUT->render($menu); 245 }); 246 247 // Default sorting. 248 $this->set_initial_sort_column('tool_types:name', SORT_ASC); 249 } 250 251 /** 252 * Add any actions for this report. 253 * 254 * @return void 255 */ 256 protected function add_actions(): void { 257 } 258 259 /** 260 * Adds the filters we want to display in the report 261 * 262 * They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their 263 * unique identifier 264 */ 265 protected function add_filters(): void { 266 267 $this->add_filters_from_entities([]); 268 } 269 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body