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 core_cache\output; 18 19 use cache_factory; 20 use cache_store; 21 use context; 22 use core_collator; 23 use html_table; 24 use html_table_cell; 25 use html_table_row; 26 use html_writer; 27 use lang_string; 28 use moodle_url; 29 use single_select; 30 31 /** 32 * The cache renderer (mainly admin interfaces). 33 * 34 * @package core_cache 35 * @copyright 2012 Sam Hemelryk 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class renderer extends \plugin_renderer_base { 39 40 /** 41 * Displays store summaries. 42 * 43 * @param array $storeinstancesummaries information about each store instance, 44 * as returned by core_cache\administration_helper::get_store_instance_summaries(). 45 * @param array $storepluginsummaries information about each store plugin as 46 * returned by core_cache\administration_helper::get_store_plugin_summaries(). 47 * @return string HTML 48 */ 49 public function store_instance_summariers(array $storeinstancesummaries, array $storepluginsummaries) { 50 $table = new html_table(); 51 $table->head = array( 52 get_string('storename', 'cache'), 53 get_string('plugin', 'cache'), 54 get_string('storeready', 'cache'), 55 get_string('mappings', 'cache'), 56 get_string('modes', 'cache'), 57 get_string('supports', 'cache'), 58 get_string('locking', 'cache') . ' ' . $this->output->help_icon('locking', 'cache'), 59 get_string('actions', 'cache'), 60 ); 61 $table->colclasses = array( 62 'storename', 63 'plugin', 64 'storeready', 65 'mappings', 66 'modes', 67 'supports', 68 'locking', 69 'actions' 70 ); 71 $table->data = array(); 72 73 $defaultstoreactions = get_string('defaultstoreactions', 'cache'); 74 75 foreach ($storeinstancesummaries as $name => $storesummary) { 76 $htmlactions = cache_factory::get_administration_display_helper()->get_store_instance_actions($name, $storesummary); 77 $modes = array(); 78 foreach ($storesummary['modes'] as $mode => $enabled) { 79 if ($enabled) { 80 $modes[] = get_string('mode_'.$mode, 'cache'); 81 } 82 } 83 84 $supports = array(); 85 foreach ($storesummary['supports'] as $support => $enabled) { 86 if ($enabled) { 87 $supports[] = get_string('supports_'.$support, 'cache'); 88 } 89 } 90 91 $info = ''; 92 if (!empty($storesummary['default'])) { 93 $info = $this->output->pix_icon('i/info', $defaultstoreactions, '', array('class' => 'icon')); 94 } 95 96 $isready = $storesummary['isready'] && $storesummary['requirementsmet']; 97 $readycell = new html_table_cell; 98 if ($isready) { 99 $readycell->text = $this->output->pix_icon('i/valid', '1'); 100 } 101 102 $storename = $storesummary['name']; 103 if (!empty($storesummary['default'])) { 104 $storename = get_string('store_'.$storesummary['name'], 'cache'); 105 } 106 if (!$isready && (int)$storesummary['mappings'] > 0) { 107 $readycell->text = $this->output->help_icon('storerequiresattention', 'cache'); 108 $readycell->attributes['class'] = 'store-requires-attention'; 109 } 110 111 $lock = $storesummary['lock']['name']; 112 if (!empty($storesummary['lock']['default'])) { 113 $lock = get_string($storesummary['lock']['name'], 'cache'); 114 } 115 116 $row = new html_table_row(array( 117 $storename, 118 get_string('pluginname', 'cachestore_'.$storesummary['plugin']), 119 $readycell, 120 $storesummary['mappings'], 121 join(', ', $modes), 122 join(', ', $supports), 123 $lock, 124 $info.join(', ', $htmlactions) 125 )); 126 $row->attributes['class'] = 'store-'.$name; 127 if ($storesummary['default']) { 128 $row->attributes['class'] .= ' default-store'; 129 } 130 $table->data[] = $row; 131 } 132 133 $html = html_writer::start_tag('div', array('id' => 'core-cache-store-summaries')); 134 $html .= $this->output->heading(get_string('storesummaries', 'cache'), 3); 135 $html .= html_writer::table($table); 136 $html .= html_writer::end_tag('div'); 137 return $html; 138 } 139 140 /** 141 * Displays plugin summaries. 142 * 143 * @param array $storepluginsummaries information about each store plugin as 144 * returned by core_cache\administration_helper::get_store_plugin_summaries(). 145 * @return string HTML 146 */ 147 public function store_plugin_summaries(array $storepluginsummaries) { 148 $table = new html_table(); 149 $table->head = array( 150 get_string('plugin', 'cache'), 151 get_string('storeready', 'cache'), 152 get_string('stores', 'cache'), 153 get_string('modes', 'cache'), 154 get_string('supports', 'cache'), 155 get_string('actions', 'cache'), 156 ); 157 $table->colclasses = array( 158 'plugin', 159 'storeready', 160 'stores', 161 'modes', 162 'supports', 163 'actions' 164 ); 165 $table->data = array(); 166 167 foreach ($storepluginsummaries as $name => $plugin) { 168 $htmlactions = cache_factory::get_administration_display_helper()->get_store_plugin_actions($name, $plugin); 169 170 $modes = array(); 171 foreach ($plugin['modes'] as $mode => $enabled) { 172 if ($enabled) { 173 $modes[] = get_string('mode_'.$mode, 'cache'); 174 } 175 } 176 177 $supports = array(); 178 foreach ($plugin['supports'] as $support => $enabled) { 179 if ($enabled) { 180 $supports[] = get_string('supports_'.$support, 'cache'); 181 } 182 } 183 184 $row = new html_table_row(array( 185 $plugin['name'], 186 ($plugin['requirementsmet']) ? $this->output->pix_icon('i/valid', '1') : '', 187 $plugin['instances'], 188 join(', ', $modes), 189 join(', ', $supports), 190 join(', ', $htmlactions) 191 )); 192 193 $row->attributes['class'] = 'plugin-'.$name; 194 $table->data[] = $row; 195 } 196 197 $html = html_writer::start_tag('div', array('id' => 'core-cache-plugin-summaries')); 198 $html .= $this->output->heading(get_string('pluginsummaries', 'cache'), 3); 199 $html .= html_writer::table($table); 200 $html .= html_writer::end_tag('div'); 201 return $html; 202 } 203 204 /** 205 * Displays definition summaries. 206 * 207 * @param array $definitionsummaries information about each definition, as returned by 208 * core_cache\administration_helper::get_definition_summaries(). 209 * @param context $context the system context. 210 * 211 * @return string HTML. 212 */ 213 public function definition_summaries(array $definitionsummaries, context $context) { 214 $table = new html_table(); 215 $table->head = array( 216 get_string('definition', 'cache'), 217 get_string('mode', 'cache'), 218 get_string('component', 'cache'), 219 get_string('area', 'cache'), 220 get_string('mappings', 'cache'), 221 get_string('sharing', 'cache'), 222 get_string('canuselocalstore', 'cache'), 223 get_string('actions', 'cache') 224 ); 225 $table->colclasses = array( 226 'definition', 227 'mode', 228 'component', 229 'area', 230 'mappings', 231 'sharing', 232 'canuselocalstore', 233 'actions' 234 ); 235 $table->data = array(); 236 237 core_collator::asort_array_of_arrays_by_key($definitionsummaries, 'name'); 238 239 $none = new lang_string('none', 'cache'); 240 foreach ($definitionsummaries as $id => $definition) { 241 $htmlactions = cache_factory::get_administration_display_helper()->get_definition_actions($context, $definition); 242 if (!empty($definition['mappings'])) { 243 $mapping = join(', ', $definition['mappings']); 244 } else { 245 $mapping = '<em>'.$none.'</em>'; 246 } 247 248 $uselocalcachecol = get_string('no'); 249 if ($definition['mode'] != cache_store::MODE_REQUEST) { 250 if (isset($definition['canuselocalstore']) && $definition['canuselocalstore']) { 251 $uselocalcachecol = get_string('yes'); 252 } 253 } 254 255 $row = new html_table_row(array( 256 $definition['name'], 257 get_string('mode_'.$definition['mode'], 'cache'), 258 $definition['component'], 259 $definition['area'], 260 $mapping, 261 join(', ', $definition['selectedsharingoption']), 262 $uselocalcachecol, 263 join(', ', $htmlactions) 264 )); 265 $row->attributes['class'] = 'definition-'.$definition['component'].'-'.$definition['area']; 266 $table->data[] = $row; 267 } 268 269 $html = html_writer::start_tag('div', array('id' => 'core-cache-definition-summaries')); 270 $html .= $this->output->heading(get_string('definitionsummaries', 'cache'), 3); 271 $html .= html_writer::table($table); 272 273 $url = new moodle_url('/cache/admin.php', array('action' => 'rescandefinitions', 'sesskey' => sesskey())); 274 $link = html_writer::link($url, get_string('rescandefinitions', 'cache')); 275 $html .= html_writer::tag('div', $link, array('id' => 'core-cache-rescan-definitions')); 276 277 $html .= html_writer::end_tag('div'); 278 return $html; 279 } 280 281 /** 282 * Displays mode mappings 283 * 284 * @param string $applicationstore 285 * @param string $sessionstore 286 * @param string $requeststore 287 * @param moodle_url $editurl 288 * @return string HTML 289 */ 290 public function mode_mappings($applicationstore, $sessionstore, $requeststore, moodle_url $editurl) { 291 $table = new html_table(); 292 $table->colclasses = array( 293 'mode', 294 'mapping', 295 ); 296 $table->rowclasses = array( 297 'mode_application', 298 'mode_session', 299 'mode_request' 300 ); 301 $table->head = array( 302 get_string('mode', 'cache'), 303 get_string('mappings', 'cache'), 304 ); 305 $table->data = array( 306 array(get_string('mode_'.cache_store::MODE_APPLICATION, 'cache'), $applicationstore), 307 array(get_string('mode_'.cache_store::MODE_SESSION, 'cache'), $sessionstore), 308 array(get_string('mode_'.cache_store::MODE_REQUEST, 'cache'), $requeststore) 309 ); 310 311 $html = html_writer::start_tag('div', array('id' => 'core-cache-mode-mappings')); 312 $html .= $this->output->heading(get_string('defaultmappings', 'cache'), 3); 313 $html .= html_writer::table($table); 314 $link = html_writer::link($editurl, get_string('editmappings', 'cache')); 315 $html .= html_writer::tag('div', $link, array('class' => 'edit-link')); 316 $html .= html_writer::end_tag('div'); 317 return $html; 318 } 319 320 /** 321 * Display basic information about lock instances. 322 * 323 * @todo Add some actions so that people can configure lock instances. 324 * 325 * @param array $locks 326 * @return string 327 */ 328 public function lock_summaries(array $locks) { 329 $table = new html_table(); 330 $table->colclasses = array( 331 'name', 332 'type', 333 'default', 334 'uses', 335 'actions' 336 ); 337 $table->rowclasses = array( 338 'lock_name', 339 'lock_type', 340 'lock_default', 341 'lock_uses', 342 'lock_actions', 343 ); 344 $table->head = array( 345 get_string('lockname', 'cache'), 346 get_string('locktype', 'cache'), 347 get_string('lockdefault', 'cache'), 348 get_string('lockuses', 'cache'), 349 get_string('actions', 'cache') 350 ); 351 $table->data = array(); 352 $tick = $this->output->pix_icon('i/valid', ''); 353 foreach ($locks as $lock) { 354 $actions = array(); 355 if ($lock['uses'] === 0 && !$lock['default']) { 356 $url = new moodle_url('/cache/admin.php', array('lock' => $lock['name'], 'action' => 'deletelock')); 357 $actions[] = html_writer::link($url, get_string('delete', 'cache')); 358 } 359 $table->data[] = new html_table_row(array( 360 new html_table_cell($lock['name']), 361 new html_table_cell($lock['type']), 362 new html_table_cell($lock['default'] ? $tick : ''), 363 new html_table_cell($lock['uses']), 364 new html_table_cell(join(' ', $actions)) 365 )); 366 } 367 368 $html = html_writer::start_tag('div', array('id' => 'core-cache-lock-summary')); 369 $html .= $this->output->heading(get_string('locksummary', 'cache'), 3); 370 $html .= html_writer::table($table); 371 $html .= html_writer::end_tag('div'); 372 return $html; 373 } 374 375 /** 376 * Renders additional actions for locks, such as Add. 377 * 378 * @return string 379 */ 380 public function additional_lock_actions() : string { 381 $url = new moodle_url('/cache/admin.php', array('action' => 'newlockinstance')); 382 $select = new single_select($url, 'lock', cache_factory::get_administration_display_helper()->get_addable_lock_options()); 383 $select->label = get_string('addnewlockinstance', 'cache'); 384 385 $html = html_writer::start_tag('div', array('id' => 'core-cache-lock-additional-actions')); 386 $html .= html_writer::tag('div', $this->output->render($select), array('class' => 'new-instance')); 387 $html .= html_writer::end_tag('div'); 388 return $html; 389 } 390 391 /** 392 * Renders an array of notifications for the cache configuration screen. 393 * 394 * Takes an array of notifications with the form: 395 * $notifications = array( 396 * array('This is a success message', true), 397 * array('This is a failure message', false), 398 * ); 399 * 400 * @param array $notifications 401 * @return string 402 */ 403 public function notifications(array $notifications = array()) { 404 if (count($notifications) === 0) { 405 // There are no notifications to render. 406 return ''; 407 } 408 $html = html_writer::start_div('notifications'); 409 foreach ($notifications as $notification) { 410 list($message, $notifysuccess) = $notification; 411 $html .= $this->notification($message, ($notifysuccess) ? 'notifysuccess' : 'notifyproblem'); 412 } 413 $html .= html_writer::end_div(); 414 return $html; 415 } 416 417 /** 418 * Creates the two tables which display on the usage page. 419 * 420 * @param array $usage Usage information (from cache_helper::usage) 421 * @return array Array of 2 tables (main and summary table) 422 * @throws \coding_exception 423 */ 424 public function usage_tables(array $usage): array { 425 $table = new \html_table(); 426 $table->id = 'usage_main'; 427 $table->head = [ 428 get_string('definition', 'cache'), 429 get_string('storename', 'cache'), 430 get_string('plugin', 'cache'), 431 get_string('usage_items', 'cache'), 432 get_string('usage_mean', 'cache'), 433 get_string('usage_sd', 'cache'), 434 get_string('usage_total', 'cache'), 435 get_string('usage_totalmargin', 'cache')]; 436 $table->align = [ 437 'left', 'left', 'left', 438 'right', 'right', 'right', 'right', 'right' 439 ]; 440 $table->data = []; 441 442 $summarytable = new \html_table(); 443 $summarytable->id = 'usage_summary'; 444 $summarytable->head = [ 445 get_string('storename', 'cache'), 446 get_string('plugin', 'cache'), 447 get_string('usage_total', 'cache'), 448 get_string('usage_realtotal', 'cache') 449 ]; 450 $summarytable->align = [ 451 'left', 'left', 452 'right', 'right', 453 ]; 454 $summarytable->data = []; 455 $summarytable->attributes['class'] = 'generaltable w-auto'; 456 $storetotals = []; 457 458 // We will highlight all cells that are more than 2% of total size, so work that out first. 459 $total = 0; 460 foreach ($usage as $definition) { 461 foreach ($definition->stores as $storedata) { 462 $total += $storedata->items * $storedata->mean; 463 } 464 } 465 $highlightover = round($total / 50); 466 467 foreach ($usage as $definition) { 468 foreach ($definition->stores as $storedata) { 469 $row = []; 470 $row[] = s($definition->cacheid); 471 $row[] = s($storedata->name); 472 $row[] = s($storedata->class); 473 if (!$storedata->supported) { 474 // We don't have data for this store because it isn't searchable. 475 $row[] = '-'; 476 } else { 477 $row[] = $storedata->items; 478 } 479 if ($storedata->items) { 480 $row[] = display_size(round($storedata->mean)); 481 if ($storedata->items > 1) { 482 $row[] = display_size(round($storedata->sd)); 483 } else { 484 $row[] = ''; 485 } 486 $cellsize = round($storedata->items * $storedata->mean); 487 $row[] = display_size($cellsize, 1, 'MB'); 488 489 if (!array_key_exists($storedata->name, $storetotals)) { 490 $storetotals[$storedata->name] = (object)[ 491 'plugin' => $storedata->class, 492 'total' => 0, 493 'storetotal' => $storedata->storetotal, 494 ]; 495 } 496 $storetotals[$storedata->name]->total += $cellsize; 497 } else { 498 $row[] = ''; 499 $row[] = ''; 500 $cellsize = 0; 501 $row[] = ''; 502 } 503 if ($storedata->margin) { 504 // Plus or minus. 505 $row[] = '±' . display_size($storedata->margin * $storedata->items, 1, 'MB'); 506 } else { 507 $row[] = ''; 508 } 509 $htmlrow = new \html_table_row($row); 510 if ($cellsize > $highlightover) { 511 $htmlrow->attributes = ['class' => 'table-warning']; 512 } 513 $table->data[] = $htmlrow; 514 } 515 } 516 517 ksort($storetotals); 518 519 foreach ($storetotals as $storename => $storedetails) { 520 $row = [s($storename), s($storedetails->plugin)]; 521 $row[] = display_size($storedetails->total, 1, 'MB'); 522 if ($storedetails->storetotal !== null) { 523 $row[] = display_size($storedetails->storetotal, 1, 'MB'); 524 } else { 525 $row[] = '-'; 526 } 527 $summarytable->data[] = $row; 528 } 529 530 return [$table, $summarytable]; 531 } 532 533 /** 534 * Renders the usage page. 535 * 536 * @param \html_table $maintable Main table 537 * @param \html_table $summarytable Summary table 538 * @param \moodleform $samplesform Form to select number of samples 539 * @return string HTML for page 540 */ 541 public function usage_page(\html_table $maintable, \html_table $summarytable, \moodleform $samplesform): string { 542 $data = [ 543 'maintable' => \html_writer::table($maintable), 544 'summarytable' => \html_writer::table($summarytable), 545 'samplesform' => $samplesform->render() 546 ]; 547 548 return $this->render_from_template('core_cache/usage', $data); 549 } 550 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body