Merge branch 'MDL-65976-master' of git://github.com/jleyva/moodle into master
[moodle.git] / cache / classes / local / administration_display_helper.php
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/>.
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  */
31 namespace core_cache\local;
33 defined('MOODLE_INTERNAL') || die();
34 use cache_store, cache_factory, cache_config_writer, cache_helper, core_cache_renderer;
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 {
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     }
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'], 'sesskey' => sesskey())),
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'], 'sesskey' => sesskey())),
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     }
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, 'sesskey' => sesskey()));
104             if (empty($storedetails['default'])) {
105                 $actions[] = $OUTPUT->action_link(
106                     new \moodle_url($baseurl, array('action' => 'editstore', 'plugin' => $storedetails['plugin'])),
107                     get_string('editstore', 'cache')
108                 );
110                 $actions[] = $OUTPUT->action_link(
111                     new \moodle_url($baseurl, array('action' => 'deletestore')),
112                     get_string('deletestore', 'cache')
113                 );
114             }
116             $actions[] = $OUTPUT->action_link(
117                 new \moodle_url($baseurl, array('action' => 'purgestore')),
118                 get_string('purge', 'cache')
119             );
120         }
121         return $actions;
122     }
124     /**
125      * Returns all of the actions that can be performed on a plugin.
126      *
127      * @param string $name The name of the plugin
128      * @param array $plugindetails information about this store, from the array returned by
129      *      core_cache\administration_helper::get_store_plugin_summaries().
130      * @return array of actions. Each action is an action_url.
131      */
132     public function get_store_plugin_actions(string $name, array $plugindetails): array {
133         global $OUTPUT;
134         $actions = array();
135         if (has_capability('moodle/site:config', \context_system::instance())) {
136             if (!empty($plugindetails['canaddinstance'])) {
137                 $url = new \moodle_url('/cache/admin.php',
138                     array('action' => 'addstore', 'plugin' => $name, 'sesskey' => sesskey()));
139                 $actions[] = $OUTPUT->action_link(
140                     $url,
141                     get_string('addinstance', 'cache')
142                 );
143             }
144         }
145         return $actions;
146     }
148     /**
149      * Returns a form that can be used to add a store instance.
150      *
151      * @param string $plugin The plugin to add an instance of
152      * @return cachestore_addinstance_form
153      * @throws coding_exception
154      */
155     public function get_add_store_form(string $plugin): \cachestore_addinstance_form {
156         global $CFG; // Needed for includes.
157         $plugins = \core_component::get_plugin_list('cachestore');
158         if (!array_key_exists($plugin, $plugins)) {
159             throw new \coding_exception('Invalid cache plugin used when trying to create an edit form.');
160         }
161         $plugindir = $plugins[$plugin];
162         $class = 'cachestore_addinstance_form';
163         if (file_exists($plugindir.'/addinstanceform.php')) {
164             require_once($plugindir.'/addinstanceform.php');
165             if (class_exists('cachestore_'.$plugin.'_addinstance_form')) {
166                 $class = 'cachestore_'.$plugin.'_addinstance_form';
167                 if (!array_key_exists('cachestore_addinstance_form', class_parents($class))) {
168                     throw new \coding_exception('Cache plugin add instance forms must extend cachestore_addinstance_form');
169                 }
170             }
171         }
173         $locks = $this->get_possible_locks_for_stores($plugindir, $plugin);
175         $url = new \moodle_url('/cache/admin.php', array('action' => 'addstore'));
176         return new $class($url, array('plugin' => $plugin, 'store' => null, 'locks' => $locks));
177     }
179     /**
180      * Returns a form that can be used to edit a store instance.
181      *
182      * @param string $plugin
183      * @param string $store
184      * @return cachestore_addinstance_form
185      * @throws coding_exception
186      */
187     public function get_edit_store_form(string $plugin, string $store): \cachestore_addinstance_form {
188         global $CFG; // Needed for includes.
189         $plugins = \core_component::get_plugin_list('cachestore');
190         if (!array_key_exists($plugin, $plugins)) {
191             throw new \coding_exception('Invalid cache plugin used when trying to create an edit form.');
192         }
193         $factory = \cache_factory::instance();
194         $config = $factory->create_config_instance();
195         $stores = $config->get_all_stores();
196         if (!array_key_exists($store, $stores)) {
197             throw new \coding_exception('Invalid store name given when trying to create an edit form.');
198         }
199         $plugindir = $plugins[$plugin];
200         $class = 'cachestore_addinstance_form';
201         if (file_exists($plugindir.'/addinstanceform.php')) {
202             require_once($plugindir.'/addinstanceform.php');
203             if (class_exists('cachestore_'.$plugin.'_addinstance_form')) {
204                 $class = 'cachestore_'.$plugin.'_addinstance_form';
205                 if (!array_key_exists('cachestore_addinstance_form', class_parents($class))) {
206                     throw new \coding_exception('Cache plugin add instance forms must extend cachestore_addinstance_form');
207                 }
208             }
209         }
211         $locks = $this->get_possible_locks_for_stores($plugindir, $plugin);
213         $url = new \moodle_url('/cache/admin.php', array('action' => 'editstore', 'plugin' => $plugin, 'store' => $store));
214         $editform = new $class($url, array('plugin' => $plugin, 'store' => $store, 'locks' => $locks));
215         if (isset($stores[$store]['lock'])) {
216             $editform->set_data(array('lock' => $stores[$store]['lock']));
217         }
218         // See if the cachestore is going to want to load data for the form.
219         // If it has a customised add instance form then it is going to want to.
220         $storeclass = 'cachestore_'.$plugin;
221         $storedata = $stores[$store];
222         if (array_key_exists('configuration', $storedata) &&
223             array_key_exists('cache_is_configurable', class_implements($storeclass))) {
224             $storeclass::config_set_edit_form_data($editform, $storedata['configuration']);
225         }
226         return $editform;
227     }
229     /**
230      * Returns an array of suitable lock instances for use with this plugin, or false if the plugin handles locking itself.
231      *
232      * @param string $plugindir
233      * @param string $plugin
234      * @return array|false
235      */
236     protected function get_possible_locks_for_stores(string $plugindir, string $plugin) {
237         global $CFG; // Needed for includes.
238         $supportsnativelocking = false;
239         if (file_exists($plugindir.'/lib.php')) {
240             require_once($plugindir.'/lib.php');
241             $pluginclass = 'cachestore_'.$plugin;
242             if (class_exists($pluginclass)) {
243                 $supportsnativelocking = array_key_exists('cache_is_lockable', class_implements($pluginclass));
244             }
245         }
247         if (!$supportsnativelocking) {
248             $config = \cache_config::instance();
249             $locks = array();
250             foreach ($config->get_locks() as $lock => $conf) {
251                 if (!empty($conf['default'])) {
252                     $name = get_string($lock, 'cache');
253                 } else {
254                     $name = $lock;
255                 }
256                 $locks[$lock] = $name;
257             }
258         } else {
259             $locks = false;
260         }
262         return $locks;
263     }
265     /**
266      * Processes the results of the add/edit instance form data for a plugin returning an array of config information suitable to
267      * store in configuration.
268      *
269      * @param stdClass $data The mform data.
270      * @return array
271      * @throws coding_exception
272      */
273     public function get_store_configuration_from_data(\stdClass $data): array {
274         global $CFG;
275         $file = $CFG->dirroot.'/cache/stores/'.$data->plugin.'/lib.php';
276         if (!file_exists($file)) {
277             throw new \coding_exception('Invalid cache plugin provided. '.$file);
278         }
279         require_once($file);
280         $class = 'cachestore_'.$data->plugin;
281         if (!class_exists($class)) {
282             throw new \coding_exception('Invalid cache plugin provided.');
283         }
284         if (array_key_exists('cache_is_configurable', class_implements($class))) {
285             return $class::config_get_configuration_array($data);
286         }
287         return array();
288     }
290     /**
291      * Returns an array of lock plugins for which we can add an instance.
292      *
293      * Suitable for use within an mform select element.
294      *
295      * @return array
296      */
297     public function get_addable_lock_options(): array {
298         $plugins = \core_component::get_plugin_list_with_class('cachelock', '', 'lib.php');
299         $options = array();
300         $len = strlen('cachelock_');
301         foreach ($plugins as $plugin => $class) {
302             $method = "$class::can_add_instance";
303             if (is_callable($method) && !call_user_func($method)) {
304                 // Can't add an instance of this plugin.
305                 continue;
306             }
307             $options[substr($plugin, $len)] = get_string('pluginname', $plugin);
308         }
309         return $options;
310     }
312     /**
313      * Gets the form to use when adding a lock instance.
314      *
315      * @param string $plugin
316      * @param array $lockplugin
317      * @return cache_lock_form
318      * @throws coding_exception
319      */
320     public function get_add_lock_form(string $plugin, array $lockplugin = null): \cache_lock_form {
321         global $CFG; // Needed for includes.
322         $plugins = \core_component::get_plugin_list('cachelock');
323         if (!array_key_exists($plugin, $plugins)) {
324             throw new \coding_exception('Invalid cache lock plugin requested when trying to create a form.');
325         }
326         $plugindir = $plugins[$plugin];
327         $class = 'cache_lock_form';
328         if (file_exists($plugindir.'/addinstanceform.php') && in_array('cache_is_configurable', class_implements($class))) {
329             require_once($plugindir.'/addinstanceform.php');
330             if (class_exists('cachelock_'.$plugin.'_addinstance_form')) {
331                 $class = 'cachelock_'.$plugin.'_addinstance_form';
332                 if (!array_key_exists('cache_lock_form', class_parents($class))) {
333                     throw new \coding_exception('Cache lock plugin add instance forms must extend cache_lock_form');
334                 }
335             }
336         }
337         return new $class(null, array('lock' => $plugin));
338     }
340     /**
341      * Gets configuration data from a new lock instance form.
342      *
343      * @param string $plugin
344      * @param stdClass $data
345      * @return array
346      * @throws coding_exception
347      */
348     public function get_lock_configuration_from_data(string $plugin, \stdClass $data): array {
349         global $CFG;
350         $file = $CFG->dirroot.'/cache/locks/'.$plugin.'/lib.php';
351         if (!file_exists($file)) {
352             throw new \coding_exception('Invalid cache plugin provided. '.$file);
353         }
354         require_once($file);
355         $class = 'cachelock_'.$plugin;
356         if (!class_exists($class)) {
357             throw new \coding_exception('Invalid cache plugin provided.');
358         }
359         if (array_key_exists('cache_is_configurable', class_implements($class))) {
360             return $class::config_get_configuration_array($data);
361         }
362         return array();
363     }
365     /**
366      * Handles the page actions, based on the parameter.
367      *
368      * @param string $action the action to handle.
369      * @param array $forminfo an empty array to be overridden and set.
370      * @return array the empty or overridden forminfo array.
371      */
372     public function perform_cache_actions(string $action, array $forminfo): array {
373         switch ($action) {
374             case 'rescandefinitions' : // Rescan definitions.
375                 $this->action_rescan_definition();
376                 break;
378             case 'addstore' : // Add the requested store.
379                 $forminfo = $this->action_addstore();
380                 break;
382             case 'editstore' : // Edit the requested store.
383                 $forminfo = $this->action_editstore();
384                 break;
386             case 'deletestore' : // Delete a given store.
387                 $this->action_deletestore($action);
388                 break;
390             case 'editdefinitionmapping' : // Edit definition mappings.
391                 $forminfo = $this->action_editdefinitionmapping();
392                 break;
394             case 'editdefinitionsharing' : // Edit definition sharing.
395                 $forminfo = $this->action_editdefinitionsharing();
396                 break;
398             case 'editmodemappings': // Edit default mode mappings.
399                 $forminfo = $this->action_editmodemappings();
400                 break;
402             case 'purgedefinition': // Purge a specific definition.
403                 $this->action_purgedefinition();
404                 break;
406             case 'purgestore':
407             case 'purge': // Purge a store cache.
408                 $this->action_purge();
409                 break;
411             case 'newlockinstance':
412                 $forminfo = $this->action_newlockinstance();
413                 break;
415             case 'deletelock':
416                 // Deletes a lock instance.
417                 $this->action_deletelock($action);
418                 break;
419         }
421         return $forminfo;
422     }
424     /**
425      * Performs the rescan definition action.
426      *
427      * @return void
428      */
429     public function action_rescan_definition() {
430         global $PAGE;
432         \cache_config_writer::update_definitions();
433         redirect($PAGE->url);
434     }
436     /**
437      * Performs the add store action.
438      *
439      * @return array an array of the form to display to the user, and the page title.
440      */
441     public function action_addstore() : array {
442         global $PAGE;
443         $storepluginsummaries = $this->get_store_plugin_summaries();
445         $plugin = required_param('plugin', PARAM_PLUGIN);
446         if (!$storepluginsummaries[$plugin]['canaddinstance']) {
447             print_error('ex_unmetstorerequirements', 'cache');
448         }
449         $mform = $this->get_add_store_form($plugin);
450         $title = get_string('addstore', 'cache', $storepluginsummaries[$plugin]['name']);
451         if ($mform->is_cancelled()) {
452             redirect($PAGE->url);
453         } else if ($data = $mform->get_data()) {
454             $config = $this->get_store_configuration_from_data($data);
455             $writer = \cache_config_writer::instance();
456             unset($config['lock']);
457             foreach ($writer->get_locks() as $lock => $lockconfig) {
458                 if ($lock == $data->lock) {
459                     $config['lock'] = $data->lock;
460                 }
461             }
462             $writer->add_store_instance($data->name, $data->plugin, $config);
463             redirect($PAGE->url, get_string('addstoresuccess', 'cache', $storepluginsummaries[$plugin]['name']), 5);
464         }
466         return array('form' => $mform, 'title' => $title);
467     }
469     /**
470      * Performs the edit store action.
471      *
472      * @return array an array of the form to display, and the page title.
473      */
474     public function action_editstore(): array {
475         global $PAGE;
476         $storepluginsummaries = $this->get_store_plugin_summaries();
478         $plugin = required_param('plugin', PARAM_PLUGIN);
479         $store = required_param('store', PARAM_TEXT);
480         $mform = $this->get_edit_store_form($plugin, $store);
481         $title = get_string('addstore', 'cache', $storepluginsummaries[$plugin]['name']);
482         if ($mform->is_cancelled()) {
483             redirect($PAGE->url);
484         } else if ($data = $mform->get_data()) {
485             $config = $this->get_store_configuration_from_data($data);
486             $writer = \cache_config_writer::instance();
488             unset($config['lock']);
489             foreach ($writer->get_locks() as $lock => $lockconfig) {
490                 if ($lock == $data->lock) {
491                     $config['lock'] = $data->lock;
492                 }
493             }
494             $writer->edit_store_instance($data->name, $data->plugin, $config);
495             redirect($PAGE->url, get_string('editstoresuccess', 'cache', $storepluginsummaries[$plugin]['name']), 5);
496         }
498         return array('form' => $mform, 'title' => $title);
499     }
501     /**
502      * Performs the deletestore action.
503      *
504      * @param string $action the action calling to this function.
505      * @return void
506      */
507     public function action_deletestore(string $action) {
508         global $OUTPUT, $PAGE, $SITE;
509         $notifysuccess = true;
510         $storeinstancesummaries = $this->get_store_instance_summaries();
512         $store = required_param('store', PARAM_TEXT);
513         $confirm = optional_param('confirm', false, PARAM_BOOL);
515         if (!array_key_exists($store, $storeinstancesummaries)) {
516             $notifysuccess = false;
517             $notifications[] = array(get_string('invalidstore', 'cache'), false);
518         } else if ($storeinstancesummaries[$store]['mappings'] > 0) {
519             $notifysuccess = false;
520             $notifications[] = array(get_string('deletestorehasmappings', 'cache'), false);
521         }
523         if ($notifysuccess) {
524             if (!$confirm) {
525                 $title = get_string('confirmstoredeletion', 'cache');
526                 $params = array('store' => $store, 'confirm' => 1, 'action' => $action, 'sesskey' => sesskey());
527                 $url = new \moodle_url($PAGE->url, $params);
528                 $button = new \single_button($url, get_string('deletestore', 'cache'));
530                 $PAGE->set_title($title);
531                 $PAGE->set_heading($SITE->fullname);
532                 echo $OUTPUT->header();
533                 echo $OUTPUT->heading($title);
534                 $confirmation = get_string('deletestoreconfirmation', 'cache', $storeinstancesummaries[$store]['name']);
535                 echo $OUTPUT->confirm($confirmation, $button, $PAGE->url);
536                 echo $OUTPUT->footer();
537                 exit;
538             } else {
539                 $writer = \cache_config_writer::instance();
540                 $writer->delete_store_instance($store);
541                 redirect($PAGE->url, get_string('deletestoresuccess', 'cache'), 5);
542             }
543         }
544     }
546     /**
547      * Performs the edit definition mapping action.
548      *
549      * @return array an array of the form to display, and the page title.
550      * @throws cache_exception
551      */
552     public function action_editdefinitionmapping(): array {
553         global $PAGE;
554         $definitionsummaries = $this->get_definition_summaries();
556         $definition = required_param('definition', PARAM_SAFEPATH);
557         if (!array_key_exists($definition, $definitionsummaries)) {
558             throw new \cache_exception('Invalid cache definition requested');
559         }
560         $title = get_string('editdefinitionmappings', 'cache', $definition);
561         $mform = new \cache_definition_mappings_form($PAGE->url, array('definition' => $definition));
562         if ($mform->is_cancelled()) {
563             redirect($PAGE->url);
564         } else if ($data = $mform->get_data()) {
565             $writer = \cache_config_writer::instance();
566             $mappings = array();
567             foreach ($data->mappings as $mapping) {
568                 if (!empty($mapping)) {
569                     $mappings[] = $mapping;
570                 }
571             }
572             $writer->set_definition_mappings($definition, $mappings);
573             redirect($PAGE->url);
574         }
576         return array('form' => $mform, 'title' => $title);
577     }
579     /**
580      * Performs the edit definition sharing action.
581      *
582      * @return array an array of the edit definition sharing form, and the page title.
583      */
584     public function action_editdefinitionsharing(): array {
585         global $PAGE;
586         $definitionsummaries = $this->get_definition_summaries();
588         $definition = required_param('definition', PARAM_SAFEPATH);
589         if (!array_key_exists($definition, $definitionsummaries)) {
590             throw new \cache_exception('Invalid cache definition requested');
591         }
592         $title = get_string('editdefinitionsharing', 'cache', $definition);
593         $sharingoptions = $definitionsummaries[$definition]['sharingoptions'];
594         $customdata = array('definition' => $definition, 'sharingoptions' => $sharingoptions);
595         $mform = new \cache_definition_sharing_form($PAGE->url, $customdata);
596         $mform->set_data(array(
597             'sharing' => $definitionsummaries[$definition]['selectedsharingoption'],
598             'userinputsharingkey' => $definitionsummaries[$definition]['userinputsharingkey']
599         ));
600         if ($mform->is_cancelled()) {
601             redirect($PAGE->url);
602         } else if ($data = $mform->get_data()) {
603             $component = $definitionsummaries[$definition]['component'];
604             $area = $definitionsummaries[$definition]['area'];
605             // Purge the stores removing stale data before we alter the sharing option.
606             \cache_helper::purge_stores_used_by_definition($component, $area);
607             $writer = \cache_config_writer::instance();
608             $sharing = array_sum(array_keys($data->sharing));
609             $userinputsharingkey = $data->userinputsharingkey;
610             $writer->set_definition_sharing($definition, $sharing, $userinputsharingkey);
611             redirect($PAGE->url);
612         }
614         return array('form' => $mform, 'title' => $title);
615     }
617     /**
618      * Performs the edit mode mappings action.
619      *
620      * @return array an array of the edit mode mappings form.
621      */
622     public function action_editmodemappings(): array {
623         global $PAGE;
624         $storeinstancesummaries = $this->get_store_instance_summaries();
625         $defaultmodestores = $this->get_default_mode_stores();
627         $mform = new \cache_mode_mappings_form(null, $storeinstancesummaries);
628         $mform->set_data(array(
629             'mode_'.cache_store::MODE_APPLICATION => key($defaultmodestores[cache_store::MODE_APPLICATION]),
630             'mode_'.cache_store::MODE_SESSION => key($defaultmodestores[cache_store::MODE_SESSION]),
631             'mode_'.cache_store::MODE_REQUEST => key($defaultmodestores[cache_store::MODE_REQUEST]),
632         ));
633         if ($mform->is_cancelled()) {
634             redirect($PAGE->url);
635         } else if ($data = $mform->get_data()) {
636             $mappings = array(
637                 cache_store::MODE_APPLICATION => array($data->{'mode_'.cache_store::MODE_APPLICATION}),
638                 cache_store::MODE_SESSION => array($data->{'mode_'.cache_store::MODE_SESSION}),
639                 cache_store::MODE_REQUEST => array($data->{'mode_'.cache_store::MODE_REQUEST}),
640             );
641             $writer = cache_config_writer::instance();
642             $writer->set_mode_mappings($mappings);
643             redirect($PAGE->url);
644         }
646         return array('form' => $mform);
647     }
649     /**
650      * Performs the purge definition action.
651      *
652      * @return void
653      */
654     public function action_purgedefinition() {
655         global $PAGE;
657         $id = required_param('definition', PARAM_SAFEPATH);
658         list($component, $area) = explode('/', $id, 2);
659         $factory = cache_factory::instance();
660         $definition = $factory->create_definition($component, $area);
661         if ($definition->has_required_identifiers()) {
662             // We will have to purge the stores used by this definition.
663             cache_helper::purge_stores_used_by_definition($component, $area);
664         } else {
665             // Alrighty we can purge just the data belonging to this definition.
666             cache_helper::purge_by_definition($component, $area);
667         }
669         $message = get_string('purgexdefinitionsuccess', 'cache', [
670                     'name' => $definition->get_name(),
671                     'component' => $component,
672                     'area' => $area,
673                 ]);
674         $purgeagainlink = \html_writer::link(new \moodle_url('/cache/admin.php', [
675                 'action' => 'purgedefinition', 'sesskey' => sesskey(), 'definition' => $id]),
676                 get_string('purgeagain', 'cache'));
677         redirect($PAGE->url, $message . ' ' . $purgeagainlink, 5);
678     }
680     /**
681      * Performs the purge action.
682      *
683      * @return void
684      */
685     public function action_purge() {
686         global $PAGE;
688         $store = required_param('store', PARAM_TEXT);
689         cache_helper::purge_store($store);
690         $message = get_string('purgexstoresuccess', 'cache', ['store' => $store]);
691         $purgeagainlink = \html_writer::link(new \moodle_url('/cache/admin.php', [
692                 'action' => 'purgestore', 'sesskey' => sesskey(), 'store' => $store]),
693                 get_string('purgeagain', 'cache'));
694         redirect($PAGE->url, $message . ' ' . $purgeagainlink, 5);
695     }
697     /**
698      * Performs the new lock instance action.
699      *
700      * @return array An array containing the new lock instance form.
701      */
702     public function action_newlockinstance(): array {
703         global $PAGE;
705         // Adds a new lock instance.
706         $lock = required_param('lock', PARAM_ALPHANUMEXT);
707         $mform = $this->get_add_lock_form($lock);
708         if ($mform->is_cancelled()) {
709             redirect($PAGE->url);
710         } else if ($data = $mform->get_data()) {
711             $factory = cache_factory::instance();
712             $config = $factory->create_config_instance(true);
713             $name = $data->name;
714             $data = $this->get_lock_configuration_from_data($lock, $data);
715             $config->add_lock_instance($name, $lock, $data);
716             redirect($PAGE->url, get_string('addlocksuccess', 'cache', $name), 5);
717         }
719         return array('form' => $mform);
720     }
722     /**
723      * Performs the delete lock action.
724      *
725      * @param string $action the action calling this function.
726      * @return void
727      */
728     public function action_deletelock(string $action) {
729         global $OUTPUT, $PAGE, $SITE;
730         $notifysuccess = true;
731         $locks = $this->get_lock_summaries();
733         $lock = required_param('lock', PARAM_ALPHANUMEXT);
734         $confirm = optional_param('confirm', false, PARAM_BOOL);
735         if (!array_key_exists($lock, $locks)) {
736             $notifysuccess = false;
737             $notifications[] = array(get_string('invalidlock', 'cache'), false);
738         } else if ($locks[$lock]['uses'] > 0) {
739             $notifysuccess = false;
740             $notifications[] = array(get_string('deletelockhasuses', 'cache'), false);
741         }
742         if ($notifysuccess) {
743             if (!$confirm) {
744                 $title = get_string('confirmlockdeletion', 'cache');
745                 $params = array('lock' => $lock, 'confirm' => 1, 'action' => $action, 'sesskey' => sesskey());
746                 $url = new \moodle_url($PAGE->url, $params);
747                 $button = new \single_button($url, get_string('deletelock', 'cache'));
749                 $PAGE->set_title($title);
750                 $PAGE->set_heading($SITE->fullname);
751                 echo $OUTPUT->header();
752                 echo $OUTPUT->heading($title);
753                 $confirmation = get_string('deletelockconfirmation', 'cache', $lock);
754                 echo $OUTPUT->confirm($confirmation, $button, $PAGE->url);
755                 echo $OUTPUT->footer();
756                 exit;
757             } else {
758                 $writer = cache_config_writer::instance();
759                 $writer->delete_lock_instance($lock);
760                 redirect($PAGE->url, get_string('deletelocksuccess', 'cache'), 5);
761             }
762         }
763     }
765     /**
766      * Outputs the main admin page by generating it through the renderer.
767      *
768      * @param core_cache_renderer $renderer the renderer to use to generate the page.
769      * @return string the HTML for the admin page.
770      */
771     public function generate_admin_page(core_cache_renderer $renderer): string {
772         $context = \context_system::instance();
773         $html = '';
775         $storepluginsummaries = $this->get_store_plugin_summaries();
776         $storeinstancesummaries = $this->get_store_instance_summaries();
777         $definitionsummaries = $this->get_definition_summaries();
778         $defaultmodestores = $this->get_default_mode_stores();
779         $locks = $this->get_lock_summaries();
781         $html .= $renderer->store_plugin_summaries($storepluginsummaries);
782         $html .= $renderer->store_instance_summariers($storeinstancesummaries, $storepluginsummaries);
783         $html .= $renderer->definition_summaries($definitionsummaries, $context);
784         $html .= $renderer->lock_summaries($locks);
785         $html .= $renderer->additional_lock_actions();
787         $applicationstore = join(', ', $defaultmodestores[cache_store::MODE_APPLICATION]);
788         $sessionstore = join(', ', $defaultmodestores[cache_store::MODE_SESSION]);
789         $requeststore = join(', ', $defaultmodestores[cache_store::MODE_REQUEST]);
790         $editurl = new \moodle_url('/cache/admin.php', array('action' => 'editmodemappings', 'sesskey' => sesskey()));
791         $html .= $renderer->mode_mappings($applicationstore, $sessionstore, $requeststore, $editurl);
793         return $html;
794     }