Skip to content

Commit

Permalink
MDL-61244 caching: Allow granular purging of caches
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark Johnson committed Jun 14, 2018
1 parent 9e7c397 commit e3389c8
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 29 deletions.
75 changes: 75 additions & 0 deletions admin/classes/form/purge_caches.php
@@ -0,0 +1,75 @@
<?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/>.

/**
* Form for selective purging of caches.
*
* @package core
* @copyright 2018 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace core_admin\form;

defined('MOODLE_INTERNAL') || die();

require_once($CFG->libdir.'/formslib.php');

/**
* Form for selecting which caches to purge on admin/purgecaches.php
*
* @package core
* @copyright 2018 The Open University
* @author Mark Johnson <mark.johnson@open.ac.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class purge_caches extends \moodleform {
/**
* Define a "Purge all caches" button, and a fieldset with checkboxes for selectively purging separate caches.
*/
public function definition() {
$mform = $this->_form;
$mform->addElement('hidden', 'returnurl', $this->_customdata['returnurl']);
$mform->setType('returnurl', PARAM_LOCALURL);
$mform->addElement('submit', 'all', get_string('purgecaches', 'admin'));
$mform->addElement('header', 'purgecacheheader', get_string('purgeselectedcaches', 'admin'));
$checkboxes = [
$mform->createElement('advcheckbox', 'theme', '', get_string('purgethemecache', 'admin')),
$mform->createElement('advcheckbox', 'lang', '', get_string('purgelangcache', 'admin')),
$mform->createElement('advcheckbox', 'js', '', get_string('purgejscache', 'admin')),
$mform->createElement('advcheckbox', 'filter', '', get_string('purgefiltercache', 'admin')),
$mform->createElement('advcheckbox', 'muc', '', get_string('purgemuc', 'admin')),
$mform->createElement('advcheckbox', 'other', '', get_string('purgeothercaches', 'admin'))
];
$mform->addGroup($checkboxes, 'purgeselectedoptions');
$mform->addElement('submit', 'purgeselectedcaches', get_string('purgeselectedcaches', 'admin'));
}

/**
* If the "Purge selected caches" button was pressed, ensure at least one cache was selected.
*
* @param array $data
* @param array $files
* @return array Error messages
*/
public function validation($data, $files) {
$errors = [];
if (isset($data['purgeselectedcaches']) && empty(array_filter($data['purgeselectedoptions']))) {
$errors['purgeselectedoptions'] = get_string('purgecachesnoneselected', 'admin');
}
return $errors;
}
}
33 changes: 27 additions & 6 deletions admin/cli/purge_caches.php
Expand Up @@ -28,28 +28,49 @@
require(__DIR__.'/../../config.php');
require_once($CFG->libdir.'/clilib.php');

list($options, $unrecognized) = cli_get_params(array('help' => false), array('h' => 'help'));
$longoptions = [
'help' => false,
'muc' => false,
'theme' => false,
'lang' => false,
'js' => false,
'filter' => false,
'other' => false
];
list($options, $unrecognized) = cli_get_params($longoptions, ['h' => 'help']);

if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized), 2);
}

if ($options['help']) {
$help =
"Invalidates all Moodle internal caches
// The indentation of this string is "wrong" but this is to avoid a extra whitespace in console output.
$help = <<<EOF
Invalidates Moodle internal caches
Specific caches can be defined (alone or in combination) using arguments. If none are specified,
all caches will be purged.
Options:
-h, --help Print out this help
--muc Purge all MUC caches (includes lang cache)
--theme Purge theme cache
--lang Purge language string cache
--js Purge JavaScript cache
--filter Purge text filter cache
--other Purge all file caches and other miscellaneous caches (may include MUC
if using cachestore_file).
Example:
\$sudo -u www-data /usr/bin/php admin/cli/purge_caches.php
";
\$ sudo -u www-data /usr/bin/php admin/cli/purge_caches.php
EOF;

echo $help;
exit(0);
}

purge_all_caches();
purge_caches(array_filter($options));

exit(0);
33 changes: 20 additions & 13 deletions admin/purgecaches.php
Expand Up @@ -27,36 +27,43 @@
require_once($CFG->libdir.'/adminlib.php');

$confirm = optional_param('confirm', 0, PARAM_BOOL);
$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
$returnurl = optional_param('returnurl', '/admin/purgecaches.php', PARAM_LOCALURL);
$returnurl = new moodle_url($returnurl);

admin_externalpage_setup('purgecaches');

$form = new core_admin\form\purge_caches(null, ['returnurl' => $returnurl]);

// If we have got here as a confirmed aciton, do it.
if ($confirm && confirm_sesskey()) {
if ($data = $form->get_data()) {

// Valid request. Purge, and redirect the user back to where they came from.
purge_all_caches();
$selected = $data->purgeselectedoptions;
purge_caches($selected);

if ($returnurl) {
$returnurl = $CFG->wwwroot . $returnurl;
if (isset($data->all)) {
$message = get_string('purgecachesfinished', 'admin');
} else {
$returnurl = new moodle_url('/admin/purgecaches.php');
$message = get_string('purgeselectedcachesfinished', 'admin');
}
redirect($returnurl, get_string('purgecachesfinished', 'admin'));

} else if ($confirm && confirm_sesskey()) {
purge_caches();
$message = get_string('purgecachesfinished', 'admin');
}

// Otherwise, show a button to actually purge the caches.
$actionurl = new moodle_url('/admin/purgecaches.php', array('sesskey'=>sesskey(), 'confirm'=>1));
if ($returnurl) {
$actionurl->param('returnurl', $returnurl);
if (isset($message)) {
redirect($returnurl, $message);
}

// Otherwise, show a form to actually purge the caches.

echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('purgecaches', 'admin'));
echo $OUTPUT->heading(get_string('purgecachespage', 'admin'));

echo $OUTPUT->box_start('generalbox', 'notice');
echo html_writer::tag('p', get_string('purgecachesconfirm', 'admin'));
echo $OUTPUT->single_button($actionurl, get_string('purgecaches', 'admin'), 'post');
echo $form->render();
echo $OUTPUT->box_end();

echo $OUTPUT->footer();
3 changes: 2 additions & 1 deletion admin/settings/development.php
Expand Up @@ -83,7 +83,8 @@
$ADMIN->add('development', new admin_externalpage('mnettestclient', new lang_string('testclient', 'mnet'), "$CFG->wwwroot/$CFG->admin/mnet/testclient.php"));
}

$ADMIN->add('development', new admin_externalpage('purgecaches', new lang_string('purgecaches','admin'), "$CFG->wwwroot/$CFG->admin/purgecaches.php"));
$ADMIN->add('development', new admin_externalpage('purgecaches', new lang_string('purgecachespage', 'admin'),
"$CFG->wwwroot/$CFG->admin/purgecaches.php"));

$ADMIN->add('development', new admin_externalpage('thirdpartylibs', new lang_string('thirdpartylibs','admin'), "$CFG->wwwroot/$CFG->admin/thirdpartylibs.php"));
} // end of speedup
34 changes: 34 additions & 0 deletions admin/tests/behat/purge_caches.feature
@@ -0,0 +1,34 @@
@core @core_admin
Feature: Purge caches
In order to see changes to cached data
As a Moodle administrator
I want manually purge different data and file caches

Background:
Given I log in as "admin"
And I navigate to "Development > Purge all caches" in site administration

Scenario: Purge all caches
Given I should not see "All caches were purged"
When I press "Purge all caches"
Then I should see "All caches were purged"

Scenario: Purge selected caches
Given I should not see "Selected caches were purged"
When I set the field "Themes" to "1"
And I press "Purge selected caches"
Then I should see "The selected caches were purged"

Scenario: Purge selected caches without selecting any caches
Given I should not see "Select one or more caches to purge"
When I press "Purge selected caches"
Then I should not see "The selected caches were purged"
And I should see "Select one or more caches to purge"

Scenario: Redirect back to the original page after following a Purge all caches link
Given I am on site homepage
And I should see "Available courses"
And I should not see "All caches were purged"
When I follow "Purge all caches"
Then I should see "All caches were purged"
And I should see "Available courses"
10 changes: 10 additions & 0 deletions lang/en/admin.php
Expand Up @@ -978,6 +978,16 @@
$string['purgecaches'] = 'Purge all caches';
$string['purgecachesconfirm'] = 'Moodle can cache themes, javascript, language strings, filtered text, rss feeds and many other pieces of calculated data. Purging these caches will delete that data from the server and force browsers to refetch data, so that you can be sure you are seeing the most up-to-date values produced by the current code. There is no danger in purging caches, but your site may appear slower for a while until the server and clients calculate new information and cache it.';
$string['purgecachesfinished'] = 'All caches were purged.';
$string['purgecachesnoneselected'] = 'Select one or more caches to purge';
$string['purgecachespage'] = 'Purge caches';
$string['purgefiltercache'] = 'Text filters';
$string['purgejscache'] = 'JavaScript';
$string['purgelangcache'] = 'Language strings';
$string['purgemuc'] = 'All MUC caches';
$string['purgeothercaches'] = 'All file and miscellaneous caches';
$string['purgeselectedcaches'] = 'Purge selected caches';
$string['purgeselectedcachesfinished'] = 'The selected caches were purged.';
$string['purgethemecache'] = 'Themes';
$string['requestcategoryselection'] = 'Enable category selection';
$string['restorecourse'] = 'Restore course';
$string['restorernewroleid'] = 'Restorers\' role in courses';
Expand Down
60 changes: 51 additions & 9 deletions lib/moodlelib.php
Expand Up @@ -1627,18 +1627,61 @@ function get_users_from_config($value, $capability, $includeadmins = true) {
/**
* Invalidates browser caches and cached data in temp.
*
* IMPORTANT - If you are adding anything here to do with the cache directory you should also have a look at
* {@link phpunit_util::reset_dataroot()}
*
* @return void
*/
function purge_all_caches() {
global $CFG, $DB;
purge_caches();
}

reset_text_filters_cache();
js_reset_all_caches();
theme_reset_all_caches();
get_string_manager()->reset_caches();
/**
* Selectively invalidate different types of cache.
*
* Purges the cache areas specified. By default, this will purge all caches but can selectively purge specific
* areas alone or in combination.
*
* @param bool[] $options Specific parts of the cache to purge. Valid options are:
* 'muc' Purge MUC caches?
* 'theme' Purge theme cache?
* 'lang' Purge language string cache?
* 'js' Purge javascript cache?
* 'filter' Purge text filter cache?
* 'other' Purge all other caches?
*/
function purge_caches($options = []) {
$defaults = array_fill_keys(['muc', 'theme', 'lang', 'js', 'filter', 'other'], false);
if (empty(array_filter($options))) {
$options = array_fill_keys(array_keys($defaults), true); // Set all options to true.
} else {
$options = array_merge($defaults, array_intersect_key($options, $defaults)); // Override defaults with specified options.
}
if ($options['muc']) {
cache_helper::purge_all();
}
if ($options['theme']) {
theme_reset_all_caches();
}
if ($options['lang']) {
get_string_manager()->reset_caches();
}
if ($options['js']) {
js_reset_all_caches();
}
if ($options['filter']) {
reset_text_filters_cache();
}
if ($options['other']) {
purge_other_caches();
}
}

/**
* Purge all non-MUC caches not otherwise purged in purge_caches.
*
* IMPORTANT - If you are adding anything here to do with the cache directory you should also have a look at
* {@link phpunit_util::reset_dataroot()}
*/
function purge_other_caches() {
global $DB, $CFG;
core_text::reset_caches();
if (class_exists('core_plugin_manager')) {
core_plugin_manager::reset_caches();
Expand All @@ -1652,7 +1695,6 @@ function purge_all_caches() {
}

$DB->reset_caches();
cache_helper::purge_all();

// Purge all other caches: rss, simplepie, etc.
clearstatcache();
Expand Down

0 comments on commit e3389c8

Please sign in to comment.