diff --git a/cache/admin.php b/cache/admin.php
index f8931b60601..86b730e0c7e 100644
--- a/cache/admin.php
+++ b/cache/admin.php
@@ -42,263 +42,27 @@ if (empty($SESSION->cacheadminreparsedefinitions)) {
 $action = optional_param('action', null, PARAM_ALPHA);
 
 admin_externalpage_setup('cacheconfig');
-$context = context_system::instance();
+$adminhelper = cache_factory::instance()->get_administration_display_helper();
 
-$storeinstancesummaries = cache_administration_helper::get_store_instance_summaries();
-$storepluginsummaries = cache_administration_helper::get_store_plugin_summaries();
-$definitionsummaries = cache_administration_helper::get_definition_summaries();
-$defaultmodestores = cache_administration_helper::get_default_mode_stores();
-$locks = cache_administration_helper::get_lock_summaries();
-
-$title = new lang_string('cacheadmin', 'cache');
-$mform = null;
 $notifications = array();
-$notifysuccess = true;
+// Empty array to hold any form information returned from actions.
+$forminfo = [];
 
+// Handle page actions in admin helper class.
 if (!empty($action) && confirm_sesskey()) {
-    switch ($action) {
-        case 'rescandefinitions' : // Rescan definitions.
-            cache_config_writer::update_definitions();
-            redirect($PAGE->url);
-            break;
-        case 'addstore' : // Add the requested store.
-            $plugin = required_param('plugin', PARAM_PLUGIN);
-            if (!$storepluginsummaries[$plugin]['canaddinstance']) {
-                print_error('ex_unmetstorerequirements', 'cache');
-            }
-            $mform = cache_administration_helper::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 = cache_administration_helper::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);
-            }
-            break;
-        case 'editstore' : // Edit the requested store.
-            $plugin = required_param('plugin', PARAM_PLUGIN);
-            $store = required_param('store', PARAM_TEXT);
-            $mform = cache_administration_helper::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 = cache_administration_helper::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);
-            }
-            break;
-        case 'deletestore' : // Delete a given store.
-            $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);
-                }
-            }
-            break;
-        case 'editdefinitionmapping' : // Edit definition mappings.
-            $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);
-            }
-            break;
-        case 'editdefinitionsharing' :
-            $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);
-            }
-            break;
-        case 'editmodemappings': // Edit default mode mappings.
-            $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);
-            }
-            break;
-
-        case 'purgedefinition': // Purge a specific definition.
-            $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);
-            break;
-
-        case 'purgestore':
-        case 'purge': // Purge a store cache.
-            $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);
-            break;
-
-        case 'newlockinstance':
-            // Adds a new lock instance.
-            $lock = required_param('lock', PARAM_ALPHANUMEXT);
-            $mform = cache_administration_helper::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 = cache_administration_helper::get_lock_configuration_from_data($lock, $data);
-                $config->add_lock_instance($name, $lock, $data);
-                redirect($PAGE->url, get_string('addlocksuccess', 'cache', $name), 5);
-            }
-            break;
-        case 'deletelock':
-            // Deletes a lock instance.
-            $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);
-                }
-            }
-            break;
-    }
+    $forminfo = $adminhelper->perform_cache_actions($action, $forminfo);
 }
 
 // Add cache store warnings to the list of notifications.
 // Obviously as these are warnings they are show as failures.
-foreach (cache_helper::warnings($storeinstancesummaries) as $warning) {
+foreach (cache_helper::warnings(core_cache\administration_helper::get_store_instance_summaries()) as $warning) {
     $notifications[] = array($warning, false);
 }
 
+// Decide on display mode based on returned forminfo.
+$mform = array_key_exists('form', $forminfo) ? $forminfo['form'] : null;
+$title = array_key_exists('title', $forminfo) ? $forminfo['title'] : new lang_string('cacheadmin', 'cache');
+
 $PAGE->set_title($title);
 $PAGE->set_heading($SITE->fullname);
 /* @var core_cache_renderer $renderer */
@@ -311,16 +75,8 @@ echo $renderer->notifications($notifications);
 if ($mform instanceof moodleform) {
     $mform->display();
 } else {
-    echo $renderer->store_plugin_summaries($storepluginsummaries);
-    echo $renderer->store_instance_summariers($storeinstancesummaries, $storepluginsummaries);
-    echo $renderer->definition_summaries($definitionsummaries, $context);
-    echo $renderer->lock_summaries($locks);
-
-    $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()));
-    echo $renderer->mode_mappings($applicationstore, $sessionstore, $requeststore, $editurl);
+    // Handle main page definition in admin helper class.
+    echo $adminhelper->generate_admin_page($renderer);
 }
 
 echo $renderer->footer();
diff --git a/cache/classes/administration_helper.php b/cache/classes/administration_helper.php
new file mode 100644
index 00000000000..551e62c1695
--- /dev/null
+++ b/cache/classes/administration_helper.php
@@ -0,0 +1,389 @@
+<?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 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;
+
+defined('MOODLE_INTERNAL') || die();
+use cache_helper, cache_store, cache_config, cache_factory, cache_definition;
+
+/**
+ * Administration helper base class.
+ *
+ * Defines abstract methods for a subclass to define the admin page.
+ *
+ * @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
+ */
+abstract class administration_helper extends cache_helper {
+
+    /**
+     * Returns an array containing all of the information about stores a renderer needs.
+     * @return array
+     */
+    public static function get_store_instance_summaries(): array {
+        $return = array();
+        $default = array();
+        $instance = \cache_config::instance();
+        $stores = $instance->get_all_stores();
+        $locks = $instance->get_locks();
+        foreach ($stores as $name => $details) {
+            $class = $details['class'];
+            $store = false;
+            if ($class::are_requirements_met()) {
+                $store = new $class($details['name'], $details['configuration']);
+            }
+            $lock = (isset($details['lock'])) ? $locks[$details['lock']] : $instance->get_default_lock();
+            $record = array(
+                'name' => $name,
+                'plugin' => $details['plugin'],
+                'default' => $details['default'],
+                'isready' => $store ? $store->is_ready() : false,
+                'requirementsmet' => $class::are_requirements_met(),
+                'mappings' => 0,
+                'lock' => $lock,
+                'modes' => array(
+                    cache_store::MODE_APPLICATION =>
+                        ($class::get_supported_modes($return) & cache_store::MODE_APPLICATION) == cache_store::MODE_APPLICATION,
+                    cache_store::MODE_SESSION =>
+                        ($class::get_supported_modes($return) & cache_store::MODE_SESSION) == cache_store::MODE_SESSION,
+                    cache_store::MODE_REQUEST =>
+                        ($class::get_supported_modes($return) & cache_store::MODE_REQUEST) == cache_store::MODE_REQUEST,
+                ),
+                'supports' => array(
+                    'multipleidentifiers' => $store ? $store->supports_multiple_identifiers() : false,
+                    'dataguarantee' => $store ? $store->supports_data_guarantee() : false,
+                    'nativettl' => $store ? $store->supports_native_ttl() : false,
+                    'nativelocking' => ($store instanceof \cache_is_lockable),
+                    'keyawareness' => ($store instanceof \cache_is_key_aware),
+                    'searchable' => ($store instanceof \cache_is_searchable)
+                ),
+                'warnings' => $store ? $store->get_warnings() : array()
+            );
+            if (empty($details['default'])) {
+                $return[$name] = $record;
+            } else {
+                $default[$name] = $record;
+            }
+        }
+
+        ksort($return);
+        ksort($default);
+        $return = $return + $default;
+
+        foreach ($instance->get_definition_mappings() as $mapping) {
+            if (!array_key_exists($mapping['store'], $return)) {
+                continue;
+            }
+            $return[$mapping['store']]['mappings']++;
+        }
+
+        return $return;
+    }
+
+    /**
+     * Returns an array of information about plugins, everything a renderer needs.
+     *
+     * @return array for each store, an array containing various information about each store.
+     *     See the code below for details
+     */
+    public static function get_store_plugin_summaries(): array {
+        $return = array();
+        $plugins = \core_component::get_plugin_list_with_file('cachestore', 'lib.php', true);
+        foreach ($plugins as $plugin => $path) {
+            $class = 'cachestore_'.$plugin;
+            $return[$plugin] = array(
+                'name' => get_string('pluginname', 'cachestore_'.$plugin),
+                'requirementsmet' => $class::are_requirements_met(),
+                'instances' => 0,
+                'modes' => array(
+                    cache_store::MODE_APPLICATION => ($class::get_supported_modes() & cache_store::MODE_APPLICATION),
+                    cache_store::MODE_SESSION => ($class::get_supported_modes() & cache_store::MODE_SESSION),
+                    cache_store::MODE_REQUEST => ($class::get_supported_modes() & cache_store::MODE_REQUEST),
+                ),
+                'supports' => array(
+                    'multipleidentifiers' => ($class::get_supported_features() & cache_store::SUPPORTS_MULTIPLE_IDENTIFIERS),
+                    'dataguarantee' => ($class::get_supported_features() & cache_store::SUPPORTS_DATA_GUARANTEE),
+                    'nativettl' => ($class::get_supported_features() & cache_store::SUPPORTS_NATIVE_TTL),
+                    'nativelocking' => (in_array('cache_is_lockable', class_implements($class))),
+                    'keyawareness' => (array_key_exists('cache_is_key_aware', class_implements($class))),
+                ),
+                'canaddinstance' => ($class::can_add_instance() && $class::are_requirements_met())
+            );
+        }
+
+        $instance = cache_config::instance();
+        $stores = $instance->get_all_stores();
+        foreach ($stores as $store) {
+            $plugin = $store['plugin'];
+            if (array_key_exists($plugin, $return)) {
+                $return[$plugin]['instances']++;
+            }
+        }
+
+        return $return;
+    }
+
+    /**
+     * Returns an array about the definitions. All the information a renderer needs.
+     *
+     * @return array for each store, an array containing various information about each store.
+     *     See the code below for details
+     */
+    public static function get_definition_summaries(): array {
+        $factory = cache_factory::instance();
+        $config = $factory->create_config_instance();
+        $storenames = array();
+        foreach ($config->get_all_stores() as $key => $store) {
+            if (!empty($store['default'])) {
+                $storenames[$key] = new \lang_string('store_'.$key, 'cache');
+            } else {
+                $storenames[$store['name']] = $store['name'];
+            }
+        }
+        /* @var cache_definition[] $definitions */
+        $definitions = [];
+        $return = [];
+        foreach ($config->get_definitions() as $key => $definition) {
+            $definitions[$key] = cache_definition::load($definition['component'].'/'.$definition['area'], $definition);
+        }
+        foreach ($definitions as $id => $definition) {
+            $mappings = array();
+            foreach (cache_helper::get_stores_suitable_for_definition($definition) as $store) {
+                $mappings[] = $storenames[$store->my_name()];
+            }
+            $return[$id] = array(
+                'id' => $id,
+                'name' => $definition->get_name(),
+                'mode' => $definition->get_mode(),
+                'component' => $definition->get_component(),
+                'area' => $definition->get_area(),
+                'mappings' => $mappings,
+                'canuselocalstore' => $definition->can_use_localstore(),
+                'sharingoptions' => self::get_definition_sharing_options($definition->get_sharing_options(), false),
+                'selectedsharingoption' => self::get_definition_sharing_options($definition->get_selected_sharing_option(), true),
+                'userinputsharingkey' => $definition->get_user_input_sharing_key()
+            );
+        }
+        return $return;
+    }
+
+    /**
+     * Get the default stores for all modes.
+     *
+     * @return array An array containing sub-arrays, one for each mode.
+     */
+    public static function get_default_mode_stores(): array {
+        global $OUTPUT;
+        $instance = cache_config::instance();
+        $adequatestores = cache_helper::get_stores_suitable_for_mode_default();
+        $icon = new \pix_icon('i/warning', new \lang_string('inadequatestoreformapping', 'cache'));
+        $storenames = array();
+        foreach ($instance->get_all_stores() as $key => $store) {
+            if (!empty($store['default'])) {
+                $storenames[$key] = new \lang_string('store_'.$key, 'cache');
+            }
+        }
+        $modemappings = array(
+            cache_store::MODE_APPLICATION => array(),
+            cache_store::MODE_SESSION => array(),
+            cache_store::MODE_REQUEST => array(),
+        );
+        foreach ($instance->get_mode_mappings() as $mapping) {
+            $mode = $mapping['mode'];
+            if (!array_key_exists($mode, $modemappings)) {
+                debugging('Unknown mode in cache store mode mappings', DEBUG_DEVELOPER);
+                continue;
+            }
+            if (array_key_exists($mapping['store'], $storenames)) {
+                $modemappings[$mode][$mapping['store']] = $storenames[$mapping['store']];
+            } else {
+                $modemappings[$mode][$mapping['store']] = $mapping['store'];
+            }
+            if (!array_key_exists($mapping['store'], $adequatestores)) {
+                $modemappings[$mode][$mapping['store']] = $modemappings[$mode][$mapping['store']].' '.$OUTPUT->render($icon);
+            }
+        }
+        return $modemappings;
+    }
+
+    /**
+     * Returns an array summarising the locks available in the system.
+     *
+     * @return array array of lock summaries.
+     */
+    public static function get_lock_summaries(): array {
+        $locks = array();
+        $instance = cache_config::instance();
+        $stores = $instance->get_all_stores();
+        foreach ($instance->get_locks() as $lock) {
+            $default = !empty($lock['default']);
+            if ($default) {
+                $name = new \lang_string($lock['name'], 'cache');
+            } else {
+                $name = $lock['name'];
+            }
+            $uses = 0;
+            foreach ($stores as $store) {
+                if (!empty($store['lock']) && $store['lock'] === $lock['name']) {
+                    $uses++;
+                }
+            }
+            $lockdata = array(
+                'name' => $name,
+                'default' => $default,
+                'uses' => $uses,
+                'type' => get_string('pluginname', $lock['type'])
+            );
+            $locks[$lock['name']] = $lockdata;
+        }
+        return $locks;
+    }
+
+    /**
+     * Given a sharing option hash this function returns an array of strings that can be used to describe it.
+     *
+     * @param int $sharingoption The sharing option hash to get strings for.
+     * @param bool $isselectedoptions Set to true if the strings will be used to view the selected options.
+     * @return array An array of lang_string's.
+     */
+    public static function get_definition_sharing_options(int $sharingoption, bool $isselectedoptions = true): array {
+        $options = array();
+        $prefix = ($isselectedoptions) ? 'sharingselected' : 'sharing';
+        if ($sharingoption & cache_definition::SHARING_ALL) {
+            $options[cache_definition::SHARING_ALL] = new \lang_string($prefix.'_all', 'cache');
+        }
+        if ($sharingoption & cache_definition::SHARING_SITEID) {
+            $options[cache_definition::SHARING_SITEID] = new \lang_string($prefix.'_siteid', 'cache');
+        }
+        if ($sharingoption & cache_definition::SHARING_VERSION) {
+            $options[cache_definition::SHARING_VERSION] = new \lang_string($prefix.'_version', 'cache');
+        }
+        if ($sharingoption & cache_definition::SHARING_INPUT) {
+            $options[cache_definition::SHARING_INPUT] = new \lang_string($prefix.'_input', 'cache');
+        }
+        return $options;
+    }
+
+    /**
+     * Get an array of stores that are suitable to be used for a given definition.
+     *
+     * @param string $component
+     * @param string $area
+     * @return array Array containing 3 elements
+     *      1. An array of currently used stores
+     *      2. An array of suitable stores
+     *      3. An array of default stores
+     */
+    public static function get_definition_store_options(string $component, string $area): array {
+        $factory = cache_factory::instance();
+        $definition = $factory->create_definition($component, $area);
+        $config = cache_config::instance();
+        $currentstores = $config->get_stores_for_definition($definition);
+        $possiblestores = $config->get_stores($definition->get_mode(), $definition->get_requirements_bin());
+
+        $defaults = array();
+        foreach ($currentstores as $key => $store) {
+            if (!empty($store['default'])) {
+                $defaults[] = $key;
+                unset($currentstores[$key]);
+            }
+        }
+        foreach ($possiblestores as $key => $store) {
+            if ($store['default']) {
+                unset($possiblestores[$key]);
+                $possiblestores[$key] = $store;
+            }
+        }
+        return array($currentstores, $possiblestores, $defaults);
+    }
+
+    /**
+     * This function must be implemented to display options for store plugins.
+     *
+     * @param string $name the name of the store plugin.
+     * @param array $plugindetails array of store plugin details.
+     * @return array array of actions.
+     */
+    public function get_store_plugin_actions(string $name, array $plugindetails): array {
+        return array();
+    }
+
+    /**
+     * This function must be implemented to display options for store instances.
+     *
+     * @param string $name the store instance name.
+     * @param array $storedetails array of store instance details.
+     * @return array array of actions.
+     */
+    public function get_store_instance_actions(string $name, array $storedetails): array {
+        return array();
+    }
+
+    /**
+     * This function must be implemented to display options for definition mappings.
+     *
+     * @param context $context the context for the definition.
+     * @param array $definitionsummary the definition summary.
+     * @return array array of actions.
+     */
+    public function get_definition_actions(\context $context, array $definitionsummary): array {
+        return array();
+    }
+
+    /**
+     * This function must be implemented to get addable locks.
+     *
+     * @return array array of locks that are addable.
+     */
+    public function get_addable_lock_options(): array {
+        return array();
+    }
+
+    /**
+     * This function must be implemented to perform any page actions by a child class.
+     *
+     * @param string $action the action to perform.
+     * @param array $forminfo empty array to be set by actions.
+     * @return array array of form info.
+     */
+    public abstract function perform_cache_actions(string $action, array $forminfo): array;
+
+    /**
+     * This function must be implemented to display the cache admin page.
+     *
+     * @param core_cache_renderer $renderer the renderer used to generate the page.
+     * @return string the HTML for the page.
+     */
+    public abstract function generate_admin_page(\core_cache_renderer $renderer): string;
+}
diff --git a/cache/classes/factory.php b/cache/classes/factory.php
index a974377932d..9791c4715d2 100644
--- a/cache/classes/factory.php
+++ b/cache/classes/factory.php
@@ -112,7 +112,13 @@ class cache_factory {
     protected $state = 0;
 
     /**
-     * Returns an instance of the cache_factor method.
+     * The current cache display helper.
+     * @var core_cache\local\administration_display_helper
+     */
+    protected static $displayhelper = null;
+
+    /**
+     * Returns an instance of the cache_factory class.
      *
      * @param bool $forcereload If set to true a new cache_factory instance will be created and used.
      * @return cache_factory
@@ -134,6 +140,10 @@ class cache_factory {
                     // The cache stores have been disabled.
                     self::$instance->set_state(self::STATE_STORES_DISABLED);
                 }
+
+            } else if (!empty($CFG->alternative_cache_factory_class)) {
+                $factoryclass = $CFG->alternative_cache_factory_class;
+                self::$instance = new $factoryclass();
             } else {
                 // We're using the regular factory.
                 self::$instance = new cache_factory();
@@ -636,4 +646,16 @@ class cache_factory {
         $factory->reset_cache_instances();
         $factory->set_state(self::STATE_STORES_DISABLED);
     }
+
+    /**
+     * Returns an instance of the current display_helper.
+     *
+     * @return core_cache\administration_helper
+     */
+    public static function get_administration_display_helper() : core_cache\administration_helper {
+        if (is_null(self::$displayhelper)) {
+            self::$displayhelper = new \core_cache\local\administration_display_helper();
+        }
+        return self::$displayhelper;
+    }
 }
diff --git a/cache/classes/helper.php b/cache/classes/helper.php
index dc4821b2558..50643fe7f80 100644
--- a/cache/classes/helper.php
+++ b/cache/classes/helper.php
@@ -829,7 +829,7 @@ class cache_helper {
         global $CFG;
         if ($stores === null) {
             require_once($CFG->dirroot.'/cache/locallib.php');
-            $stores = cache_administration_helper::get_store_instance_summaries();
+            $stores = core_cache\administration_helper::get_store_instance_summaries();
         }
         $warnings = array();
         foreach ($stores as $store) {
diff --git a/cache/classes/local/administration_display_helper.php b/cache/classes/local/administration_display_helper.php
new file mode 100644
index 00000000000..1e2aff7c263
--- /dev/null
+++ b/cache/classes/local/administration_display_helper.php
@@ -0,0 +1,795 @@
+<?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, core_cache_renderer;
+
+/**
+ * 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);
+        }
+
+        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);
+        }
+
+        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);
+        }
+
+        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_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_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;
+    }
+}
\ No newline at end of file
diff --git a/cache/forms.php b/cache/forms.php
index e482702c424..16435705e23 100644
--- a/cache/forms.php
+++ b/cache/forms.php
@@ -97,7 +97,7 @@ class cachestore_addinstance_form extends moodleform {
             if (!preg_match('#^[a-zA-Z0-9\-_ ]+$#', $data['name'])) {
                 $errors['name'] = get_string('storenameinvalid', 'cache');
             } else if (empty($this->_customdata['store'])) {
-                $stores = cache_administration_helper::get_store_instance_summaries();
+                $stores = core_cache\administration_helper::get_store_instance_summaries();
                 if (array_key_exists($data['name'], $stores)) {
                     $errors['name'] = get_string('storenamealreadyused', 'cache');
                 }
@@ -139,9 +139,9 @@ class cache_definition_mappings_form extends moodleform {
 
         list($component, $area) = explode('/', $definition, 2);
         list($currentstores, $storeoptions, $defaults) =
-                cache_administration_helper::get_definition_store_options($component, $area);
+                core_cache\administration_helper::get_definition_store_options($component, $area);
 
-        $storedata = cache_administration_helper::get_definition_summaries();
+        $storedata = core_cache\administration_helper::get_definition_summaries();
         if ($storedata[$definition]['mode'] != cache_store::MODE_REQUEST) {
             if (isset($storedata[$definition]['canuselocalstore']) && $storedata[$definition]['canuselocalstore']) {
                 $form->addElement('html', $OUTPUT->notification(get_string('localstorenotification', 'cache'), 'notifymessage'));
@@ -247,7 +247,7 @@ class cache_definition_sharing_form extends moodleform {
     public function set_data($data) {
         if (!isset($data['sharing'])) {
             // Set the default value here. mforms doesn't handle defaults very nicely.
-            $data['sharing'] = cache_administration_helper::get_definition_sharing_options(cache_definition::SHARING_DEFAULT);
+            $data['sharing'] = core_cache\administration_helper::get_definition_sharing_options(cache_definition::SHARING_DEFAULT);
         }
         parent::set_data($data);
     }
diff --git a/cache/locallib.php b/cache/locallib.php
index 62ead7473f4..b8509cedc27 100644
--- a/cache/locallib.php
+++ b/cache/locallib.php
@@ -659,597 +659,4 @@ class cache_config_writer extends cache_config {
         }
         $this->config_save();
     }
-
-}
-
-/**
- * A cache helper for administration tasks
- *
- * @package    core
- * @category   cache
- * @copyright  2012 Sam Hemelryk
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-abstract class cache_administration_helper extends cache_helper {
-
-    /**
-     * Returns an array containing all of the information about stores a renderer needs.
-     * @return array
-     */
-    public static function get_store_instance_summaries() {
-        $return = array();
-        $default = array();
-        $instance = cache_config::instance();
-        $stores = $instance->get_all_stores();
-        $locks = $instance->get_locks();
-        foreach ($stores as $name => $details) {
-            $class = $details['class'];
-            $store = false;
-            if ($class::are_requirements_met()) {
-                $store = new $class($details['name'], $details['configuration']);
-            }
-            $lock = (isset($details['lock'])) ? $locks[$details['lock']] : $instance->get_default_lock();
-            $record = array(
-                'name' => $name,
-                'plugin' => $details['plugin'],
-                'default' => $details['default'],
-                'isready' => $store ? $store->is_ready() : false,
-                'requirementsmet' => $class::are_requirements_met(),
-                'mappings' => 0,
-                'lock' => $lock,
-                'modes' => array(
-                    cache_store::MODE_APPLICATION =>
-                        ($class::get_supported_modes($return) & cache_store::MODE_APPLICATION) == cache_store::MODE_APPLICATION,
-                    cache_store::MODE_SESSION =>
-                        ($class::get_supported_modes($return) & cache_store::MODE_SESSION) == cache_store::MODE_SESSION,
-                    cache_store::MODE_REQUEST =>
-                        ($class::get_supported_modes($return) & cache_store::MODE_REQUEST) == cache_store::MODE_REQUEST,
-                ),
-                'supports' => array(
-                    'multipleidentifiers' => $store ? $store->supports_multiple_identifiers() : false,
-                    'dataguarantee' => $store ? $store->supports_data_guarantee() : false,
-                    'nativettl' => $store ? $store->supports_native_ttl() : false,
-                    'nativelocking' => ($store instanceof cache_is_lockable),
-                    'keyawareness' => ($store instanceof cache_is_key_aware),
-                    'searchable' => ($store instanceof cache_is_searchable)
-                ),
-                'warnings' => $store ? $store->get_warnings() : array()
-            );
-            if (empty($details['default'])) {
-                $return[$name] = $record;
-            } else {
-                $default[$name] = $record;
-            }
-        }
-
-        ksort($return);
-        ksort($default);
-        $return = $return + $default;
-
-        foreach ($instance->get_definition_mappings() as $mapping) {
-            if (!array_key_exists($mapping['store'], $return)) {
-                continue;
-            }
-            $return[$mapping['store']]['mappings']++;
-        }
-
-        return $return;
-    }
-
-    /**
-     * Returns an array of information about plugins, everything a renderer needs.
-     *
-     * @return array for each store, an array containing various information about each store.
-     *     See the code below for details
-     */
-    public static function get_store_plugin_summaries() {
-        $return = array();
-        $plugins = core_component::get_plugin_list_with_file('cachestore', 'lib.php', true);
-        foreach ($plugins as $plugin => $path) {
-            $class = 'cachestore_'.$plugin;
-            $return[$plugin] = array(
-                'name' => get_string('pluginname', 'cachestore_'.$plugin),
-                'requirementsmet' => $class::are_requirements_met(),
-                'instances' => 0,
-                'modes' => array(
-                    cache_store::MODE_APPLICATION => ($class::get_supported_modes() & cache_store::MODE_APPLICATION),
-                    cache_store::MODE_SESSION => ($class::get_supported_modes() & cache_store::MODE_SESSION),
-                    cache_store::MODE_REQUEST => ($class::get_supported_modes() & cache_store::MODE_REQUEST),
-                ),
-                'supports' => array(
-                    'multipleidentifiers' => ($class::get_supported_features() & cache_store::SUPPORTS_MULTIPLE_IDENTIFIERS),
-                    'dataguarantee' => ($class::get_supported_features() & cache_store::SUPPORTS_DATA_GUARANTEE),
-                    'nativettl' => ($class::get_supported_features() & cache_store::SUPPORTS_NATIVE_TTL),
-                    'nativelocking' => (in_array('cache_is_lockable', class_implements($class))),
-                    'keyawareness' => (array_key_exists('cache_is_key_aware', class_implements($class))),
-                ),
-                'canaddinstance' => ($class::can_add_instance() && $class::are_requirements_met())
-            );
-        }
-
-        $instance = cache_config::instance();
-        $stores = $instance->get_all_stores();
-        foreach ($stores as $store) {
-            $plugin = $store['plugin'];
-            if (array_key_exists($plugin, $return)) {
-                $return[$plugin]['instances']++;
-            }
-        }
-
-        return $return;
-    }
-
-    /**
-     * Returns an array about the definitions. All the information a renderer needs.
-     *
-     * @return array for each store, an array containing various information about each store.
-     *     See the code below for details
-     */
-    public static function get_definition_summaries() {
-        $factory = cache_factory::instance();
-        $config = $factory->create_config_instance();
-        $storenames = array();
-        foreach ($config->get_all_stores() as $key => $store) {
-            if (!empty($store['default'])) {
-                $storenames[$key] = new lang_string('store_'.$key, 'cache');
-            } else {
-                $storenames[$store['name']] = $store['name'];
-            }
-        }
-        /* @var cache_definition[] $definitions */
-        $definitions = array();
-        foreach ($config->get_definitions() as $key => $definition) {
-            $definitions[$key] = cache_definition::load($definition['component'].'/'.$definition['area'], $definition);
-        }
-        foreach ($definitions as $id => $definition) {
-            $mappings = array();
-            foreach (cache_helper::get_stores_suitable_for_definition($definition) as $store) {
-                $mappings[] = $storenames[$store->my_name()];
-            }
-            $return[$id] = array(
-                'id' => $id,
-                'name' => $definition->get_name(),
-                'mode' => $definition->get_mode(),
-                'component' => $definition->get_component(),
-                'area' => $definition->get_area(),
-                'mappings' => $mappings,
-                'canuselocalstore' => $definition->can_use_localstore(),
-                'sharingoptions' => self::get_definition_sharing_options($definition->get_sharing_options(), false),
-                'selectedsharingoption' => self::get_definition_sharing_options($definition->get_selected_sharing_option(), true),
-                'userinputsharingkey' => $definition->get_user_input_sharing_key()
-            );
-        }
-        return $return;
-    }
-
-    /**
-     * Given a sharing option hash this function returns an array of strings that can be used to describe it.
-     *
-     * @param int $sharingoption The sharing option hash to get strings for.
-     * @param bool $isselectedoptions Set to true if the strings will be used to view the selected options.
-     * @return array An array of lang_string's.
-     */
-    public static function get_definition_sharing_options($sharingoption, $isselectedoptions = true) {
-        $options = array();
-        $prefix = ($isselectedoptions) ? 'sharingselected' : 'sharing';
-        if ($sharingoption & cache_definition::SHARING_ALL) {
-            $options[cache_definition::SHARING_ALL] = new lang_string($prefix.'_all', 'cache');
-        }
-        if ($sharingoption & cache_definition::SHARING_SITEID) {
-            $options[cache_definition::SHARING_SITEID] = new lang_string($prefix.'_siteid', 'cache');
-        }
-        if ($sharingoption & cache_definition::SHARING_VERSION) {
-            $options[cache_definition::SHARING_VERSION] = new lang_string($prefix.'_version', 'cache');
-        }
-        if ($sharingoption & cache_definition::SHARING_INPUT) {
-            $options[cache_definition::SHARING_INPUT] = new lang_string($prefix.'_input', 'cache');
-        }
-        return $options;
-    }
-
-    /**
-     * 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
-     *      cache_administration_helper::get_definition_summaries(). Currently only 'sharingoptions'
-     *      element is used.
-     * @return array of actions. Each action is an array with two elements, 'text' and 'url'.
-     */
-    public static function get_definition_actions(context $context, array $definitionsummary) {
-        if (has_capability('moodle/site:config', $context)) {
-            $actions = array();
-            // Edit mappings.
-            $actions[] = array(
-                'text' => get_string('editmappings', 'cache'),
-                'url' => new moodle_url('/cache/admin.php', array('action' => 'editdefinitionmapping', 'sesskey' => sesskey()))
-            );
-            // Edit sharing.
-            if (count($definitionsummary['sharingoptions']) > 1) {
-                $actions[] = array(
-                    'text' => get_string('editsharing', 'cache'),
-                    'url' => new moodle_url('/cache/admin.php', array('action' => 'editdefinitionsharing', 'sesskey' => sesskey()))
-                );
-            }
-            // Purge.
-            $actions[] = array(
-                'text' => get_string('purge', 'cache'),
-                'url' => new moodle_url('/cache/admin.php', array('action' => 'purgedefinition', 'sesskey' => sesskey()))
-            );
-            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
-     *      cache_administration_helper::get_store_instance_summaries().
-     * @return array of actions. Each action is an array with two elements, 'text' and 'url'.
-     */
-    public static function get_store_instance_actions($name, array $storedetails) {
-        $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[] = array(
-                    'text' => get_string('editstore', 'cache'),
-                    'url' => new moodle_url($baseurl, array('action' => 'editstore', 'plugin' => $storedetails['plugin']))
-                );
-                $actions[] = array(
-                    'text' => get_string('deletestore', 'cache'),
-                    'url' => new moodle_url($baseurl, array('action' => 'deletestore'))
-                );
-            }
-            $actions[] = array(
-                'text' => get_string('purge', 'cache'),
-                'url' => new moodle_url($baseurl, array('action' => 'purgestore'))
-            );
-        }
-        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
-     *      cache_administration_helper::get_store_plugin_summaries().
-     * @param array $plugindetails
-     * @return array
-     */
-    public static function get_store_plugin_actions($name, array $plugindetails) {
-        $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[] = array(
-                    'text' => get_string('addinstance', 'cache'),
-                    'url' => $url
-                );
-            }
-        }
-        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 static function get_add_store_form($plugin) {
-        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 = self::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 static function get_edit_store_form($plugin, $store) {
-        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 = self::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 static function get_possible_locks_for_stores($plugindir, $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 static function get_store_configuration_from_data(stdClass $data) {
-        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();
-    }
-
-    /**
-     * Get an array of stores that are suitable to be used for a given definition.
-     *
-     * @param string $component
-     * @param string $area
-     * @return array Array containing 3 elements
-     *      1. An array of currently used stores
-     *      2. An array of suitable stores
-     *      3. An array of default stores
-     */
-    public static function get_definition_store_options($component, $area) {
-        $factory = cache_factory::instance();
-        $definition = $factory->create_definition($component, $area);
-        $config = cache_config::instance();
-        $currentstores = $config->get_stores_for_definition($definition);
-        $possiblestores = $config->get_stores($definition->get_mode(), $definition->get_requirements_bin());
-
-        $defaults = array();
-        foreach ($currentstores as $key => $store) {
-            if (!empty($store['default'])) {
-                $defaults[] = $key;
-                unset($currentstores[$key]);
-            }
-        }
-        foreach ($possiblestores as $key => $store) {
-            if ($store['default']) {
-                unset($possiblestores[$key]);
-                $possiblestores[$key] = $store;
-            }
-        }
-        return array($currentstores, $possiblestores, $defaults);
-    }
-
-    /**
-     * Get the default stores for all modes.
-     *
-     * @return array An array containing sub-arrays, one for each mode.
-     */
-    public static function get_default_mode_stores() {
-        global $OUTPUT;
-        $instance = cache_config::instance();
-        $adequatestores = cache_helper::get_stores_suitable_for_mode_default();
-        $icon = new pix_icon('i/warning', new lang_string('inadequatestoreformapping', 'cache'));
-        $storenames = array();
-        foreach ($instance->get_all_stores() as $key => $store) {
-            if (!empty($store['default'])) {
-                $storenames[$key] = new lang_string('store_'.$key, 'cache');
-            }
-        }
-        $modemappings = array(
-            cache_store::MODE_APPLICATION => array(),
-            cache_store::MODE_SESSION => array(),
-            cache_store::MODE_REQUEST => array(),
-        );
-        foreach ($instance->get_mode_mappings() as $mapping) {
-            $mode = $mapping['mode'];
-            if (!array_key_exists($mode, $modemappings)) {
-                debugging('Unknown mode in cache store mode mappings', DEBUG_DEVELOPER);
-                continue;
-            }
-            if (array_key_exists($mapping['store'], $storenames)) {
-                $modemappings[$mode][$mapping['store']] = $storenames[$mapping['store']];
-            } else {
-                $modemappings[$mode][$mapping['store']] = $mapping['store'];
-            }
-            if (!array_key_exists($mapping['store'], $adequatestores)) {
-                $modemappings[$mode][$mapping['store']] = $modemappings[$mode][$mapping['store']].' '.$OUTPUT->render($icon);
-            }
-        }
-        return $modemappings;
-    }
-
-    /**
-     * Returns an array summarising the locks available in the system
-     */
-    public static function get_lock_summaries() {
-        $locks = array();
-        $instance = cache_config::instance();
-        $stores = $instance->get_all_stores();
-        foreach ($instance->get_locks() as $lock) {
-            $default = !empty($lock['default']);
-            if ($default) {
-                $name = new lang_string($lock['name'], 'cache');
-            } else {
-                $name = $lock['name'];
-            }
-            $uses = 0;
-            foreach ($stores as $store) {
-                if (!empty($store['lock']) && $store['lock'] === $lock['name']) {
-                    $uses++;
-                }
-            }
-            $lockdata = array(
-                'name' => $name,
-                'default' => $default,
-                'uses' => $uses,
-                'type' => get_string('pluginname', $lock['type'])
-            );
-            $locks[$lock['name']] = $lockdata;
-        }
-        return $locks;
-    }
-
-    /**
-     * Returns an array of lock plugins for which we can add an instance.
-     *
-     * Suitable for use within an mform select element.
-     *
-     * @return array
-     */
-    public static function get_addable_lock_options() {
-        $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 static function get_add_lock_form($plugin, array $lockplugin = null) {
-        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 static function get_lock_configuration_from_data($plugin, $data) {
-        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();
-    }
 }
diff --git a/cache/renderer.php b/cache/renderer.php
index 38ef769901b..93b3655b5af 100644
--- a/cache/renderer.php
+++ b/cache/renderer.php
@@ -41,9 +41,9 @@ class core_cache_renderer extends plugin_renderer_base {
      * Displays store summaries.
      *
      * @param array $storeinstancesummaries information about each store instance,
-     *      as returned by cache_administration_helper::get_store_instance_summaries().
+     *      as returned by core_cache\administration_helper::get_store_instance_summaries().
      * @param array $storepluginsummaries information about each store plugin as
-     *      returned by cache_administration_helper::get_store_plugin_summaries().
+     *      returned by core_cache\administration_helper::get_store_plugin_summaries().
      * @return string HTML
      */
     public function store_instance_summariers(array $storeinstancesummaries, array $storepluginsummaries) {
@@ -73,7 +73,7 @@ class core_cache_renderer extends plugin_renderer_base {
         $defaultstoreactions = get_string('defaultstoreactions', 'cache');
 
         foreach ($storeinstancesummaries as $name => $storesummary) {
-            $actions = cache_administration_helper::get_store_instance_actions($name, $storesummary);
+            $htmlactions = cache_factory::get_administration_display_helper()->get_store_instance_actions($name, $storesummary);
             $modes = array();
             foreach ($storesummary['modes'] as $mode => $enabled) {
                 if ($enabled) {
@@ -92,10 +92,6 @@ class core_cache_renderer extends plugin_renderer_base {
             if (!empty($storesummary['default'])) {
                 $info = $this->output->pix_icon('i/info', $defaultstoreactions, '', array('class' => 'icon'));
             }
-            $htmlactions = array();
-            foreach ($actions as $action) {
-                $htmlactions[] = $this->output->action_link($action['url'], $action['text']);
-            }
 
             $isready = $storesummary['isready'] && $storesummary['requirementsmet'];
             $readycell = new html_table_cell;
@@ -145,7 +141,7 @@ class core_cache_renderer extends plugin_renderer_base {
      * Displays plugin summaries.
      *
      * @param array $storepluginsummaries information about each store plugin as
-     *      returned by cache_administration_helper::get_store_plugin_summaries().
+     *      returned by core_cache\administration_helper::get_store_plugin_summaries().
      * @return string HTML
      */
     public function store_plugin_summaries(array $storepluginsummaries) {
@@ -169,7 +165,7 @@ class core_cache_renderer extends plugin_renderer_base {
         $table->data = array();
 
         foreach ($storepluginsummaries as $name => $plugin) {
-            $actions = cache_administration_helper::get_store_plugin_actions($name, $plugin);
+            $htmlactions = cache_factory::get_administration_display_helper()->get_store_plugin_actions($name, $plugin);
 
             $modes = array();
             foreach ($plugin['modes'] as $mode => $enabled) {
@@ -185,11 +181,6 @@ class core_cache_renderer extends plugin_renderer_base {
                 }
             }
 
-            $htmlactions = array();
-            foreach ($actions as $action) {
-                $htmlactions[] = $this->output->action_link($action['url'], $action['text']);
-            }
-
             $row = new html_table_row(array(
                 $plugin['name'],
                 ($plugin['requirementsmet']) ? $this->output->pix_icon('i/valid', '1') : '',
@@ -214,7 +205,7 @@ class core_cache_renderer extends plugin_renderer_base {
      * Displays definition summaries.
      *
      * @param array $definitionsummaries information about each definition, as returned by
-     *      cache_administration_helper::get_definition_summaries().
+     *      core_cache\administration_helper::get_definition_summaries().
      * @param context $context the system context.
      *
      * @return string HTML.
@@ -247,12 +238,7 @@ class core_cache_renderer extends plugin_renderer_base {
 
         $none = new lang_string('none', 'cache');
         foreach ($definitionsummaries as $id => $definition) {
-            $actions = cache_administration_helper::get_definition_actions($context, $definition);
-            $htmlactions = array();
-            foreach ($actions as $action) {
-                $action['url']->param('definition', $id);
-                $htmlactions[] = $this->output->action_link($action['url'], $action['text']);
-            }
+            $htmlactions = cache_factory::get_administration_display_helper()->get_definition_actions($context, $definition);
             if (!empty($definition['mappings'])) {
                 $mapping = join(', ', $definition['mappings']);
             } else {
@@ -379,13 +365,24 @@ class core_cache_renderer extends plugin_renderer_base {
             ));
         }
 
-        $url = new moodle_url('/cache/admin.php', array('action' => 'newlockinstance', 'sesskey' => sesskey()));
-        $select = new single_select($url, 'lock', cache_administration_helper::get_addable_lock_options());
-        $select->label = get_string('addnewlockinstance', 'cache');
-
         $html = html_writer::start_tag('div', array('id' => 'core-cache-lock-summary'));
         $html .= $this->output->heading(get_string('locksummary', 'cache'), 3);
         $html .= html_writer::table($table);
+        $html .= html_writer::end_tag('div');
+        return $html;
+    }
+
+    /**
+     * Renders additional actions for locks, such as Add.
+     *
+     * @return string
+     */
+    public function additional_lock_actions() : string {
+        $url = new moodle_url('/cache/admin.php', array('action' => 'newlockinstance', 'sesskey' => sesskey()));
+        $select = new single_select($url, 'lock', cache_factory::get_administration_display_helper()->get_addable_lock_options());
+        $select->label = get_string('addnewlockinstance', 'cache');
+
+        $html = html_writer::start_tag('div', array('id' => 'core-cache-lock-additional-actions'));
         $html .= html_writer::tag('div', $this->output->render($select), array('class' => 'new-instance'));
         $html .= html_writer::end_tag('div');
         return $html;
diff --git a/cache/tests/administration_helper_test.php b/cache/tests/administration_helper_test.php
index 95c70de7515..865539f51d3 100644
--- a/cache/tests/administration_helper_test.php
+++ b/cache/tests/administration_helper_test.php
@@ -35,7 +35,7 @@ require_once($CFG->dirroot.'/cache/tests/fixtures/lib.php');
 
 
 /**
- * PHPunit tests for the cache API and in particular the cache_administration_helper
+ * PHPunit tests for the cache API and in particular the core_cache\administration_helper
  *
  * @copyright  2012 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@@ -73,7 +73,7 @@ class core_cache_administration_helper_testcase extends advanced_testcase {
             cache_store::MODE_REQUEST => array('default_request'),
         )));
 
-        $storesummaries = cache_administration_helper::get_store_instance_summaries();
+        $storesummaries = core_cache\administration_helper::get_store_instance_summaries();
         $this->assertInternalType('array', $storesummaries);
         $this->assertArrayHasKey('summariesstore', $storesummaries);
         $summary = $storesummaries['summariesstore'];
@@ -94,7 +94,7 @@ class core_cache_administration_helper_testcase extends advanced_testcase {
         $this->assertEquals(1, $summary['requirementsmet']);
         $this->assertEquals(1, $summary['mappings']);
 
-        $definitionsummaries = cache_administration_helper::get_definition_summaries();
+        $definitionsummaries = core_cache\administration_helper::get_definition_summaries();
         $this->assertInternalType('array', $definitionsummaries);
         $this->assertArrayHasKey('core/eventinvalidation', $definitionsummaries);
         $summary = $definitionsummaries['core/eventinvalidation'];
@@ -114,7 +114,7 @@ class core_cache_administration_helper_testcase extends advanced_testcase {
         $this->assertInternalType('array', $summary['mappings']);
         $this->assertContains('summariesstore', $summary['mappings']);
 
-        $pluginsummaries = cache_administration_helper::get_store_plugin_summaries();
+        $pluginsummaries = core_cache\administration_helper::get_store_plugin_summaries();
         $this->assertInternalType('array', $pluginsummaries);
         $this->assertArrayHasKey('file', $pluginsummaries);
         $summary = $pluginsummaries['file'];
@@ -126,18 +126,18 @@ class core_cache_administration_helper_testcase extends advanced_testcase {
         $this->assertArrayHasKey('supports', $summary);
         $this->assertArrayHasKey('canaddinstance', $summary);
 
-        $locksummaries = cache_administration_helper::get_lock_summaries();
+        $locksummaries = core_cache\administration_helper::get_lock_summaries();
         $this->assertInternalType('array', $locksummaries);
         $this->assertTrue(count($locksummaries) > 0);
 
-        $mappings = cache_administration_helper::get_default_mode_stores();
+        $mappings = core_cache\administration_helper::get_default_mode_stores();
         $this->assertInternalType('array', $mappings);
         $this->assertCount(3, $mappings);
         $this->assertArrayHasKey(cache_store::MODE_APPLICATION, $mappings);
         $this->assertInternalType('array', $mappings[cache_store::MODE_APPLICATION]);
         $this->assertContains('summariesstore', $mappings[cache_store::MODE_APPLICATION]);
 
-        $potentials = cache_administration_helper::get_definition_store_options('core', 'eventinvalidation');
+        $potentials = core_cache\administration_helper::get_definition_store_options('core', 'eventinvalidation');
         $this->assertInternalType('array', $potentials); // Currently used, suitable, default
         $this->assertCount(3, $potentials);
         $this->assertArrayHasKey('summariesstore', $potentials[0]);
@@ -149,11 +149,11 @@ class core_cache_administration_helper_testcase extends advanced_testcase {
      * Test instantiating an add store form.
      */
     public function test_get_add_store_form() {
-        $form = cache_administration_helper::get_add_store_form('file');
+        $form = cache_factory::get_administration_display_helper()->get_add_store_form('file');
         $this->assertInstanceOf('moodleform', $form);
 
         try {
-            $form = cache_administration_helper::get_add_store_form('somethingstupid');
+            $form = cache_factory::get_administration_display_helper()->get_add_store_form('somethingstupid');
             $this->fail('You should not be able to create an add form for a store plugin that does not exist.');
         } catch (moodle_exception $e) {
             $this->assertInstanceOf('coding_exception', $e, 'Needs to be: ' .get_class($e)." ::: ".$e->getMessage());
@@ -164,21 +164,23 @@ class core_cache_administration_helper_testcase extends advanced_testcase {
      * Test instantiating a form to edit a store instance.
      */
     public function test_get_edit_store_form() {
+        // Always instantiate a new core display helper here.
+        $administrationhelper = new core_cache\local\administration_display_helper;
         $config = cache_config_writer::instance();
         $this->assertTrue($config->add_store_instance('test_get_edit_store_form', 'file'));
 
-        $form = cache_administration_helper::get_edit_store_form('file', 'test_get_edit_store_form');
+        $form = $administrationhelper->get_edit_store_form('file', 'test_get_edit_store_form');
         $this->assertInstanceOf('moodleform', $form);
 
         try {
-            $form = cache_administration_helper::get_edit_store_form('somethingstupid', 'moron');
+            $form = $administrationhelper->get_edit_store_form('somethingstupid', 'moron');
             $this->fail('You should not be able to create an edit form for a store plugin that does not exist.');
         } catch (moodle_exception $e) {
             $this->assertInstanceOf('coding_exception', $e);
         }
 
         try {
-            $form = cache_administration_helper::get_edit_store_form('file', 'blisters');
+            $form = $administrationhelper->get_edit_store_form('file', 'blisters');
             $this->fail('You should not be able to create an edit form for a store plugin that does not exist.');
         } catch (moodle_exception $e) {
             $this->assertInstanceOf('coding_exception', $e);
diff --git a/cache/upgrade.txt b/cache/upgrade.txt
index d92a97e4952..1890d306076 100644
--- a/cache/upgrade.txt
+++ b/cache/upgrade.txt
@@ -6,6 +6,8 @@ Information provided here is intended especially for developers.
 * The function extend_lock() from the lock_factory interface has been deprecated without replacement including the related
   implementations.
 * The function extend() from the lock class has been deprecated without replacement.
+* The cache_factory class can now be overridden by an alternative cache config class, which can
+  also now control the frontend display of the cache/admin.php page (see MDL-41492).
 
 === 3.9 ===
 * The record_cache_hit/miss/set methods now take a cache_store instead of a cache_definition object
diff --git a/config-dist.php b/config-dist.php
index ecd56b6b6fa..97fae22b8b0 100644
--- a/config-dist.php
+++ b/config-dist.php
@@ -1057,6 +1057,18 @@ $CFG->admin = 'admin';
 //      $CFG->showcampaigncontent = true;
 //
 //=========================================================================
+// 16. ALTERNATIVE CACHE CONFIG SETTINGS
+//=========================================================================
+//
+// Alternative cache config.
+// Since 3.10 it is possible to override the cache_factory class with an alternative caching factory.
+// This overridden factory can provide alternative classes for caching such as cache_config,
+// cache_config_writer and core_cache\local\administration_display_helper.
+// The autoloaded factory class name can be specified to use.
+//
+//      $CFG->alternative_cache_factory_class = 'tool_alternativecache_cache_factory';
+//
+//=========================================================================
 // ALL DONE!  To continue installation, visit your main page with a browser
 //=========================================================================
 
diff --git a/theme/boost/scss/moodle/admin.scss b/theme/boost/scss/moodle/admin.scss
index 65b5cd09b47..0b545e34d04 100644
--- a/theme/boost/scss/moodle/admin.scss
+++ b/theme/boost/scss/moodle/admin.scss
@@ -603,7 +603,7 @@
 
 #core-cache-rescan-definitions,
 #core-cache-mode-mappings .edit-link,
-#core-cache-lock-summary .new-instance {
+#core-cache-lock-additional-actions .new-instance {
     margin-top: 0.5em;
     text-align: center;
 }
diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css
index 6a60c9a243f..916d24ecddf 100644
--- a/theme/boost/style/moodle.css
+++ b/theme/boost/style/moodle.css
@@ -12420,7 +12420,7 @@ input[disabled] {
 
 #core-cache-rescan-definitions,
 #core-cache-mode-mappings .edit-link,
-#core-cache-lock-summary .new-instance {
+#core-cache-lock-additional-actions .new-instance {
   margin-top: 0.5em;
   text-align: center; }
 
diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css
index 37f2d5b77e9..72bfb01f99a 100644
--- a/theme/classic/style/moodle.css
+++ b/theme/classic/style/moodle.css
@@ -12634,7 +12634,7 @@ input[disabled] {
 
 #core-cache-rescan-definitions,
 #core-cache-mode-mappings .edit-link,
-#core-cache-lock-summary .new-instance {
+#core-cache-lock-additional-actions .new-instance {
   margin-top: 0.5em;
   text-align: center; }