Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
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 defined('MOODLE_INTERNAL') || die(); 34 use cache_store, cache_factory, cache_config_writer, cache_helper; 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 print_error('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 * @return void 509 */ 510 public function action_deletestore(string $action) { 511 global $OUTPUT, $PAGE, $SITE; 512 $notifysuccess = true; 513 $storeinstancesummaries = $this->get_store_instance_summaries(); 514 515 $store = required_param('store', PARAM_TEXT); 516 $confirm = optional_param('confirm', false, PARAM_BOOL); 517 518 if (!array_key_exists($store, $storeinstancesummaries)) { 519 $notifysuccess = false; 520 $notifications[] = array(get_string('invalidstore', 'cache'), false); 521 } else if ($storeinstancesummaries[$store]['mappings'] > 0) { 522 $notifysuccess = false; 523 $notifications[] = array(get_string('deletestorehasmappings', 'cache'), false); 524 } 525 526 if ($notifysuccess) { 527 if (!$confirm) { 528 $title = get_string('confirmstoredeletion', 'cache'); 529 $params = array('store' => $store, 'confirm' => 1, 'action' => $action, 'sesskey' => sesskey()); 530 $url = new \moodle_url($PAGE->url, $params); 531 $button = new \single_button($url, get_string('deletestore', 'cache')); 532 533 $PAGE->set_title($title); 534 $PAGE->set_heading($SITE->fullname); 535 echo $OUTPUT->header(); 536 echo $OUTPUT->heading($title); 537 $confirmation = get_string('deletestoreconfirmation', 'cache', $storeinstancesummaries[$store]['name']); 538 echo $OUTPUT->confirm($confirmation, $button, $PAGE->url); 539 echo $OUTPUT->footer(); 540 exit; 541 } else { 542 require_sesskey(); 543 $writer = \cache_config_writer::instance(); 544 $writer->delete_store_instance($store); 545 redirect($PAGE->url, get_string('deletestoresuccess', 'cache'), 5); 546 } 547 } 548 } 549 550 /** 551 * Performs the edit definition mapping action. 552 * 553 * @return array an array of the form to display, and the page title. 554 * @throws cache_exception 555 */ 556 public function action_editdefinitionmapping(): array { 557 global $PAGE; 558 $definitionsummaries = $this->get_definition_summaries(); 559 560 $definition = required_param('definition', PARAM_SAFEPATH); 561 if (!array_key_exists($definition, $definitionsummaries)) { 562 throw new \cache_exception('Invalid cache definition requested'); 563 } 564 $title = get_string('editdefinitionmappings', 'cache', $definition); 565 $mform = new \cache_definition_mappings_form($PAGE->url, array('definition' => $definition)); 566 if ($mform->is_cancelled()) { 567 redirect($PAGE->url); 568 } else if ($data = $mform->get_data()) { 569 $writer = \cache_config_writer::instance(); 570 $mappings = array(); 571 foreach ($data->mappings as $mapping) { 572 if (!empty($mapping)) { 573 $mappings[] = $mapping; 574 } 575 } 576 $writer->set_definition_mappings($definition, $mappings); 577 redirect($PAGE->url); 578 } 579 580 $PAGE->navbar->add(get_string('updatedefinitionmapping', 'cache'), $PAGE->url); 581 return array('form' => $mform, 'title' => $title); 582 } 583 584 /** 585 * Performs the edit definition sharing action. 586 * 587 * @return array an array of the edit definition sharing form, and the page title. 588 */ 589 public function action_editdefinitionsharing(): array { 590 global $PAGE; 591 $definitionsummaries = $this->get_definition_summaries(); 592 593 $definition = required_param('definition', PARAM_SAFEPATH); 594 if (!array_key_exists($definition, $definitionsummaries)) { 595 throw new \cache_exception('Invalid cache definition requested'); 596 } 597 $title = get_string('editdefinitionsharing', 'cache', $definition); 598 $sharingoptions = $definitionsummaries[$definition]['sharingoptions']; 599 $customdata = array('definition' => $definition, 'sharingoptions' => $sharingoptions); 600 $mform = new \cache_definition_sharing_form($PAGE->url, $customdata); 601 $mform->set_data(array( 602 'sharing' => $definitionsummaries[$definition]['selectedsharingoption'], 603 'userinputsharingkey' => $definitionsummaries[$definition]['userinputsharingkey'] 604 )); 605 if ($mform->is_cancelled()) { 606 redirect($PAGE->url); 607 } else if ($data = $mform->get_data()) { 608 $component = $definitionsummaries[$definition]['component']; 609 $area = $definitionsummaries[$definition]['area']; 610 // Purge the stores removing stale data before we alter the sharing option. 611 \cache_helper::purge_stores_used_by_definition($component, $area); 612 $writer = \cache_config_writer::instance(); 613 $sharing = array_sum(array_keys($data->sharing)); 614 $userinputsharingkey = $data->userinputsharingkey; 615 $writer->set_definition_sharing($definition, $sharing, $userinputsharingkey); 616 redirect($PAGE->url); 617 } 618 619 $PAGE->navbar->add(get_string('updatedefinitionsharing', 'cache'), $PAGE->url); 620 return array('form' => $mform, 'title' => $title); 621 } 622 623 /** 624 * Performs the edit mode mappings action. 625 * 626 * @return array an array of the edit mode mappings form. 627 */ 628 public function action_editmodemappings(): array { 629 global $PAGE; 630 $storeinstancesummaries = $this->get_store_instance_summaries(); 631 $defaultmodestores = $this->get_default_mode_stores(); 632 633 $mform = new \cache_mode_mappings_form(null, $storeinstancesummaries); 634 $mform->set_data(array( 635 'mode_'.cache_store::MODE_APPLICATION => key($defaultmodestores[cache_store::MODE_APPLICATION]), 636 'mode_'.cache_store::MODE_SESSION => key($defaultmodestores[cache_store::MODE_SESSION]), 637 'mode_'.cache_store::MODE_REQUEST => key($defaultmodestores[cache_store::MODE_REQUEST]), 638 )); 639 if ($mform->is_cancelled()) { 640 redirect($PAGE->url); 641 } else if ($data = $mform->get_data()) { 642 $mappings = array( 643 cache_store::MODE_APPLICATION => array($data->{'mode_'.cache_store::MODE_APPLICATION}), 644 cache_store::MODE_SESSION => array($data->{'mode_'.cache_store::MODE_SESSION}), 645 cache_store::MODE_REQUEST => array($data->{'mode_'.cache_store::MODE_REQUEST}), 646 ); 647 $writer = cache_config_writer::instance(); 648 $writer->set_mode_mappings($mappings); 649 redirect($PAGE->url); 650 } 651 652 return array('form' => $mform); 653 } 654 655 /** 656 * Performs the purge definition action. 657 * 658 * @return void 659 */ 660 public function action_purgedefinition() { 661 global $PAGE; 662 663 require_sesskey(); 664 $id = required_param('definition', PARAM_SAFEPATH); 665 list($component, $area) = explode('/', $id, 2); 666 $factory = cache_factory::instance(); 667 $definition = $factory->create_definition($component, $area); 668 if ($definition->has_required_identifiers()) { 669 // We will have to purge the stores used by this definition. 670 cache_helper::purge_stores_used_by_definition($component, $area); 671 } else { 672 // Alrighty we can purge just the data belonging to this definition. 673 cache_helper::purge_by_definition($component, $area); 674 } 675 676 $message = get_string('purgexdefinitionsuccess', 'cache', [ 677 'name' => $definition->get_name(), 678 'component' => $component, 679 'area' => $area, 680 ]); 681 $purgeagainlink = \html_writer::link(new \moodle_url('/cache/admin.php', [ 682 'action' => 'purgedefinition', 'sesskey' => sesskey(), 'definition' => $id]), 683 get_string('purgeagain', 'cache')); 684 redirect($PAGE->url, $message . ' ' . $purgeagainlink, 5); 685 } 686 687 /** 688 * Performs the purge action. 689 * 690 * @return void 691 */ 692 public function action_purge() { 693 global $PAGE; 694 695 require_sesskey(); 696 $store = required_param('store', PARAM_TEXT); 697 cache_helper::purge_store($store); 698 $message = get_string('purgexstoresuccess', 'cache', ['store' => $store]); 699 $purgeagainlink = \html_writer::link(new \moodle_url('/cache/admin.php', [ 700 'action' => 'purgestore', 'sesskey' => sesskey(), 'store' => $store]), 701 get_string('purgeagain', 'cache')); 702 redirect($PAGE->url, $message . ' ' . $purgeagainlink, 5); 703 } 704 705 /** 706 * Performs the new lock instance action. 707 * 708 * @return array An array containing the new lock instance form. 709 */ 710 public function action_newlockinstance(): array { 711 global $PAGE; 712 713 // Adds a new lock instance. 714 $lock = required_param('lock', PARAM_ALPHANUMEXT); 715 $mform = $this->get_add_lock_form($lock); 716 if ($mform->is_cancelled()) { 717 redirect($PAGE->url); 718 } else if ($data = $mform->get_data()) { 719 $factory = cache_factory::instance(); 720 $config = $factory->create_config_instance(true); 721 $name = $data->name; 722 $data = $this->get_lock_configuration_from_data($lock, $data); 723 $config->add_lock_instance($name, $lock, $data); 724 redirect($PAGE->url, get_string('addlocksuccess', 'cache', $name), 5); 725 } 726 727 return array('form' => $mform); 728 } 729 730 /** 731 * Performs the delete lock action. 732 * 733 * @param string $action the action calling this function. 734 * @return void 735 */ 736 public function action_deletelock(string $action) { 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 $notifications[] = array(get_string('invalidlock', 'cache'), false); 746 } else if ($locks[$lock]['uses'] > 0) { 747 $notifysuccess = false; 748 $notifications[] = array(get_string('deletelockhasuses', 'cache'), false); 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 } 772 } 773 774 /** 775 * Outputs the main admin page by generating it through the renderer. 776 * 777 * @param \core_cache\output\renderer $renderer the renderer to use to generate the page. 778 * @return string the HTML for the admin page. 779 */ 780 public function generate_admin_page(\core_cache\output\renderer $renderer): string { 781 $context = \context_system::instance(); 782 $html = ''; 783 784 $storepluginsummaries = $this->get_store_plugin_summaries(); 785 $storeinstancesummaries = $this->get_store_instance_summaries(); 786 $definitionsummaries = $this->get_definition_summaries(); 787 $defaultmodestores = $this->get_default_mode_stores(); 788 $locks = $this->get_lock_summaries(); 789 790 $html .= $renderer->store_plugin_summaries($storepluginsummaries); 791 $html .= $renderer->store_instance_summariers($storeinstancesummaries, $storepluginsummaries); 792 $html .= $renderer->definition_summaries($definitionsummaries, $context); 793 $html .= $renderer->lock_summaries($locks); 794 $html .= $renderer->additional_lock_actions(); 795 796 $applicationstore = join(', ', $defaultmodestores[cache_store::MODE_APPLICATION]); 797 $sessionstore = join(', ', $defaultmodestores[cache_store::MODE_SESSION]); 798 $requeststore = join(', ', $defaultmodestores[cache_store::MODE_REQUEST]); 799 $editurl = new \moodle_url('/cache/admin.php', array('action' => 'editmodemappings')); 800 $html .= $renderer->mode_mappings($applicationstore, $sessionstore, $requeststore, $editurl); 801 802 return $html; 803 } 804 805 /** 806 * Gets usage information about the whole cache system. 807 * 808 * This is a slow function and should only be used on an admin information page. 809 * 810 * The returned array lists all cache definitions with fields 'cacheid' and 'stores'. For 811 * each store, the following fields are available: 812 * 813 * - name (store name) 814 * - class (e.g. cachestore_redis) 815 * - supported (true if we have any information) 816 * - items (number of items stored) 817 * - mean (mean size of item) 818 * - sd (standard deviation for item sizes) 819 * - margin (margin of error for mean at 95% confidence) 820 * - storetotal (total usage for store if known, otherwise null) 821 * 822 * The storetotal field will be the same for every cache that uses the same store. 823 * 824 * @param int $samplekeys Number of keys to sample when checking size of large caches 825 * @return array Details of cache usage 826 */ 827 public function get_usage(int $samplekeys): array { 828 $results = []; 829 830 $factory = cache_factory::instance(); 831 832 // Check the caches we already have an instance of, so we don't make another one... 833 $got = $factory->get_caches_in_use(); 834 $gotid = []; 835 foreach ($got as $longid => $unused) { 836 // The IDs here can be of the form cacheid/morestuff if there are parameters in the 837 // cache. Any entry for a cacheid is good enough to consider that we don't need to make 838 // another entry ourselves, so we remove the extra bits and track the basic cache id. 839 $gotid[preg_replace('~^([^/]+/[^/]+)/.*$~', '$1', $longid)] = true; 840 } 841 842 $storetotals = []; 843 844 $config = $factory->create_config_instance(); 845 foreach ($config->get_definitions() as $configdetails) { 846 if (!array_key_exists($configdetails['component'] . '/' . $configdetails['area'], $gotid)) { 847 // Where possible (if it doesn't need identifiers), make an instance of the cache, otherwise 848 // we can't get the store instances for it (and it won't show up in the list). 849 if (empty($configdetails['requireidentifiers'])) { 850 \cache::make($configdetails['component'], $configdetails['area']); 851 } 852 } 853 $definition = $factory->create_definition($configdetails['component'], $configdetails['area']); 854 $stores = $factory->get_store_instances_in_use($definition); 855 856 // Create object for results about this cache definition. 857 $currentresult = (object)['cacheid' => $definition->get_id(), 'stores' => []]; 858 $results[$currentresult->cacheid] = $currentresult; 859 860 /** @var cache_store $store */ 861 foreach ($stores as $store) { 862 // Skip static cache. 863 if ($store instanceof \cachestore_static) { 864 continue; 865 } 866 867 // Get cache size details from store. 868 $currentstore = $store->cache_size_details($samplekeys); 869 870 // Add in basic information about store. 871 $currentstore->name = $store->my_name(); 872 $currentstore->class = get_class($store); 873 874 // Add in store total. 875 if (!array_key_exists($currentstore->name, $storetotals)) { 876 $storetotals[$currentstore->name] = $store->store_total_size(); 877 } 878 $currentstore->storetotal = $storetotals[$currentstore->name]; 879 880 $currentresult->stores[] = $currentstore; 881 } 882 } 883 884 ksort($results); 885 return $results; 886 } 887 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body