Files
moodle/cache/classes/local/administration_display_helper.php
T
Sujith Haridasan aa1c2e2382 MDL-73848 administration: Fix the breadcrumbs and nav highlights
Create or update the breadcrumbs in the site administration
pages where it is required.
Highlight the corresponding site adminstration tab.
Highlight the primary nav to Site administration when user
is navigating to any of the site administration pages.

Also changed the boostnavbar so that the nodes in the secondary
navigation are not shown in the breadcrumbs when user is in site
administration page.
2022-03-25 17:44:47 +08:00

882 lines
36 KiB
PHP

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Cache display administration helper.
*
* This file is part of Moodle's cache API, affectionately called MUC.
* It contains the components that are requried in order to use caching.
*
* @package core
* @category cache
* @author Peter Burnett <peterburnett@catalyst-au.net>
* @copyright 2020 Catalyst IT
* @copyright 2012 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_cache\local;
defined('MOODLE_INTERNAL') || die();
use cache_store, cache_factory, cache_config_writer, cache_helper;
/**
* A cache helper for administration tasks
*
* @package core
* @category cache
* @copyright 2020 Peter Burnett <peterburnett@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class administration_display_helper extends \core_cache\administration_helper {
/**
* Please do not call constructor directly. Use cache_factory::get_administration_display_helper() instead.
*/
public function __construct() {
// Nothing to do here.
}
/**
* Returns all of the actions that can be performed on a definition.
*
* @param context $context the system context.
* @param array $definitionsummary information about this cache, from the array returned by
* core_cache\administration_helper::get_definition_summaries(). Currently only 'sharingoptions'
* element is used.
* @return array of actions. Each action is an action_url.
*/
public function get_definition_actions(\context $context, array $definitionsummary): array {
global $OUTPUT;
if (has_capability('moodle/site:config', $context)) {
$actions = array();
// Edit mappings.
$actions[] = $OUTPUT->action_link(
new \moodle_url('/cache/admin.php', array('action' => 'editdefinitionmapping',
'definition' => $definitionsummary['id'], 'sesskey' => sesskey())),
get_string('editmappings', 'cache')
);
// Edit sharing.
if (count($definitionsummary['sharingoptions']) > 1) {
$actions[] = $OUTPUT->action_link(
new \moodle_url('/cache/admin.php', array('action' => 'editdefinitionsharing',
'definition' => $definitionsummary['id'], 'sesskey' => sesskey())),
get_string('editsharing', 'cache')
);
}
// Purge.
$actions[] = $OUTPUT->action_link(
new \moodle_url('/cache/admin.php', array('action' => 'purgedefinition',
'definition' => $definitionsummary['id'], 'sesskey' => sesskey())),
get_string('purge', 'cache')
);
return $actions;
}
return array();
}
/**
* Returns all of the actions that can be performed on a store.
*
* @param string $name The name of the store
* @param array $storedetails information about this store, from the array returned by
* core_cache\administration_helper::get_store_instance_summaries().
* @return array of actions. Each action is an action_url.
*/
public function get_store_instance_actions(string $name, array $storedetails): array {
global $OUTPUT;
$actions = array();
if (has_capability('moodle/site:config', \context_system::instance())) {
$baseurl = new \moodle_url('/cache/admin.php', array('store' => $name, 'sesskey' => sesskey()));
if (empty($storedetails['default'])) {
$actions[] = $OUTPUT->action_link(
new \moodle_url($baseurl, array('action' => 'editstore', 'plugin' => $storedetails['plugin'])),
get_string('editstore', 'cache')
);
$actions[] = $OUTPUT->action_link(
new \moodle_url($baseurl, array('action' => 'deletestore')),
get_string('deletestore', 'cache')
);
}
$actions[] = $OUTPUT->action_link(
new \moodle_url($baseurl, array('action' => 'purgestore')),
get_string('purge', 'cache')
);
}
return $actions;
}
/**
* Returns all of the actions that can be performed on a plugin.
*
* @param string $name The name of the plugin
* @param array $plugindetails information about this store, from the array returned by
* core_cache\administration_helper::get_store_plugin_summaries().
* @return array of actions. Each action is an action_url.
*/
public function get_store_plugin_actions(string $name, array $plugindetails): array {
global $OUTPUT;
$actions = array();
if (has_capability('moodle/site:config', \context_system::instance())) {
if (!empty($plugindetails['canaddinstance'])) {
$url = new \moodle_url('/cache/admin.php',
array('action' => 'addstore', 'plugin' => $name, 'sesskey' => sesskey()));
$actions[] = $OUTPUT->action_link(
$url,
get_string('addinstance', 'cache')
);
}
}
return $actions;
}
/**
* Returns a form that can be used to add a store instance.
*
* @param string $plugin The plugin to add an instance of
* @return cachestore_addinstance_form
* @throws coding_exception
*/
public function get_add_store_form(string $plugin): \cachestore_addinstance_form {
global $CFG; // Needed for includes.
$plugins = \core_component::get_plugin_list('cachestore');
if (!array_key_exists($plugin, $plugins)) {
throw new \coding_exception('Invalid cache plugin used when trying to create an edit form.');
}
$plugindir = $plugins[$plugin];
$class = 'cachestore_addinstance_form';
if (file_exists($plugindir.'/addinstanceform.php')) {
require_once($plugindir.'/addinstanceform.php');
if (class_exists('cachestore_'.$plugin.'_addinstance_form')) {
$class = 'cachestore_'.$plugin.'_addinstance_form';
if (!array_key_exists('cachestore_addinstance_form', class_parents($class))) {
throw new \coding_exception('Cache plugin add instance forms must extend cachestore_addinstance_form');
}
}
}
$locks = $this->get_possible_locks_for_stores($plugindir, $plugin);
$url = new \moodle_url('/cache/admin.php', array('action' => 'addstore'));
return new $class($url, array('plugin' => $plugin, 'store' => null, 'locks' => $locks));
}
/**
* Returns a form that can be used to edit a store instance.
*
* @param string $plugin
* @param string $store
* @return cachestore_addinstance_form
* @throws coding_exception
*/
public function get_edit_store_form(string $plugin, string $store): \cachestore_addinstance_form {
global $CFG; // Needed for includes.
$plugins = \core_component::get_plugin_list('cachestore');
if (!array_key_exists($plugin, $plugins)) {
throw new \coding_exception('Invalid cache plugin used when trying to create an edit form.');
}
$factory = \cache_factory::instance();
$config = $factory->create_config_instance();
$stores = $config->get_all_stores();
if (!array_key_exists($store, $stores)) {
throw new \coding_exception('Invalid store name given when trying to create an edit form.');
}
$plugindir = $plugins[$plugin];
$class = 'cachestore_addinstance_form';
if (file_exists($plugindir.'/addinstanceform.php')) {
require_once($plugindir.'/addinstanceform.php');
if (class_exists('cachestore_'.$plugin.'_addinstance_form')) {
$class = 'cachestore_'.$plugin.'_addinstance_form';
if (!array_key_exists('cachestore_addinstance_form', class_parents($class))) {
throw new \coding_exception('Cache plugin add instance forms must extend cachestore_addinstance_form');
}
}
}
$locks = $this->get_possible_locks_for_stores($plugindir, $plugin);
$url = new \moodle_url('/cache/admin.php', array('action' => 'editstore', 'plugin' => $plugin, 'store' => $store));
$editform = new $class($url, array('plugin' => $plugin, 'store' => $store, 'locks' => $locks));
if (isset($stores[$store]['lock'])) {
$editform->set_data(array('lock' => $stores[$store]['lock']));
}
// See if the cachestore is going to want to load data for the form.
// If it has a customised add instance form then it is going to want to.
$storeclass = 'cachestore_'.$plugin;
$storedata = $stores[$store];
if (array_key_exists('configuration', $storedata) &&
array_key_exists('cache_is_configurable', class_implements($storeclass))) {
$storeclass::config_set_edit_form_data($editform, $storedata['configuration']);
}
return $editform;
}
/**
* Returns an array of suitable lock instances for use with this plugin, or false if the plugin handles locking itself.
*
* @param string $plugindir
* @param string $plugin
* @return array|false
*/
protected function get_possible_locks_for_stores(string $plugindir, string $plugin) {
global $CFG; // Needed for includes.
$supportsnativelocking = false;
if (file_exists($plugindir.'/lib.php')) {
require_once($plugindir.'/lib.php');
$pluginclass = 'cachestore_'.$plugin;
if (class_exists($pluginclass)) {
$supportsnativelocking = array_key_exists('cache_is_lockable', class_implements($pluginclass));
}
}
if (!$supportsnativelocking) {
$config = \cache_config::instance();
$locks = array();
foreach ($config->get_locks() as $lock => $conf) {
if (!empty($conf['default'])) {
$name = get_string($lock, 'cache');
} else {
$name = $lock;
}
$locks[$lock] = $name;
}
} else {
$locks = false;
}
return $locks;
}
/**
* Processes the results of the add/edit instance form data for a plugin returning an array of config information suitable to
* store in configuration.
*
* @param stdClass $data The mform data.
* @return array
* @throws coding_exception
*/
public function get_store_configuration_from_data(\stdClass $data): array {
global $CFG;
$file = $CFG->dirroot.'/cache/stores/'.$data->plugin.'/lib.php';
if (!file_exists($file)) {
throw new \coding_exception('Invalid cache plugin provided. '.$file);
}
require_once($file);
$class = 'cachestore_'.$data->plugin;
if (!class_exists($class)) {
throw new \coding_exception('Invalid cache plugin provided.');
}
if (array_key_exists('cache_is_configurable', class_implements($class))) {
return $class::config_get_configuration_array($data);
}
return array();
}
/**
* Returns an array of lock plugins for which we can add an instance.
*
* Suitable for use within an mform select element.
*
* @return array
*/
public function get_addable_lock_options(): array {
$plugins = \core_component::get_plugin_list_with_class('cachelock', '', 'lib.php');
$options = array();
$len = strlen('cachelock_');
foreach ($plugins as $plugin => $class) {
$method = "$class::can_add_instance";
if (is_callable($method) && !call_user_func($method)) {
// Can't add an instance of this plugin.
continue;
}
$options[substr($plugin, $len)] = get_string('pluginname', $plugin);
}
return $options;
}
/**
* Gets the form to use when adding a lock instance.
*
* @param string $plugin
* @param array $lockplugin
* @return cache_lock_form
* @throws coding_exception
*/
public function get_add_lock_form(string $plugin, array $lockplugin = null): \cache_lock_form {
global $CFG; // Needed for includes.
$plugins = \core_component::get_plugin_list('cachelock');
if (!array_key_exists($plugin, $plugins)) {
throw new \coding_exception('Invalid cache lock plugin requested when trying to create a form.');
}
$plugindir = $plugins[$plugin];
$class = 'cache_lock_form';
if (file_exists($plugindir.'/addinstanceform.php') && in_array('cache_is_configurable', class_implements($class))) {
require_once($plugindir.'/addinstanceform.php');
if (class_exists('cachelock_'.$plugin.'_addinstance_form')) {
$class = 'cachelock_'.$plugin.'_addinstance_form';
if (!array_key_exists('cache_lock_form', class_parents($class))) {
throw new \coding_exception('Cache lock plugin add instance forms must extend cache_lock_form');
}
}
}
return new $class(null, array('lock' => $plugin));
}
/**
* Gets configuration data from a new lock instance form.
*
* @param string $plugin
* @param stdClass $data
* @return array
* @throws coding_exception
*/
public function get_lock_configuration_from_data(string $plugin, \stdClass $data): array {
global $CFG;
$file = $CFG->dirroot.'/cache/locks/'.$plugin.'/lib.php';
if (!file_exists($file)) {
throw new \coding_exception('Invalid cache plugin provided. '.$file);
}
require_once($file);
$class = 'cachelock_'.$plugin;
if (!class_exists($class)) {
throw new \coding_exception('Invalid cache plugin provided.');
}
if (array_key_exists('cache_is_configurable', class_implements($class))) {
return $class::config_get_configuration_array($data);
}
return array();
}
/**
* Handles the page actions, based on the parameter.
*
* @param string $action the action to handle.
* @param array $forminfo an empty array to be overridden and set.
* @return array the empty or overridden forminfo array.
*/
public function perform_cache_actions(string $action, array $forminfo): array {
switch ($action) {
case 'rescandefinitions' : // Rescan definitions.
$this->action_rescan_definition();
break;
case 'addstore' : // Add the requested store.
$forminfo = $this->action_addstore();
break;
case 'editstore' : // Edit the requested store.
$forminfo = $this->action_editstore();
break;
case 'deletestore' : // Delete a given store.
$this->action_deletestore($action);
break;
case 'editdefinitionmapping' : // Edit definition mappings.
$forminfo = $this->action_editdefinitionmapping();
break;
case 'editdefinitionsharing' : // Edit definition sharing.
$forminfo = $this->action_editdefinitionsharing();
break;
case 'editmodemappings': // Edit default mode mappings.
$forminfo = $this->action_editmodemappings();
break;
case 'purgedefinition': // Purge a specific definition.
$this->action_purgedefinition();
break;
case 'purgestore':
case 'purge': // Purge a store cache.
$this->action_purge();
break;
case 'newlockinstance':
$forminfo = $this->action_newlockinstance();
break;
case 'deletelock':
// Deletes a lock instance.
$this->action_deletelock($action);
break;
}
return $forminfo;
}
/**
* Performs the rescan definition action.
*
* @return void
*/
public function action_rescan_definition() {
global $PAGE;
\cache_config_writer::update_definitions();
redirect($PAGE->url);
}
/**
* Performs the add store action.
*
* @return array an array of the form to display to the user, and the page title.
*/
public function action_addstore() : array {
global $PAGE;
$storepluginsummaries = $this->get_store_plugin_summaries();
$plugin = required_param('plugin', PARAM_PLUGIN);
if (!$storepluginsummaries[$plugin]['canaddinstance']) {
print_error('ex_unmetstorerequirements', 'cache');
}
$mform = $this->get_add_store_form($plugin);
$title = get_string('addstore', 'cache', $storepluginsummaries[$plugin]['name']);
if ($mform->is_cancelled()) {
redirect($PAGE->url);
} else if ($data = $mform->get_data()) {
$config = $this->get_store_configuration_from_data($data);
$writer = \cache_config_writer::instance();
unset($config['lock']);
foreach ($writer->get_locks() as $lock => $lockconfig) {
if ($lock == $data->lock) {
$config['lock'] = $data->lock;
}
}
$writer->add_store_instance($data->name, $data->plugin, $config);
redirect($PAGE->url, get_string('addstoresuccess', 'cache', $storepluginsummaries[$plugin]['name']), 5);
}
$PAGE->navbar->add(get_string('addstore', 'cache', 'cache'), $PAGE->url);
return array('form' => $mform, 'title' => $title);
}
/**
* Performs the edit store action.
*
* @return array an array of the form to display, and the page title.
*/
public function action_editstore(): array {
global $PAGE;
$storepluginsummaries = $this->get_store_plugin_summaries();
$plugin = required_param('plugin', PARAM_PLUGIN);
$store = required_param('store', PARAM_TEXT);
$mform = $this->get_edit_store_form($plugin, $store);
$title = get_string('addstore', 'cache', $storepluginsummaries[$plugin]['name']);
if ($mform->is_cancelled()) {
redirect($PAGE->url);
} else if ($data = $mform->get_data()) {
$config = $this->get_store_configuration_from_data($data);
$writer = \cache_config_writer::instance();
unset($config['lock']);
foreach ($writer->get_locks() as $lock => $lockconfig) {
if ($lock == $data->lock) {
$config['lock'] = $data->lock;
}
}
$writer->edit_store_instance($data->name, $data->plugin, $config);
redirect($PAGE->url, get_string('editstoresuccess', 'cache', $storepluginsummaries[$plugin]['name']), 5);
}
return array('form' => $mform, 'title' => $title);
}
/**
* Performs the deletestore action.
*
* @param string $action the action calling to this function.
* @return void
*/
public function action_deletestore(string $action) {
global $OUTPUT, $PAGE, $SITE;
$notifysuccess = true;
$storeinstancesummaries = $this->get_store_instance_summaries();
$store = required_param('store', PARAM_TEXT);
$confirm = optional_param('confirm', false, PARAM_BOOL);
if (!array_key_exists($store, $storeinstancesummaries)) {
$notifysuccess = false;
$notifications[] = array(get_string('invalidstore', 'cache'), false);
} else if ($storeinstancesummaries[$store]['mappings'] > 0) {
$notifysuccess = false;
$notifications[] = array(get_string('deletestorehasmappings', 'cache'), false);
}
if ($notifysuccess) {
if (!$confirm) {
$title = get_string('confirmstoredeletion', 'cache');
$params = array('store' => $store, 'confirm' => 1, 'action' => $action, 'sesskey' => sesskey());
$url = new \moodle_url($PAGE->url, $params);
$button = new \single_button($url, get_string('deletestore', 'cache'));
$PAGE->set_title($title);
$PAGE->set_heading($SITE->fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($title);
$confirmation = get_string('deletestoreconfirmation', 'cache', $storeinstancesummaries[$store]['name']);
echo $OUTPUT->confirm($confirmation, $button, $PAGE->url);
echo $OUTPUT->footer();
exit;
} else {
$writer = \cache_config_writer::instance();
$writer->delete_store_instance($store);
redirect($PAGE->url, get_string('deletestoresuccess', 'cache'), 5);
}
}
}
/**
* Performs the edit definition mapping action.
*
* @return array an array of the form to display, and the page title.
* @throws cache_exception
*/
public function action_editdefinitionmapping(): array {
global $PAGE;
$definitionsummaries = $this->get_definition_summaries();
$definition = required_param('definition', PARAM_SAFEPATH);
if (!array_key_exists($definition, $definitionsummaries)) {
throw new \cache_exception('Invalid cache definition requested');
}
$title = get_string('editdefinitionmappings', 'cache', $definition);
$mform = new \cache_definition_mappings_form($PAGE->url, array('definition' => $definition));
if ($mform->is_cancelled()) {
redirect($PAGE->url);
} else if ($data = $mform->get_data()) {
$writer = \cache_config_writer::instance();
$mappings = array();
foreach ($data->mappings as $mapping) {
if (!empty($mapping)) {
$mappings[] = $mapping;
}
}
$writer->set_definition_mappings($definition, $mappings);
redirect($PAGE->url);
}
$PAGE->navbar->add(get_string('updatedefinitionmapping', 'cache'), $PAGE->url);
return array('form' => $mform, 'title' => $title);
}
/**
* Performs the edit definition sharing action.
*
* @return array an array of the edit definition sharing form, and the page title.
*/
public function action_editdefinitionsharing(): array {
global $PAGE;
$definitionsummaries = $this->get_definition_summaries();
$definition = required_param('definition', PARAM_SAFEPATH);
if (!array_key_exists($definition, $definitionsummaries)) {
throw new \cache_exception('Invalid cache definition requested');
}
$title = get_string('editdefinitionsharing', 'cache', $definition);
$sharingoptions = $definitionsummaries[$definition]['sharingoptions'];
$customdata = array('definition' => $definition, 'sharingoptions' => $sharingoptions);
$mform = new \cache_definition_sharing_form($PAGE->url, $customdata);
$mform->set_data(array(
'sharing' => $definitionsummaries[$definition]['selectedsharingoption'],
'userinputsharingkey' => $definitionsummaries[$definition]['userinputsharingkey']
));
if ($mform->is_cancelled()) {
redirect($PAGE->url);
} else if ($data = $mform->get_data()) {
$component = $definitionsummaries[$definition]['component'];
$area = $definitionsummaries[$definition]['area'];
// Purge the stores removing stale data before we alter the sharing option.
\cache_helper::purge_stores_used_by_definition($component, $area);
$writer = \cache_config_writer::instance();
$sharing = array_sum(array_keys($data->sharing));
$userinputsharingkey = $data->userinputsharingkey;
$writer->set_definition_sharing($definition, $sharing, $userinputsharingkey);
redirect($PAGE->url);
}
$PAGE->navbar->add(get_string('updatedefinitionsharing', 'cache'), $PAGE->url);
return array('form' => $mform, 'title' => $title);
}
/**
* Performs the edit mode mappings action.
*
* @return array an array of the edit mode mappings form.
*/
public function action_editmodemappings(): array {
global $PAGE;
$storeinstancesummaries = $this->get_store_instance_summaries();
$defaultmodestores = $this->get_default_mode_stores();
$mform = new \cache_mode_mappings_form(null, $storeinstancesummaries);
$mform->set_data(array(
'mode_'.cache_store::MODE_APPLICATION => key($defaultmodestores[cache_store::MODE_APPLICATION]),
'mode_'.cache_store::MODE_SESSION => key($defaultmodestores[cache_store::MODE_SESSION]),
'mode_'.cache_store::MODE_REQUEST => key($defaultmodestores[cache_store::MODE_REQUEST]),
));
if ($mform->is_cancelled()) {
redirect($PAGE->url);
} else if ($data = $mform->get_data()) {
$mappings = array(
cache_store::MODE_APPLICATION => array($data->{'mode_'.cache_store::MODE_APPLICATION}),
cache_store::MODE_SESSION => array($data->{'mode_'.cache_store::MODE_SESSION}),
cache_store::MODE_REQUEST => array($data->{'mode_'.cache_store::MODE_REQUEST}),
);
$writer = cache_config_writer::instance();
$writer->set_mode_mappings($mappings);
redirect($PAGE->url);
}
return array('form' => $mform);
}
/**
* Performs the purge definition action.
*
* @return void
*/
public function action_purgedefinition() {
global $PAGE;
$id = required_param('definition', PARAM_SAFEPATH);
list($component, $area) = explode('/', $id, 2);
$factory = cache_factory::instance();
$definition = $factory->create_definition($component, $area);
if ($definition->has_required_identifiers()) {
// We will have to purge the stores used by this definition.
cache_helper::purge_stores_used_by_definition($component, $area);
} else {
// Alrighty we can purge just the data belonging to this definition.
cache_helper::purge_by_definition($component, $area);
}
$message = get_string('purgexdefinitionsuccess', 'cache', [
'name' => $definition->get_name(),
'component' => $component,
'area' => $area,
]);
$purgeagainlink = \html_writer::link(new \moodle_url('/cache/admin.php', [
'action' => 'purgedefinition', 'sesskey' => sesskey(), 'definition' => $id]),
get_string('purgeagain', 'cache'));
redirect($PAGE->url, $message . ' ' . $purgeagainlink, 5);
}
/**
* Performs the purge action.
*
* @return void
*/
public function action_purge() {
global $PAGE;
$store = required_param('store', PARAM_TEXT);
cache_helper::purge_store($store);
$message = get_string('purgexstoresuccess', 'cache', ['store' => $store]);
$purgeagainlink = \html_writer::link(new \moodle_url('/cache/admin.php', [
'action' => 'purgestore', 'sesskey' => sesskey(), 'store' => $store]),
get_string('purgeagain', 'cache'));
redirect($PAGE->url, $message . ' ' . $purgeagainlink, 5);
}
/**
* Performs the new lock instance action.
*
* @return array An array containing the new lock instance form.
*/
public function action_newlockinstance(): array {
global $PAGE;
// Adds a new lock instance.
$lock = required_param('lock', PARAM_ALPHANUMEXT);
$mform = $this->get_add_lock_form($lock);
if ($mform->is_cancelled()) {
redirect($PAGE->url);
} else if ($data = $mform->get_data()) {
$factory = cache_factory::instance();
$config = $factory->create_config_instance(true);
$name = $data->name;
$data = $this->get_lock_configuration_from_data($lock, $data);
$config->add_lock_instance($name, $lock, $data);
redirect($PAGE->url, get_string('addlocksuccess', 'cache', $name), 5);
}
return array('form' => $mform);
}
/**
* Performs the delete lock action.
*
* @param string $action the action calling this function.
* @return void
*/
public function action_deletelock(string $action) {
global $OUTPUT, $PAGE, $SITE;
$notifysuccess = true;
$locks = $this->get_lock_summaries();
$lock = required_param('lock', PARAM_ALPHANUMEXT);
$confirm = optional_param('confirm', false, PARAM_BOOL);
if (!array_key_exists($lock, $locks)) {
$notifysuccess = false;
$notifications[] = array(get_string('invalidlock', 'cache'), false);
} else if ($locks[$lock]['uses'] > 0) {
$notifysuccess = false;
$notifications[] = array(get_string('deletelockhasuses', 'cache'), false);
}
if ($notifysuccess) {
if (!$confirm) {
$title = get_string('confirmlockdeletion', 'cache');
$params = array('lock' => $lock, 'confirm' => 1, 'action' => $action, 'sesskey' => sesskey());
$url = new \moodle_url($PAGE->url, $params);
$button = new \single_button($url, get_string('deletelock', 'cache'));
$PAGE->set_title($title);
$PAGE->set_heading($SITE->fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($title);
$confirmation = get_string('deletelockconfirmation', 'cache', $lock);
echo $OUTPUT->confirm($confirmation, $button, $PAGE->url);
echo $OUTPUT->footer();
exit;
} else {
$writer = cache_config_writer::instance();
$writer->delete_lock_instance($lock);
redirect($PAGE->url, get_string('deletelocksuccess', 'cache'), 5);
}
}
}
/**
* Outputs the main admin page by generating it through the renderer.
*
* @param \core_cache\output\renderer $renderer the renderer to use to generate the page.
* @return string the HTML for the admin page.
*/
public function generate_admin_page(\core_cache\output\renderer $renderer): string {
$context = \context_system::instance();
$html = '';
$storepluginsummaries = $this->get_store_plugin_summaries();
$storeinstancesummaries = $this->get_store_instance_summaries();
$definitionsummaries = $this->get_definition_summaries();
$defaultmodestores = $this->get_default_mode_stores();
$locks = $this->get_lock_summaries();
$html .= $renderer->store_plugin_summaries($storepluginsummaries);
$html .= $renderer->store_instance_summariers($storeinstancesummaries, $storepluginsummaries);
$html .= $renderer->definition_summaries($definitionsummaries, $context);
$html .= $renderer->lock_summaries($locks);
$html .= $renderer->additional_lock_actions();
$applicationstore = join(', ', $defaultmodestores[cache_store::MODE_APPLICATION]);
$sessionstore = join(', ', $defaultmodestores[cache_store::MODE_SESSION]);
$requeststore = join(', ', $defaultmodestores[cache_store::MODE_REQUEST]);
$editurl = new \moodle_url('/cache/admin.php', array('action' => 'editmodemappings', 'sesskey' => sesskey()));
$html .= $renderer->mode_mappings($applicationstore, $sessionstore, $requeststore, $editurl);
return $html;
}
/**
* Gets usage information about the whole cache system.
*
* This is a slow function and should only be used on an admin information page.
*
* The returned array lists all cache definitions with fields 'cacheid' and 'stores'. For
* each store, the following fields are available:
*
* - name (store name)
* - class (e.g. cachestore_redis)
* - supported (true if we have any information)
* - items (number of items stored)
* - mean (mean size of item)
* - sd (standard deviation for item sizes)
* - margin (margin of error for mean at 95% confidence)
* - storetotal (total usage for store if known, otherwise null)
*
* The storetotal field will be the same for every cache that uses the same store.
*
* @param int $samplekeys Number of keys to sample when checking size of large caches
* @return array Details of cache usage
*/
public function get_usage(int $samplekeys): array {
$results = [];
$factory = cache_factory::instance();
// Check the caches we already have an instance of, so we don't make another one...
$got = $factory->get_caches_in_use();
$gotid = [];
foreach ($got as $longid => $unused) {
// The IDs here can be of the form cacheid/morestuff if there are parameters in the
// cache. Any entry for a cacheid is good enough to consider that we don't need to make
// another entry ourselves, so we remove the extra bits and track the basic cache id.
$gotid[preg_replace('~^([^/]+/[^/]+)/.*$~', '$1', $longid)] = true;
}
$storetotals = [];
$config = $factory->create_config_instance();
foreach ($config->get_definitions() as $configdetails) {
if (!array_key_exists($configdetails['component'] . '/' . $configdetails['area'], $gotid)) {
// Where possible (if it doesn't need identifiers), make an instance of the cache, otherwise
// we can't get the store instances for it (and it won't show up in the list).
if (empty($configdetails['requireidentifiers'])) {
\cache::make($configdetails['component'], $configdetails['area']);
}
}
$definition = $factory->create_definition($configdetails['component'], $configdetails['area']);
$stores = $factory->get_store_instances_in_use($definition);
// Create object for results about this cache definition.
$currentresult = (object)['cacheid' => $definition->get_id(), 'stores' => []];
$results[$currentresult->cacheid] = $currentresult;
/** @var cache_store $store */
foreach ($stores as $store) {
// Skip static cache.
if ($store instanceof \cachestore_static) {
continue;
}
// Get cache size details from store.
$currentstore = $store->cache_size_details($samplekeys);
// Add in basic information about store.
$currentstore->name = $store->my_name();
$currentstore->class = get_class($store);
// Add in store total.
if (!array_key_exists($currentstore->name, $storetotals)) {
$storetotals[$currentstore->name] = $store->store_total_size();
}
$currentstore->storetotal = $storetotals[$currentstore->name];
$currentresult->stores[] = $currentstore;
}
}
ksort($results);
return $results;
}
}