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 /** 18 * Contains the class used for the displaying the expired contexts table. 19 * 20 * @package tool_dataprivacy 21 * @copyright 2018 Jun Pataleta <jun@moodle.com> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 namespace tool_dataprivacy\output; 25 defined('MOODLE_INTERNAL') || die(); 26 27 require_once($CFG->libdir . '/tablelib.php'); 28 29 use coding_exception; 30 use context_helper; 31 use dml_exception; 32 use Exception; 33 use html_writer; 34 use pix_icon; 35 use stdClass; 36 use table_sql; 37 use tool_dataprivacy\api; 38 use tool_dataprivacy\expired_context; 39 use tool_dataprivacy\external\purpose_exporter; 40 use tool_dataprivacy\purpose; 41 42 defined('MOODLE_INTERNAL') || die; 43 44 /** 45 * The class for displaying the expired contexts table. 46 * 47 * @copyright 2018 Jun Pataleta <jun@moodle.com> 48 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 49 */ 50 class expired_contexts_table extends table_sql { 51 52 /** @var int The context level acting as a filter for this table. */ 53 protected $contextlevel = null; 54 55 /** 56 * @var bool $selectall Has the user selected all users on the page? True by default. 57 */ 58 protected $selectall = true; 59 60 /** @var purpose[] Array of purposes by their id. */ 61 protected $purposes = []; 62 63 /** @var purpose[] Map of context => purpose. */ 64 protected $purposemap = []; 65 66 /** @var array List of roles. */ 67 protected $roles = []; 68 69 /** 70 * expired_contexts_table constructor. 71 * 72 * @param int|null $contextlevel 73 * @throws coding_exception 74 */ 75 public function __construct($contextlevel = null) { 76 parent::__construct('expired-contexts-table'); 77 78 $this->contextlevel = $contextlevel; 79 80 $columnheaders = [ 81 'name' => get_string('name'), 82 'info' => get_string('info'), 83 'purpose' => get_string('purpose', 'tool_dataprivacy'), 84 'category' => get_string('category', 'tool_dataprivacy'), 85 'retentionperiod' => get_string('retentionperiod', 'tool_dataprivacy'), 86 'tobedeleted' => get_string('tobedeleted', 'tool_dataprivacy'), 87 'timecreated' => get_string('expiry', 'tool_dataprivacy'), 88 ]; 89 $checkboxattrs = [ 90 'title' => get_string('selectall'), 91 'data-action' => 'selectall' 92 ]; 93 $columnheaders['select'] = html_writer::checkbox('selectall', 1, true, null, $checkboxattrs); 94 95 $this->define_columns(array_keys($columnheaders)); 96 $this->define_headers(array_values($columnheaders)); 97 $this->no_sorting('name'); 98 $this->no_sorting('select'); 99 $this->no_sorting('info'); 100 $this->no_sorting('purpose'); 101 $this->no_sorting('category'); 102 $this->no_sorting('retentionperiod'); 103 $this->no_sorting('tobedeleted'); 104 105 // Make this table sorted by first name by default. 106 $this->sortable(true, 'timecreated'); 107 108 // We use roles in several places. 109 $this->roles = role_get_names(); 110 } 111 112 /** 113 * The context name column. 114 * 115 * @param stdClass $expiredctx The row data. 116 * @return string 117 * @throws coding_exception 118 */ 119 public function col_name($expiredctx) { 120 global $OUTPUT; 121 $context = context_helper::instance_by_id($expiredctx->get('contextid')); 122 $parent = $context->get_parent_context(); 123 $contextdata = (object)[ 124 'name' => $context->get_context_name(false, true), 125 'parent' => $parent->get_context_name(false, true), 126 ]; 127 $fullcontexts = $context->get_parent_contexts(true); 128 $contextsinpath = []; 129 foreach ($fullcontexts as $contextinpath) { 130 $contextsinpath[] = $contextinpath->get_context_name(false, true); 131 } 132 $infoicon = new pix_icon('i/info', implode(' / ', array_reverse($contextsinpath))); 133 $infoiconhtml = $OUTPUT->render($infoicon); 134 $name = html_writer::span(get_string('nameandparent', 'tool_dataprivacy', $contextdata), 'mr-1'); 135 136 return $name . $infoiconhtml; 137 } 138 139 /** 140 * The context information column. 141 * 142 * @param stdClass $expiredctx The row data. 143 * @return string 144 * @throws coding_exception 145 */ 146 public function col_info($expiredctx) { 147 global $OUTPUT; 148 149 $context = context_helper::instance_by_id($expiredctx->get('contextid')); 150 151 $children = $context->get_child_contexts(); 152 if (empty($children)) { 153 return get_string('none'); 154 } else { 155 $childnames = []; 156 foreach ($children as $child) { 157 $childnames[] = $child->get_context_name(false, true); 158 } 159 $infoicon = new pix_icon('i/info', implode(', ', $childnames)); 160 $infoiconhtml = $OUTPUT->render($infoicon); 161 $name = html_writer::span(get_string('nchildren', 'tool_dataprivacy', count($children)), 'mr-1'); 162 163 return $name . $infoiconhtml; 164 } 165 } 166 167 /** 168 * The category name column. 169 * 170 * @param stdClass $expiredctx The row data. 171 * @return mixed 172 * @throws coding_exception 173 * @throws dml_exception 174 */ 175 public function col_category($expiredctx) { 176 $context = context_helper::instance_by_id($expiredctx->get('contextid')); 177 $category = api::get_effective_context_category($context); 178 179 return s($category->get('name')); 180 } 181 182 /** 183 * The purpose column. 184 * 185 * @param stdClass $expiredctx The row data. 186 * @return string 187 * @throws coding_exception 188 */ 189 public function col_purpose($expiredctx) { 190 $purpose = $this->get_purpose_for_expiry($expiredctx); 191 192 return s($purpose->get('name')); 193 } 194 195 /** 196 * The retention period column. 197 * 198 * @param stdClass $expiredctx The row data. 199 * @return string 200 */ 201 public function col_retentionperiod($expiredctx) { 202 $purpose = $this->get_purpose_for_expiry($expiredctx); 203 204 $expiries = []; 205 206 $expiry = html_writer::tag('dt', get_string('default'), ['class' => 'col-sm-3']); 207 if ($expiredctx->get('defaultexpired')) { 208 $expiries[get_string('default')] = get_string('expiredrolewithretention', 'tool_dataprivacy', (object) [ 209 'retention' => api::format_retention_period(new \DateInterval($purpose->get('retentionperiod'))), 210 ]); 211 } else { 212 $expiries[get_string('default')] = get_string('unexpiredrolewithretention', 'tool_dataprivacy', (object) [ 213 'retention' => api::format_retention_period(new \DateInterval($purpose->get('retentionperiod'))), 214 ]); 215 } 216 217 if (!$expiredctx->is_fully_expired()) { 218 $purposeoverrides = $purpose->get_purpose_overrides(); 219 220 foreach ($expiredctx->get('unexpiredroles') as $roleid) { 221 $role = $this->roles[$roleid]; 222 $override = $purposeoverrides[$roleid]; 223 224 $expiries[$role->localname] = get_string('unexpiredrolewithretention', 'tool_dataprivacy', (object) [ 225 'retention' => api::format_retention_period(new \DateInterval($override->get('retentionperiod'))), 226 ]); 227 } 228 229 foreach ($expiredctx->get('expiredroles') as $roleid) { 230 $role = $this->roles[$roleid]; 231 $override = $purposeoverrides[$roleid]; 232 233 $expiries[$role->localname] = get_string('expiredrolewithretention', 'tool_dataprivacy', (object) [ 234 'retention' => api::format_retention_period(new \DateInterval($override->get('retentionperiod'))), 235 ]); 236 } 237 } 238 239 $output = array_map(function($rolename, $expiry) { 240 $return = html_writer::tag('dt', $rolename, ['class' => 'col-sm-3']); 241 $return .= html_writer::tag('dd', $expiry, ['class' => 'col-sm-9']); 242 243 return $return; 244 }, array_keys($expiries), $expiries); 245 246 return html_writer::tag('dl', implode($output), ['class' => 'row']); 247 } 248 249 /** 250 * The timecreated a.k.a. the context expiry date column. 251 * 252 * @param stdClass $expiredctx The row data. 253 * @return string 254 */ 255 public function col_timecreated($expiredctx) { 256 return userdate($expiredctx->get('timecreated')); 257 } 258 259 /** 260 * Generate the select column. 261 * 262 * @param stdClass $expiredctx The row data. 263 * @return string 264 */ 265 public function col_select($expiredctx) { 266 $id = $expiredctx->get('id'); 267 return html_writer::checkbox('expiredcontext_' . $id, $id, $this->selectall, '', ['class' => 'selectcontext']); 268 } 269 270 /** 271 * Formatting for the 'tobedeleted' column which indicates in a friendlier fashion whose data will be removed. 272 * 273 * @param stdClass $expiredctx The row data. 274 * @return string 275 */ 276 public function col_tobedeleted($expiredctx) { 277 if ($expiredctx->is_fully_expired()) { 278 return get_string('defaultexpired', 'tool_dataprivacy'); 279 } 280 281 $purpose = $this->get_purpose_for_expiry($expiredctx); 282 283 $a = (object) []; 284 285 $expiredroles = []; 286 foreach ($expiredctx->get('expiredroles') as $roleid) { 287 $expiredroles[] = html_writer::tag('li', $this->roles[$roleid]->localname); 288 } 289 $a->expired = html_writer::tag('ul', implode($expiredroles)); 290 291 $unexpiredroles = []; 292 foreach ($expiredctx->get('unexpiredroles') as $roleid) { 293 $unexpiredroles[] = html_writer::tag('li', $this->roles[$roleid]->localname); 294 } 295 $a->unexpired = html_writer::tag('ul', implode($unexpiredroles)); 296 297 if ($expiredctx->get('defaultexpired')) { 298 return get_string('defaultexpiredexcept', 'tool_dataprivacy', $a); 299 } else if (empty($unexpiredroles)) { 300 return get_string('defaultunexpired', 'tool_dataprivacy', $a); 301 } else { 302 return get_string('defaultunexpiredwithexceptions', 'tool_dataprivacy', $a); 303 } 304 } 305 306 /** 307 * Query the database for results to display in the table. 308 * 309 * @param int $pagesize size of page for paginated displayed table. 310 * @param bool $useinitialsbar do you want to use the initials bar. 311 * @throws dml_exception 312 * @throws coding_exception 313 */ 314 public function query_db($pagesize, $useinitialsbar = true) { 315 // Only count expired contexts that are awaiting confirmation. 316 $total = expired_context::get_record_count_by_contextlevel($this->contextlevel, expired_context::STATUS_EXPIRED); 317 $this->pagesize($pagesize, $total); 318 319 $sort = $this->get_sql_sort(); 320 if (empty($sort)) { 321 $sort = 'timecreated'; 322 } 323 324 // Only load expired contexts that are awaiting confirmation. 325 $expiredcontexts = expired_context::get_records_by_contextlevel($this->contextlevel, expired_context::STATUS_EXPIRED, 326 $sort, $this->get_page_start(), $this->get_page_size()); 327 328 $this->rawdata = []; 329 $contextids = []; 330 foreach ($expiredcontexts as $persistent) { 331 $this->rawdata[] = $persistent; 332 $contextids[] = $persistent->get('contextid'); 333 } 334 335 $this->preload_contexts($contextids); 336 337 // Set initial bars. 338 if ($useinitialsbar) { 339 $this->initialbars($total > $pagesize); 340 } 341 } 342 343 /** 344 * Override default implementation to display a more meaningful information to the user. 345 */ 346 public function print_nothing_to_display() { 347 global $OUTPUT; 348 echo $this->render_reset_button(); 349 $this->print_initials_bar(); 350 echo $OUTPUT->notification(get_string('noexpiredcontexts', 'tool_dataprivacy'), 'warning'); 351 } 352 353 /** 354 * Override the table's show_hide_link method to prevent the show/hide link for the select column from rendering. 355 * 356 * @param string $column the column name, index into various names. 357 * @param int $index numerical index of the column. 358 * @return string HTML fragment. 359 */ 360 protected function show_hide_link($column, $index) { 361 if ($index < 6) { 362 return parent::show_hide_link($column, $index); 363 } 364 return ''; 365 } 366 367 /** 368 * Get the purpose for the specified expired context. 369 * 370 * @param expired_context $expiredcontext 371 * @return purpose 372 */ 373 protected function get_purpose_for_expiry(expired_context $expiredcontext) : purpose { 374 $context = context_helper::instance_by_id($expiredcontext->get('contextid')); 375 376 if (empty($this->purposemap[$context->id])) { 377 $purpose = api::get_effective_context_purpose($context); 378 $this->purposemap[$context->id] = $purpose->get('id'); 379 380 if (empty($this->purposes[$purpose->get('id')])) { 381 $this->purposes[$purpose->get('id')] = $purpose; 382 } 383 } 384 385 return $this->purposes[$this->purposemap[$context->id]]; 386 } 387 388 /** 389 * Preload context records given a set of contextids. 390 * 391 * @param array $contextids 392 */ 393 protected function preload_contexts(array $contextids) { 394 global $DB; 395 396 if (empty($contextids)) { 397 return; 398 } 399 400 $ctxfields = \context_helper::get_preload_record_columns_sql('ctx'); 401 list($insql, $inparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED); 402 $sql = "SELECT {$ctxfields} FROM {context} ctx WHERE ctx.id {$insql}"; 403 $contextlist = $DB->get_recordset_sql($sql, $inparams); 404 foreach ($contextlist as $contextdata) { 405 \context_helper::preload_from_record($contextdata); 406 } 407 $contextlist->close(); 408 409 } 410 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body