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_badges\reportbuilder\local\systemreports; 20 21 use core\context\{course, system}; 22 use core_badges\reportbuilder\local\entities\badge; 23 use core_reportbuilder\local\helpers\database; 24 use core_reportbuilder\local\report\{action, column}; 25 use core_reportbuilder\system_report; 26 use lang_string; 27 use moodle_url; 28 use pix_icon; 29 use stdClass; 30 31 defined('MOODLE_INTERNAL') || die; 32 33 global $CFG; 34 require_once("{$CFG->libdir}/badgeslib.php"); 35 36 /** 37 * Badges system report class implementation 38 * 39 * @package core_badges 40 * @copyright 2023 David Carrillo <davidmc@moodle.com> 41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 42 */ 43 class badges extends system_report { 44 45 /** 46 * Initialise report, we need to set the main table, load our entities and set columns/filters 47 */ 48 protected function initialise(): void { 49 // Our main entity, it contains all of the column definitions that we need. 50 $badgeentity = new badge(); 51 $entityalias = $badgeentity->get_table_alias('badge'); 52 53 $this->set_main_table('badge', $entityalias); 54 $this->add_entity($badgeentity); 55 56 $paramtype = database::generate_param_name(); 57 $context = $this->get_context(); 58 if ($context instanceof system) { 59 $type = BADGE_TYPE_SITE; 60 $this->add_base_condition_sql("{$entityalias}.type = :$paramtype", [$paramtype => $type]); 61 } else { 62 $type = BADGE_TYPE_COURSE; 63 $paramcourseid = database::generate_param_name(); 64 $this->add_base_condition_sql("{$entityalias}.type = :$paramtype AND {$entityalias}.courseid = :$paramcourseid", 65 [$paramtype => $type, $paramcourseid => $context->instanceid]); 66 } 67 68 // Any columns required by actions should be defined here to ensure they're always available. 69 $this->add_base_fields("{$entityalias}.id, {$entityalias}.type, {$entityalias}.courseid, {$entityalias}.status"); 70 71 // Now we can call our helper methods to add the content we want to include in the report. 72 $this->add_columns($badgeentity); 73 $this->add_filters(); 74 $this->add_actions(); 75 76 // Set initial sorting by name. 77 $this->set_initial_sort_column('badge:namewithlink', SORT_ASC); 78 79 // Set if report can be downloaded. 80 $this->set_downloadable(false); 81 } 82 83 /** 84 * Validates access to view this report 85 * 86 * @return bool 87 */ 88 protected function can_view(): bool { 89 return has_any_capability([ 90 'moodle/badges:viewawarded', 91 'moodle/badges:createbadge', 92 'moodle/badges:awardbadge', 93 'moodle/badges:configurecriteria', 94 'moodle/badges:configuremessages', 95 'moodle/badges:configuredetails', 96 'moodle/badges:deletebadge'], $this->get_context()); 97 } 98 99 /** 100 * Adds the columns we want to display in the report 101 * 102 * They are provided by the entities we previously added in the {@see initialise} method, referencing each by their 103 * unique identifier. If custom columns are needed just for this report, they can be defined here. 104 * 105 * @param badge $badgeentity 106 */ 107 public function add_columns(badge $badgeentity): void { 108 $columns = [ 109 'badge:image', 110 'badge:namewithlink', 111 'badge:status', 112 'badge:criteria', 113 ]; 114 115 $this->add_columns_from_entities($columns); 116 117 // Issued badges column. 118 // TODO: Move this column to the entity when MDL-76392 is integrated. 119 $tempbadgealias = database::generate_alias(); 120 $badgeentityalias = $badgeentity->get_table_alias('badge'); 121 $this->add_column((new column( 122 'issued', 123 new lang_string('awards', 'core_badges'), 124 $badgeentity->get_entity_name() 125 )) 126 ->add_joins($this->get_joins()) 127 ->set_type(column::TYPE_INTEGER) 128 ->add_field("(SELECT COUNT({$tempbadgealias}.userid) 129 FROM {badge_issued} {$tempbadgealias} 130 INNER JOIN {user} u 131 ON {$tempbadgealias}.userid = u.id 132 WHERE {$tempbadgealias}.badgeid = {$badgeentityalias}.id AND u.deleted = 0)", 'issued') 133 ->set_is_sortable(true)); 134 135 // Remove title from image column. 136 $this->get_column('badge:image')->set_title(null); 137 138 // Change title from namewithlink column. 139 $this->get_column('badge:namewithlink')->set_title(new lang_string('name')); 140 } 141 142 /** 143 * Adds the filters we want to display in the report 144 * 145 * They are all provided by the entities we previously added in the {@see initialise} method, referencing each by their 146 * unique identifier 147 */ 148 protected function add_filters(): void { 149 $filters = [ 150 'badge:name', 151 'badge:status', 152 ]; 153 $this->add_filters_from_entities($filters); 154 } 155 156 /** 157 * Add the system report actions. An extra column will be appended to each row, containing all actions added here 158 * 159 * Note the use of ":id" placeholder which will be substituted according to actual values in the row 160 */ 161 protected function add_actions(): void { 162 // Activate badge. 163 $this->add_action((new action( 164 new moodle_url('/badges/action.php', [ 165 'id' => ':id', 166 'activate' => true, 167 'return' => ':return', 168 ]), 169 new pix_icon('t/show', '', 'core'), 170 [], 171 false, 172 new lang_string('activate', 'badges') 173 ))->add_callback(static function(stdclass $row): bool { 174 $badge = new \core_badges\badge($row->id); 175 176 // Populate the return URL. 177 $row->return = (new moodle_url('/badges/index.php', 178 ['type' => $badge->type, 'id' => (int) $badge->courseid]))->out_as_local_url(false); 179 180 return has_capability('moodle/badges:configuredetails', $badge->get_context()) && 181 $badge->has_criteria() && 182 ($row->status == BADGE_STATUS_INACTIVE || $row->status == BADGE_STATUS_INACTIVE_LOCKED); 183 184 })); 185 186 // Deactivate badge. 187 $this->add_action((new action( 188 new moodle_url('/badges/index.php', [ 189 'lock' => ':id', 190 'sesskey' => sesskey(), 191 'type' => ':type', 192 'id' => ':courseid', 193 ]), 194 new pix_icon('t/hide', '', 'core'), 195 [], 196 false, 197 new lang_string('deactivate', 'badges') 198 ))->add_callback(static function(stdclass $row): bool { 199 $badge = new \core_badges\badge($row->id); 200 return has_capability('moodle/badges:configuredetails', $badge->get_context()) && 201 $badge->has_criteria() && 202 $row->status != BADGE_STATUS_INACTIVE && $row->status != BADGE_STATUS_INACTIVE_LOCKED; 203 })); 204 205 // Award badge manually. 206 $this->add_action((new action( 207 new moodle_url('/badges/award.php', [ 208 'id' => ':id', 209 ]), 210 new pix_icon('t/award', '', 'core'), 211 [], 212 false, 213 new lang_string('award', 'badges') 214 ))->add_callback(static function(stdclass $row): bool { 215 $badge = new \core_badges\badge($row->id); 216 return has_capability('moodle/badges:awardbadge', $badge->get_context()) && 217 $badge->has_manual_award_criteria() && 218 $badge->is_active(); 219 })); 220 221 // Edit action. 222 $this->add_action((new action( 223 new moodle_url('/badges/edit.php', [ 224 'id' => ':id', 225 'action' => 'badge', 226 ]), 227 new pix_icon('t/edit', '', 'core'), 228 [], 229 false, 230 new lang_string('edit', 'core') 231 ))->add_callback(static function(stdclass $row): bool { 232 $context = self::get_badge_context((int)$row->type, (int)$row->courseid); 233 return has_capability('moodle/badges:configuredetails', $context); 234 235 })); 236 237 // Duplicate action. 238 $this->add_action((new action( 239 new moodle_url('/badges/action.php', [ 240 'id' => ':id', 241 'copy' => 1, 242 'sesskey' => sesskey(), 243 ]), 244 new pix_icon('t/copy', '', 'core'), 245 [], 246 false, 247 new lang_string('copy', 'badges') 248 ))->add_callback(static function(stdclass $row): bool { 249 $context = self::get_badge_context((int)$row->type, (int)$row->courseid); 250 return has_capability('moodle/badges:createbadge', $context); 251 })); 252 253 // Delete action. 254 $this->add_action((new action( 255 new moodle_url('/badges/index.php', [ 256 'delete' => ':id', 257 'type' => ':type', 258 'id' => ':courseid', 259 ]), 260 new pix_icon('t/delete', '', 'core'), 261 [], 262 false, 263 new lang_string('delete', 'core') 264 ))->add_callback(static function(stdclass $row): bool { 265 $context = self::get_badge_context((int)$row->type, (int)$row->courseid); 266 return has_capability('moodle/badges:deletebadge', $context); 267 })); 268 } 269 270 /** 271 * Return badge context based on type and courseid 272 * 273 * @param int $type 274 * @param int $courseid 275 * @return \core\context 276 * @throws \coding_exception 277 */ 278 private static function get_badge_context(int $type, int $courseid): \core\context { 279 switch ($type) { 280 case BADGE_TYPE_SITE: 281 return system::instance(); 282 case BADGE_TYPE_COURSE: 283 return course::instance($courseid); 284 default: 285 throw new \coding_exception('Wrong context'); 286 } 287 } 288 289 /** 290 * CSS classes to add to the row 291 * 292 * @param stdClass $row 293 * @return string 294 */ 295 public function get_row_class(stdClass $row): string { 296 return ($row->status == BADGE_STATUS_INACTIVE_LOCKED || $row->status == BADGE_STATUS_INACTIVE) ? 'text-muted' : ''; 297 } 298 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body