See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 400 and 401]
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 * Cache display administration helper. 19 * 20 * This file is part of Moodle's cache API, affectionately called MUC. 21 * It contains the components that are requried in order to use caching. 22 * 23 * @package core 24 * @category cache 25 * @author Peter Burnett <peterburnett@catalyst-au.net> 26 * @copyright 2020 Catalyst IT 27 * @copyright 2012 Sam Hemelryk 28 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 */ 30 31 namespace core_cache\local; 32 33 use cache_store, cache_factory, cache_config_writer, cache_helper; 34 use core\output\notification; 35 36 /** 37 * A cache helper for administration tasks 38 * 39 * @package core 40 * @category cache 41 * @copyright 2020 Peter Burnett <peterburnett@catalyst-au.net> 42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 43 */ 44 class administration_display_helper extends \core_cache\administration_helper { 45 46 /** 47 * Please do not call constructor directly. Use cache_factory::get_administration_display_helper() instead. 48 */ 49 public function __construct() { 50 // Nothing to do here. 51 } 52 53 /** 54 * Returns all of the actions that can be performed on a definition. 55 * 56 * @param context $context the system context. 57 * @param array $definitionsummary information about this cache, from the array returned by 58 * core_cache\administration_helper::get_definition_summaries(). Currently only 'sharingoptions' 59 * element is used. 60 * @return array of actions. Each action is an action_url. 61 */ 62 public function get_definition_actions(\context $context, array $definitionsummary): array { 63 global $OUTPUT; 64 if (has_capability('moodle/site:config', $context)) { 65 $actions = array(); 66 // Edit mappings. 67 $actions[] = $OUTPUT->action_link( 68 new \moodle_url('/cache/admin.php', array('action' => 'editdefinitionmapping', 69 'definition' => $definitionsummary['id'])), 70 get_string('editmappings', 'cache') 71 ); 72 // Edit sharing. 73 if (count($definitionsummary['sharingoptions']) > 1) { 74 $actions[] = $OUTPUT->action_link( 75 new \moodle_url('/cache/admin.php', array('action' => 'editdefinitionsharing', 76 'definition' => $definitionsummary['id'])), 77 get_string('editsharing', 'cache') 78 ); 79 } 80 // Purge. 81 $actions[] = $OUTPUT->action_link( 82 new \moodle_url('/cache/admin.php', array('action' => 'purgedefinition', 83 'definition' => $definitionsummary['id'], 'sesskey' => sesskey())), 84 get_string('purge', 'cache') 85 ); 86 return $actions; 87 } 88 return array(); 89 } 90 91 /** 92 * Returns all of the actions that can be performed on a store. 93 * 94 * @param string $name The name of the store 95 * @param array $storedetails information about this store, from the array returned by 96 * core_cache\administration_helper::get_store_instance_summaries(). 97 * @return array of actions. Each action is an action_url. 98 */ 99 public function get_store_instance_actions(string $name, array $storedetails): array { 100 global $OUTPUT; 101 $actions = array(); 102 if (has_capability('moodle/site:config', \context_system::instance())) { 103 $baseurl = new \moodle_url('/cache/admin.php', array('store' => $name)); 104 if (empty($storedetails['default'])) { 105 // Edit store. 106 $actions[] = $OUTPUT->action_link( 107 new \moodle_url($baseurl, array('action' => 'editstore', 'plugin' => $storedetails['plugin'])), 108 get_string('editstore', 'cache') 109 ); 110 // Delete store. 111 $actions[] = $OUTPUT->action_link( 112 new \moodle_url($baseurl, array('action' => 'deletestore')), 113 get_string('deletestore', 'cache') 114 ); 115 } 116 // Purge store. 117 $actions[] = $OUTPUT->action_link( 118 new \moodle_url($baseurl, array('action' => 'purgestore', 'sesskey' => sesskey())), 119 get_string('purge', 'cache') 120 ); 121 } 122 return $actions; 123 } 124 125 /** 126 * Returns all of the actions that can be performed on a plugin. 127 * 128 * @param string $name The name of the plugin 129 * @param array $plugindetails information about this store, from the array returned by 130 * core_cache\administration_helper::get_store_plugin_summaries(). 131 * @return array of actions. Each action is an action_url. 132 */ 133 public function get_store_plugin_actions(string $name, array $plugindetails): array { 134 global $OUTPUT; 135 $actions = array(); 136 if (has_capability('moodle/site:config', \context_system::instance())) { 137 if (!empty($plugindetails['canaddinstance'])) { 138 $url = new \moodle_url('/cache/admin.php', 139 array('action' => 'addstore', 'plugin' => $name)); 140 $actions[] = $OUTPUT->action_link( 141 $url, 142 get_string('addinstance', 'cache') 143 ); 144 } 145 } 146 return $actions; 147 } 148 149 /** 150 * Returns a form that can be used to add a store instance. 151 * 152 * @param string $plugin The plugin to add an instance of 153 * @return cachestore_addinstance_form 154 * @throws coding_exception 155 */ 156 public function get_add_store_form(string $plugin): \cachestore_addinstance_form { 157 global $CFG; // Needed for includes. 158 $plugins = \core_component::get_plugin_list('cachestore'); 159 if (!array_key_exists($plugin, $plugins)) { 160 throw new \coding_exception('Invalid cache plugin used when trying to create an edit form.'); 161 } 162 $plugindir = $plugins[$plugin]; 163 $class = 'cachestore_addinstance_form'; 164 if (file_exists($plugindir.'/addinstanceform.php')) { 165 require_once($plugindir.'/addinstanceform.php'); 166 if (class_exists('cachestore_'.$plugin.'_addinstance_form')) { 167 $class = 'cachestore_'.$plugin.'_addinstance_form'; 168 if (!array_key_exists('cachestore_addinstance_form', class_parents($class))) { 169 throw new \coding_exception('Cache plugin add instance forms must extend cachestore_addinstance_form'); 170 } 171 } 172 } 173 174 $locks = $this->get_possible_locks_for_stores($plugindir, $plugin); 175 176 $url = new \moodle_url('/cache/admin.php', array('action' => 'addstore')); 177 return new $class($url, array('plugin' => $plugin, 'store' => null, 'locks' => $locks)); 178 } 179 180 /** 181 * Returns a form that can be used to edit a store instance. 182 * 183 * @param string $plugin 184 * @param string $store 185 * @return cachestore_addinstance_form 186 * @throws coding_exception 187 */ 188 public function get_edit_store_form(string $plugin, string $store): \cachestore_addinstance_form { 189 global $CFG; // Needed for includes. 190 $plugins = \core_component::get_plugin_list('cachestore'); 191 if (!array_key_exists($plugin, $plugins)) { 192 throw new \coding_exception('Invalid cache plugin used when trying to create an edit form.'); 193 } 194 $factory = \cache_factory::instance(); 195 $config = $factory->create_config_instance(); 196 $stores = $config->get_all_stores(); 197 if (!array_key_exists($store, $stores)) { 198 throw new \coding_exception('Invalid store name given when trying to create an edit form.'); 199 } 200 $plugindir = $plugins[$plugin]; 201 $class = 'cachestore_addinstance_form'; 202 if (file_exists($plugindir.'/addinstanceform.php')) { 203 require_once($plugindir.'/addinstanceform.php'); 204 if (class_exists('cachestore_'.$plugin.'_addinstance_form')) { 205 $class = 'cachestore_'.$plugin.'_addinstance_form'; 206 if (!array_key_exists('cachestore_addinstance_form', class_parents($class))) { 207 throw new \coding_exception('Cache plugin add instance forms must extend cachestore_addinstance_form'); 208 } 209 } 210 } 211 212 $locks = $this->get_possible_locks_for_stores($plugindir, $plugin); 213 214 $url = new \moodle_url('/cache/admin.php', array('action' => 'editstore', 'plugin' => $plugin, 'store' => $store)); 215 $editform = new $class($url, array('plugin' => $plugin, 'store' => $store, 'locks' => $locks)); 216 if (isset($stores[$store]['lock'])) { 217 $editform->set_data(array('lock' => $stores[$store]['lock'])); 218 } 219 // See if the cachestore is going to want to load data for the form. 220 // If it has a customised add instance form then it is going to want to. 221 $storeclass = 'cachestore_'.$plugin; 222 $storedata = $stores[$store]; 223 if (array_key_exists('configuration', $storedata) && 224 array_key_exists('cache_is_configurable', class_implements($storeclass))) { 225 $storeclass::config_set_edit_form_data($editform, $storedata['configuration']); 226 } 227 return $editform; 228 } 229 230 /** 231 * Returns an array of suitable lock instances for use with this plugin, or false if the plugin handles locking itself. 232 * 233 * @param string $plugindir 234 * @param string $plugin 235 * @return array|false 236 */ 237 protected function get_possible_locks_for_stores(string $plugindir, string $plugin) { 238 global $CFG; // Needed for includes. 239 $supportsnativelocking = false; 240 if (file_exists($plugindir.'/lib.php')) { 241 require_once($plugindir.'/lib.php'); 242 $pluginclass = 'cachestore_'.$plugin; 243 if (class_exists($pluginclass)) { 244 $supportsnativelocking = array_key_exists('cache_is_lockable', class_implements($pluginclass)); 245 } 246 } 247 248 if (!$supportsnativelocking) { 249 $config = \cache_config::instance(); 250 $locks = array(); 251 foreach ($config->get_locks() as $lock => $conf) { 252 if (!empty($conf['default'])) { 253 $name = get_string($lock, 'cache'); 254 } else { 255 $name = $lock; 256 } 257 $locks[$lock] = $name; 258 } 259 } else { 260 $locks = false; 261 } 262 263 return $locks; 264 } 265 266 /** 267 * Processes the results of the add/edit instance form data for a plugin returning an array of config information suitable to 268 * store in configuration. 269 * 270 * @param stdClass $data The mform data. 271 * @return array 272 * @throws coding_exception 273 */ 274 public function get_store_configuration_from_data(\stdClass $data): array { 275 global $CFG; 276 $file = $CFG->dirroot.'/cache/stores/'.$data->plugin.'/lib.php'; 277 if (!file_exists($file)) { 278 throw new \coding_exception('Invalid cache plugin provided. '.$file); 279 } 280 require_once($file); 281 $class = 'cachestore_'.$data->plugin; 282 if (!class_exists($class)) { 283 throw new \coding_exception('Invalid cache plugin provided.'); 284 } 285 if (array_key_exists('cache_is_configurable', class_implements($class))) { 286 return $class::config_get_configuration_array($data); 287 } 288 return array(); 289 } 290 291 /** 292 * Returns an array of lock plugins for which we can add an instance. 293 * 294 * Suitable for use within an mform select element. 295 * 296 * @return array 297 */ 298 public function get_addable_lock_options(): array { 299 $plugins = \core_component::get_plugin_list_with_class('cachelock', '', 'lib.php'); 300 $options = array(); 301 $len = strlen('cachelock_'); 302 foreach ($plugins as $plugin => $class) { 303 $method = "$class::can_add_instance"; 304 if (is_callable($method) && !call_user_func($method)) { 305 // Can't add an instance of this plugin. 306 continue; 307 } 308 $options[substr($plugin, $len)] = get_string('pluginname', $plugin); 309 } 310 return $options; 311 } 312 313 /** 314 * Gets the form to use when adding a lock instance. 315 * 316 * @param string $plugin 317 * @param array $lockplugin 318 * @return cache_lock_form 319 * @throws coding_exception 320 */ 321 public function get_add_lock_form(string $plugin, array $lockplugin = null): \cache_lock_form { 322 global $CFG; // Needed for includes. 323 $plugins = \core_component::get_plugin_list('cachelock'); 324 if (!array_key_exists($plugin, $plugins)) { 325 throw new \coding_exception('Invalid cache lock plugin requested when trying to create a form.'); 326 } 327 $plugindir = $plugins[$plugin]; 328 $class = 'cache_lock_form'; 329 if (file_exists($plugindir.'/addinstanceform.php') && in_array('cache_is_configurable', class_implements($class))) { 330 require_once($plugindir.'/addinstanceform.php'); 331 if (class_exists('cachelock_'.$plugin.'_addinstance_form')) { 332 $class = 'cachelock_'.$plugin.'_addinstance_form'; 333 if (!array_key_exists('cache_lock_form', class_parents($class))) { 334 throw new \coding_exception('Cache lock plugin add instance forms must extend cache_lock_form'); 335 } 336 } 337 } 338 return new $class(null, array('lock' => $plugin)); 339 } 340 341 /** 342 * Gets configuration data from a new lock instance form. 343 * 344 * @param string $plugin 345 * @param stdClass $data 346 * @return array 347 * @throws coding_exception 348 */ 349 public function get_lock_configuration_from_data(string $plugin, \stdClass $data): array { 350 global $CFG; 351 $file = $CFG->dirroot.'/cache/locks/'.$plugin.'/lib.php'; 352 if (!file_exists($file)) { 353 throw new \coding_exception('Invalid cache plugin provided. '.$file); 354 } 355 require_once($file); 356 $class = 'cachelock_'.$plugin; 357 if (!class_exists($class)) { 358 throw new \coding_exception('Invalid cache plugin provided.'); 359 } 360 if (array_key_exists('cache_is_configurable', class_implements($class))) { 361 return $class::config_get_configuration_array($data); 362 } 363 return array(); 364 } 365 366 /** 367 * Handles the page actions, based on the parameter. 368 * 369 * @param string $action the action to handle. 370 * @param array $forminfo an empty array to be overridden and set. 371 * @return array the empty or overridden forminfo array. 372 */ 373 public function perform_cache_actions(string $action, array $forminfo): array { 374 switch ($action) { 375 case 'rescandefinitions' : // Rescan definitions. 376 $this->action_rescan_definition(); 377 break; 378 379 case 'addstore' : // Add the requested store. 380 $forminfo = $this->action_addstore(); 381 break; 382 383 case 'editstore' : // Edit the requested store. 384 $forminfo = $this->action_editstore(); 385 break; 386 387 case 'deletestore' : // Delete a given store. 388 $this->action_deletestore($action); 389 break; 390 391 case 'editdefinitionmapping' : // Edit definition mappings. 392 $forminfo = $this->action_editdefinitionmapping(); 393 break; 394 395 case 'editdefinitionsharing' : // Edit definition sharing. 396 $forminfo = $this->action_editdefinitionsharing(); 397 break; 398 399 case 'editmodemappings': // Edit default mode mappings. 400 $forminfo = $this->action_editmodemappings(); 401 break; 402 403 case 'purgedefinition': // Purge a specific definition. 404 $this->action_purgedefinition(); 405 break; 406 407 case 'purgestore': 408 case 'purge': // Purge a store cache. 409 $this->action_purge(); 410 break; 411 412 case 'newlockinstance': 413 $forminfo = $this->action_newlockinstance(); 414 break; 415 416 case 'deletelock': 417 // Deletes a lock instance. 418 $this->action_deletelock($action); 419 break; 420 } 421 422 return $forminfo; 423 } 424 425 /** 426 * Performs the rescan definition action. 427 * 428 * @return void 429 */ 430 public function action_rescan_definition() { 431 global $PAGE; 432 433 require_sesskey(); 434 \cache_config_writer::update_definitions(); 435 redirect($PAGE->url); 436 } 437 438 /** 439 * Performs the add store action. 440 * 441 * @return array an array of the form to display to the user, and the page title. 442 */ 443 public function action_addstore() : array { 444 global $PAGE; 445 $storepluginsummaries = $this->get_store_plugin_summaries(); 446 447 $plugin = required_param('plugin', PARAM_PLUGIN); 448 if (!$storepluginsummaries[$plugin]['canaddinstance']) { 449 throw new \moodle_exception('ex_unmetstorerequirements', 'cache'); 450 } 451 $mform = $this->get_add_store_form($plugin); 452 $title = get_string('addstore', 'cache', $storepluginsummaries[$plugin]['name']); 453 if ($mform->is_cancelled()) { 454 redirect($PAGE->url); 455 } else if ($data = $mform->get_data()) { 456 $config = $this->get_store_configuration_from_data($data); 457 $writer = \cache_config_writer::instance(); 458 unset($config['lock']); 459 foreach ($writer->get_locks() as $lock => $lockconfig) { 460 if ($lock == $data->lock) { 461 $config['lock'] = $data->lock; 462 } 463 } 464 $writer->add_store_instance($data->name, $data->plugin, $config); 465 redirect($PAGE->url, get_string('addstoresuccess', 'cache', $storepluginsummaries[$plugin]['name']), 5); 466 } 467 468 $PAGE->navbar->add(get_string('addstore', 'cache', 'cache'), $PAGE->url); 469 return array('form' => $mform, 'title' => $title); 470 } 471 472 /** 473 * Performs the edit store action. 474 * 475 * @return array an array of the form to display, and the page title. 476 */ 477 public function action_editstore(): array { 478 global $PAGE; 479 $storepluginsummaries = $this->get_store_plugin_summaries(); 480 481 $plugin = required_param('plugin', PARAM_PLUGIN); 482 $store = required_param('store', PARAM_TEXT); 483 $mform = $this->get_edit_store_form($plugin, $store); 484 $title = get_string('addstore', 'cache', $storepluginsummaries[$plugin]['name']); 485 if ($mform->is_cancelled()) { 486 redirect($PAGE->url); 487 } else if ($data = $mform->get_data()) { 488 $config = $this->get_store_configuration_from_data($data); 489 $writer = \cache_config_writer::instance(); 490 491 unset($config['lock']); 492 foreach ($writer->get_locks() as $lock => $lockconfig) { 493 if ($lock == $data->lock) { 494 $config['lock'] = $data->lock; 495 } 496 } 497 $writer->edit_store_instance($data->name, $data->plugin, $config); 498 redirect($PAGE->url, get_string('editstoresuccess', 'cache', $storepluginsummaries[$plugin]['name']), 5); 499 } 500 501 return array('form' => $mform, 'title' => $title); 502 } 503 504 /** 505 * Performs the deletestore action. 506 * 507 * @param string $action the action calling to this function. 508 */ 509 public function action_deletestore(string $action): void { 510 global $OUTPUT, $PAGE, $SITE; 511 $notifysuccess = true; 512 $storeinstancesummaries = $this->get_store_instance_summaries(); 513 514 $store = required_param('store', PARAM_TEXT); 515 $confirm = optional_param('confirm', false, PARAM_BOOL); 516 517 if (!array_key_exists($store, $storeinstancesummaries)) { 518 $notifysuccess = false; 519 $notification = get_string('invalidstore', 'cache'); 520 } else if ($storeinstancesummaries[$store]['mappings'] > 0) { 521 $notifysuccess = false; 522 $notification = get_string('deletestorehasmappings', 'cache'); 523 } 524 525 if ($notifysuccess) { 526 if (!$confirm) { 527 $title = get_string('confirmstoredeletion', 'cache'); 528 $params = array('store' => $store, 'confirm' => 1, 'action' => $action, 'sesskey' => sesskey()); 529 $url = new \moodle_url($PAGE->url, $params); 530 $button = new \single_button($url, get_string('deletestore', 'cache')); 531 532 $PAGE->set_title($title); 533 $PAGE->set_heading($SITE->fullname); 534 echo $OUTPUT->header(); 535 echo $OUTPUT->heading($title); 536 $confirmation = get_string('deletestoreconfirmation', 'cache', $storeinstancesummaries[$store]['name']); 537 echo $OUTPUT->confirm($confirmation, $button, $PAGE->url); 538 echo $OUTPUT->footer(); 539 exit; 540 } else { 541 require_sesskey(); 542 $writer = \cache_config_writer::instance(); 543 $writer->delete_store_instance($store); 544 redirect($PAGE->url, get_string('deletestoresuccess', 'cache'), 5); 545 } 546 } else { 547 redirect($PAGE->url, $notification, null, notification::NOTIFY_ERROR); 548 } 549 } 550 551 /** 552 * Performs the edit definition mapping action. 553 * 554 * @return array an array of the form to display, and the page title. 555 * @throws cache_exception 556 */ 557 public function action_editdefinitionmapping(): array { 558 global $PAGE; 559 $definitionsummaries = $this->get_definition_summaries(); 560 561 $definition = required_param('definition', PARAM_SAFEPATH); 562 if (!array_key_exists($definition, $definitionsummaries)) { 563 throw new \cache_exception('Invalid cache definition requested'); 564 } 565 $title = get_string('editdefinitionmappings', 'cache', $definition); 566 $mform = new \cache_definition_mappings_form($PAGE->url, array('definition' => $definition)); 567 if ($mform->is_cancelled()) { 568 redirect($PAGE->url); 569 } else if ($data = $mform->get_data()) { 570 $writer = \cache_config_writer::instance(); 571 $mappings = array(); 572 foreach ($data->mappings as $mapping) { 573 if (!empty($mapping)) { 574 $mappings[] = $mapping; 575 } 576 } 577 $writer->set_definition_mappings($definition, $mappings); 578 redirect($PAGE->url); 579 } 580 581 $PAGE->navbar->add(get_string('updatedefinitionmapping', 'cache'), $PAGE->url); 582 return array('form' => $mform, 'title' => $title); 583 } 584 585 /** 586 * Performs the edit definition sharing action. 587 * 588 * @return array an array of the edit definition sharing form, and the page title. 589 */ 590 public function action_editdefinitionsharing(): array { 591 global $PAGE; 592 $definitionsummaries = $this->get_definition_summaries(); 593 594 $definition = required_param('definition', PARAM_SAFEPATH); 595 if (!array_key_exists($definition, $definitionsummaries)) { 596 throw new \cache_exception('Invalid cache definition requested'); 597 } 598 $title = get_string('editdefinitionsharing', 'cache', $definition); 599 $sharingoptions = $definitionsummaries[$definition]['sharingoptions']; 600 $customdata = array('definition' => $definition, 'sharingoptions' => $sharingoptions); 601 $mform = new \cache_definition_sharing_form($PAGE->url, $customdata); 602 $mform->set_data(array( 603 'sharing' => $definitionsummaries[$definition]['selectedsharingoption'], 604 'userinputsharingkey' => $definitionsummaries[$definition]['userinputsharingkey'] 605 )); 606 if ($mform->is_cancelled()) { 607 redirect($PAGE->url); 608 } else if ($data = $mform->get_data()) { 609 $component = $definitionsummaries[$definition]['component']; 610 $area = $definitionsummaries[$definition]['area']; 611 // Purge the stores removing stale data before we alter the sharing option. 612 \cache_helper::purge_stores_used_by_definition($component, $area); 613 $writer = \cache_config_writer::instance(); 614 $sharing = array_sum(array_keys($data->sharing)); 615 $userinputsharingkey = $data->userinputsharingkey; 616 $writer->set_definition_sharing($definition, $sharing, $userinputsharingkey); 617 redirect($PAGE->url); 618 } 619 620 $PAGE->navbar->add(get_string('updatedefinitionsharing', 'cache'), $PAGE->url); 621 return array('form' => $mform, 'title' => $title); 622 } 623 624 /** 625 * Performs the edit mode mappings action. 626 * 627 * @return array an array of the edit mode mappings form. 628 */ 629 public function action_editmodemappings(): array { 630 global $PAGE; 631 $storeinstancesummaries = $this->get_store_instance_summaries(); 632 $defaultmodestores = $this->get_default_mode_stores(); 633 634 $mform = new \cache_mode_mappings_form(null, $storeinstancesummaries); 635 $mform->set_data(array( 636 'mode_'.cache_store::MODE_APPLICATION => key($defaultmodestores[cache_store::MODE_APPLICATION]), 637 'mode_'.cache_store::MODE_SESSION => key($defaultmodestores[cache_store::MODE_SESSION]), 638 'mode_'.cache_store::MODE_REQUEST => key($defaultmodestores[cache_store::MODE_REQUEST]), 639 )); 640 if ($mform->is_cancelled()) { 641 redirect($PAGE->url); 642 } else if ($data = $mform->get_data()) { 643 $mappings = array( 644 cache_store::MODE_APPLICATION => array($data->{'mode_'.cache_store::MODE_APPLICATION}), 645 cache_store::MODE_SESSION => array($data->{'mode_'.cache_store::MODE_SESSION}), 646 cache_store::MODE_REQUEST => array($data->{'mode_'.cache_store::MODE_REQUEST}), 647 ); 648 $writer = cache_config_writer::instance(); 649 $writer->set_mode_mappings($mappings); 650 redirect($PAGE->url); 651 } 652 653 return array('form' => $mform); 654 } 655 656 /** 657 * Performs the purge definition action. 658 * 659 * @return void 660 */ 661 public function action_purgedefinition() { 662 global $PAGE; 663 664 require_sesskey(); 665 $id = required_param('definition', PARAM_SAFEPATH); 666 list($component, $area) = explode('/', $id, 2); 667 $factory = cache_factory::instance(); 668 $definition = $factory->create_definition($component, $area); 669 if ($definition->has_required_identifiers()) { 670 // We will have to purge the stores used by this definition. 671 cache_helper::purge_stores_used_by_definition($component, $area); 672 } else { 673 // Alrighty we can purge just the data belonging to this definition. 674 cache_helper::purge_by_definition($component, $area); 675 } 676 677 $message = get_string('purgexdefinitionsuccess', 'cache', [ 678 'name' => $definition->get_name(), 679 'component' => $component, 680 'area' => $area, 681 ]); 682 $purgeagainlink = \html_writer::link(new \moodle_url('/cache/admin.php', [ 683 'action' => 'purgedefinition', 'sesskey' => sesskey(), 'definition' => $id]), 684 get_string('purgeagain', 'cache')); 685 redirect($PAGE->url, $message . ' ' . $purgeagainlink, 5); 686 } 687 688 /** 689 * Performs the purge action. 690 * 691 * @return void 692 */ 693 public function action_purge() { 694 global $PAGE; 695 696 require_sesskey(); 697 $store = required_param('store', PARAM_TEXT); 698 cache_helper::purge_store($store); 699 $message = get_string('purgexstoresuccess', 'cache', ['store' => $store]); 700 $purgeagainlink = \html_writer::link(new \moodle_url('/cache/admin.php', [ 701 'action' => 'purgestore', 'sesskey' => sesskey(), 'store' => $store]), 702 get_string('purgeagain', 'cache')); 703 redirect($PAGE->url, $message . ' ' . $purgeagainlink, 5); 704 } 705 706 /** 707 * Performs the new lock instance action. 708 * 709 * @return array An array containing the new lock instance form. 710 */ 711 public function action_newlockinstance(): array { 712 global $PAGE; 713 714 // Adds a new lock instance. 715 $lock = required_param('lock', PARAM_ALPHANUMEXT); 716 $mform = $this->get_add_lock_form($lock); 717 if ($mform->is_cancelled()) { 718 redirect($PAGE->url); 719 } else if ($data = $mform->get_data()) { 720 $factory = cache_factory::instance(); 721 $config = $factory->create_config_instance(true); 722 $name = $data->name; 723 $data = $this->get_lock_configuration_from_data($lock, $data); 724 $config->add_lock_instance($name, $lock, $data); 725 redirect($PAGE->url, get_string('addlocksuccess', 'cache', $name), 5); 726 } 727 728 return array('form' => $mform); 729 } 730 731 /** 732 * Performs the delete lock action. 733 * 734 * @param string $action the action calling this function. 735 */ 736 public function action_deletelock(string $action): void { 737 global $OUTPUT, $PAGE, $SITE; 738 $notifysuccess = true; 739 $locks = $this->get_lock_summaries(); 740 741 $lock = required_param('lock', PARAM_ALPHANUMEXT); 742 $confirm = optional_param('confirm', false, PARAM_BOOL); 743 if (!array_key_exists($lock, $locks)) { 744 $notifysuccess = false; 745 $notification = get_string('invalidlock', 'cache'); 746 } else if ($locks[$lock]['uses'] > 0) { 747 $notifysuccess = false; 748 $notification = get_string('deletelockhasuses', 'cache'); 749 } 750 if ($notifysuccess) { 751 if (!$confirm) { 752 $title = get_string('confirmlockdeletion', 'cache'); 753 $params = array('lock' => $lock, 'confirm' => 1, 'action' => $action, 'sesskey' => sesskey()); 754 $url = new \moodle_url($PAGE->url, $params); 755 $button = new \single_button($url, get_string('deletelock', 'cache')); 756 757 $PAGE->set_title($title); 758 $PAGE->set_heading($SITE->fullname); 759 echo $OUTPUT->header(); 760 echo $OUTPUT->heading($title); 761 $confirmation = get_string('deletelockconfirmation', 'cache', $lock); 762 echo $OUTPUT->confirm($confirmation, $button, $PAGE->url); 763 echo $OUTPUT->footer(); 764 exit; 765 } else { 766 require_sesskey(); 767 $writer = cache_config_writer::instance(); 768 $writer->delete_lock_instance($lock); 769 redirect($PAGE->url, get_string('deletelocksuccess', 'cache'), 5); 770 } 771 } else { 772 redirect($PAGE->url, $notification, null, notification::NOTIFY_ERROR); 773 } 774 } 775 776 /** 777 * Outputs the main admin page by generating it through the renderer. 778 * 779 * @param \core_cache\output\renderer $renderer the renderer to use to generate the page. 780 * @return string the HTML for the admin page. 781 */ 782 public function generate_admin_page(\core_cache\output\renderer $renderer): string { 783 $context = \context_system::instance(); 784 $html = ''; 785 786 $storepluginsummaries = $this->get_store_plugin_summaries(); 787 $storeinstancesummaries = $this->get_store_instance_summaries(); 788 $definitionsummaries = $this->get_definition_summaries(); 789 $defaultmodestores = $this->get_default_mode_stores(); 790 $locks = $this->get_lock_summaries(); 791 792 $html .= $renderer->store_plugin_summaries($storepluginsummaries); 793 $html .= $renderer->store_instance_summariers($storeinstancesummaries, $storepluginsummaries); 794 $html .= $renderer->definition_summaries($definitionsummaries, $context); 795 $html .= $renderer->lock_summaries($locks); 796 $html .= $renderer->additional_lock_actions(); 797 798 $applicationstore = join(', ', $defaultmodestores[cache_store::MODE_APPLICATION]); 799 $sessionstore = join(', ', $defaultmodestores[cache_store::MODE_SESSION]); 800 $requeststore = join(', ', $defaultmodestores[cache_store::MODE_REQUEST]); 801 $editurl = new \moodle_url('/cache/admin.php', array('action' => 'editmodemappings')); 802 $html .= $renderer->mode_mappings($applicationstore, $sessionstore, $requeststore, $editurl); 803 804 return $html; 805 } 806 807 /** 808 * Gets usage information about the whole cache system. 809 * 810 * This is a slow function and should only be used on an admin information page. 811 * 812 * The returned array lists all cache definitions with fields 'cacheid' and 'stores'. For 813 * each store, the following fields are available: 814 * 815 * - name (store name) 816 * - class (e.g. cachestore_redis) 817 * - supported (true if we have any information) 818 * - items (number of items stored) 819 * - mean (mean size of item) 820 * - sd (standard deviation for item sizes) 821 * - margin (margin of error for mean at 95% confidence) 822 * - storetotal (total usage for store if known, otherwise null) 823 * 824 * The storetotal field will be the same for every cache that uses the same store. 825 * 826 * @param int $samplekeys Number of keys to sample when checking size of large caches 827 * @return array Details of cache usage 828 */ 829 public function get_usage(int $samplekeys): array { 830 $results = []; 831 832 $factory = cache_factory::instance(); 833 834 // Check the caches we already have an instance of, so we don't make another one... 835 $got = $factory->get_caches_in_use(); 836 $gotid = []; 837 foreach ($got as $longid => $unused) { 838 // The IDs here can be of the form cacheid/morestuff if there are parameters in the 839 // cache. Any entry for a cacheid is good enough to consider that we don't need to make 840 // another entry ourselves, so we remove the extra bits and track the basic cache id. 841 $gotid[preg_replace('~^([^/]+/[^/]+)/.*$~', '$1', $longid)] = true; 842 } 843 844 $storetotals = []; 845 846 $config = $factory->create_config_instance(); 847 foreach ($config->get_definitions() as $configdetails) { 848 if (!array_key_exists($configdetails['component'] . '/' . $configdetails['area'], $gotid)) { 849 // Where possible (if it doesn't need identifiers), make an instance of the cache, otherwise 850 // we can't get the store instances for it (and it won't show up in the list). 851 if (empty($configdetails['requireidentifiers'])) { 852 \cache::make($configdetails['component'], $configdetails['area']); 853 } 854 } 855 $definition = $factory->create_definition($configdetails['component'], $configdetails['area']); 856 $stores = $factory->get_store_instances_in_use($definition); 857 858 // Create object for results about this cache definition. 859 $currentresult = (object)['cacheid' => $definition->get_id(), 'stores' => []]; 860 $results[$currentresult->cacheid] = $currentresult; 861 862 /** @var cache_store $store */ 863 foreach ($stores as $store) { 864 // Skip static cache. 865 if ($store instanceof \cachestore_static) { 866 continue; 867 } 868 869 // Get cache size details from store. 870 $currentstore = $store->cache_size_details($samplekeys); 871 872 // Add in basic information about store. 873 $currentstore->name = $store->my_name(); 874 $currentstore->class = get_class($store); 875 876 // Add in store total. 877 if (!array_key_exists($currentstore->name, $storetotals)) { 878 $storetotals[$currentstore->name] = $store->store_total_size(); 879 } 880 $currentstore->storetotal = $storetotals[$currentstore->name]; 881 882 $currentresult->stores[] = $currentstore; 883 } 884 } 885 886 ksort($results); 887 return $results; 888 } 889 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body