Commit | Line | Data |
---|---|---|
8139ad13 SH |
1 | <?php |
2 | // This file is part of Moodle - http://moodle.org/ | |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | /** | |
18 | * The supplementary cache API. | |
19 | * | |
20 | * This file is part of Moodle's cache API, affectionately called MUC. | |
21 | * It contains elements of the API that are not required in order to use caching. | |
22 | * Things in here are more in line with administration and management of the cache setup and configuration. | |
23 | * | |
24 | * @package core | |
25 | * @category cache | |
26 | * @copyright 2012 Sam Hemelryk | |
27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
28 | */ | |
29 | ||
30 | defined('MOODLE_INTERNAL') || die(); | |
31 | ||
32 | /** | |
33 | * Cache configuration writer. | |
34 | * | |
35 | * This class should only be used when you need to write to the config, all read operations exist within the cache_config. | |
36 | * | |
37 | * @package core | |
38 | * @category cache | |
39 | * @copyright 2012 Sam Hemelryk | |
40 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
41 | */ | |
42 | class cache_config_writer extends cache_config { | |
43 | ||
7383a7e2 SH |
44 | /** |
45 | * Switch that gets set to true when ever a cache_config_writer instance is saving the cache configuration file. | |
46 | * If this is set to true when save is next called we must avoid the trying to save and instead return the | |
47 | * generated config so that is may be used instead of the file. | |
48 | * @var bool | |
49 | */ | |
50 | protected static $creatingconfig = false; | |
51 | ||
8139ad13 SH |
52 | /** |
53 | * Returns an instance of the configuration writer. | |
54 | * | |
55 | * @return cache_config_writer | |
56 | */ | |
57 | public static function instance() { | |
58 | $factory = cache_factory::instance(); | |
59 | return $factory->create_config_instance(true); | |
60 | } | |
61 | ||
62 | /** | |
63 | * Saves the current configuration. | |
7383a7e2 SH |
64 | * |
65 | * Exceptions within this function are tolerated but must be of type cache_exception. | |
66 | * They are caught during initialisation and written to the error log. This is required in order to avoid | |
67 | * infinite loop situations caused by the cache throwing exceptions during its initialisation. | |
8139ad13 SH |
68 | */ |
69 | protected function config_save() { | |
70 | global $CFG; | |
79a8ea65 | 71 | $cachefile = static::get_config_file_path(); |
8139ad13 SH |
72 | $directory = dirname($cachefile); |
73 | if ($directory !== $CFG->dataroot && !file_exists($directory)) { | |
74 | $result = make_writable_directory($directory, false); | |
75 | if (!$result) { | |
7383a7e2 | 76 | throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Cannot create config directory. Check the permissions on your moodledata directory.'); |
8139ad13 SH |
77 | } |
78 | } | |
79 | if (!file_exists($directory) || !is_writable($directory)) { | |
7383a7e2 | 80 | throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Config directory is not writable. Check the permissions on the moodledata/muc directory.'); |
8139ad13 SH |
81 | } |
82 | ||
83 | // Prepare a configuration array to store. | |
7383a7e2 | 84 | $configuration = $this->generate_configuration_array(); |
8139ad13 SH |
85 | |
86 | // Prepare the file content. | |
87 | $content = "<?php defined('MOODLE_INTERNAL') || die();\n \$configuration = ".var_export($configuration, true).";"; | |
88 | ||
34c84c72 SH |
89 | // We need to create a temporary cache lock instance for use here. Remember we are generating the config file |
90 | // it doesn't exist and thus we can't use the normal API for this (it'll just try to use config). | |
573a6c8e SH |
91 | $lockconf = reset($this->configlocks); |
92 | if ($lockconf === false) { | |
93 | debugging('Your cache configuration file is out of date and needs to be refreshed.', DEBUG_DEVELOPER); | |
94 | // Use the default | |
95 | $lockconf = array( | |
96 | 'name' => 'cachelock_file_default', | |
97 | 'type' => 'cachelock_file', | |
98 | 'dir' => 'filelocks', | |
99 | 'default' => true | |
100 | ); | |
101 | } | |
34c84c72 | 102 | $factory = cache_factory::instance(); |
573a6c8e | 103 | $locking = $factory->create_lock_instance($lockconf); |
34c84c72 SH |
104 | if ($locking->lock('configwrite', 'config', true)) { |
105 | // Its safe to use w mode here because we have already acquired the lock. | |
8139ad13 SH |
106 | $handle = fopen($cachefile, 'w'); |
107 | fwrite($handle, $content); | |
108 | fflush($handle); | |
109 | fclose($handle); | |
34c84c72 | 110 | $locking->unlock('configwrite', 'config'); |
eb459f71 | 111 | @chmod($cachefile, $CFG->filepermissions); |
c05a5099 PS |
112 | // Tell PHP to recompile the script. |
113 | core_component::invalidate_opcode_php_cache($cachefile); | |
8139ad13 SH |
114 | } else { |
115 | throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Unable to open the cache config file.'); | |
116 | } | |
117 | } | |
118 | ||
7383a7e2 SH |
119 | /** |
120 | * Generates a configuration array suitable to be written to the config file. | |
121 | * @return array | |
122 | */ | |
123 | protected function generate_configuration_array() { | |
124 | $configuration = array(); | |
7866b094 | 125 | $configuration['siteidentifier'] = $this->siteidentifier; |
7383a7e2 SH |
126 | $configuration['stores'] = $this->configstores; |
127 | $configuration['modemappings'] = $this->configmodemappings; | |
128 | $configuration['definitions'] = $this->configdefinitions; | |
129 | $configuration['definitionmappings'] = $this->configdefinitionmappings; | |
130 | $configuration['locks'] = $this->configlocks; | |
131 | return $configuration; | |
132 | } | |
133 | ||
8139ad13 SH |
134 | /** |
135 | * Adds a plugin instance. | |
136 | * | |
137 | * This function also calls save so you should redirect immediately, or at least very shortly after | |
138 | * calling this method. | |
139 | * | |
140 | * @param string $name The name for the instance (must be unique) | |
141 | * @param string $plugin The name of the plugin. | |
142 | * @param array $configuration The configuration data for the plugin instance. | |
143 | * @return bool | |
144 | * @throws cache_exception | |
145 | */ | |
26ce56fd | 146 | public function add_store_instance($name, $plugin, array $configuration = array()) { |
8139ad13 SH |
147 | if (array_key_exists($name, $this->configstores)) { |
148 | throw new cache_exception('Duplicate name specificed for cache plugin instance. You must provide a unique name.'); | |
149 | } | |
6fec1820 | 150 | $class = 'cachestore_'.$plugin; |
8139ad13 | 151 | if (!class_exists($class)) { |
d0cac8b5 | 152 | $plugins = core_component::get_plugin_list_with_file('cachestore', 'lib.php'); |
8139ad13 | 153 | if (!array_key_exists($plugin, $plugins)) { |
170f821b | 154 | throw new cache_exception('Invalid plugin name specified. The plugin does not exist or is not valid.'); |
8139ad13 SH |
155 | } |
156 | $file = $plugins[$plugin]; | |
157 | if (file_exists($file)) { | |
158 | require_once($file); | |
159 | } | |
160 | if (!class_exists($class)) { | |
161 | throw new cache_exception('Invalid cache plugin specified. The plugin does not contain the required class.'); | |
162 | } | |
163 | } | |
d4797177 | 164 | $reflection = new ReflectionClass($class); |
75cde6b9 | 165 | if (!$reflection->isSubclassOf('cache_store')) { |
8139ad13 SH |
166 | throw new cache_exception('Invalid cache plugin specified. The plugin does not extend the required class.'); |
167 | } | |
168 | if (!$class::are_requirements_met()) { | |
169 | throw new cache_exception('Unable to add new cache plugin instance. The requested plugin type is not supported.'); | |
170 | } | |
171 | $this->configstores[$name] = array( | |
172 | 'name' => $name, | |
173 | 'plugin' => $plugin, | |
174 | 'configuration' => $configuration, | |
175 | 'features' => $class::get_supported_features($configuration), | |
176 | 'modes' => $class::get_supported_modes($configuration), | |
42f2c59e SH |
177 | 'mappingsonly' => !empty($configuration['mappingsonly']), |
178 | 'class' => $class, | |
179 | 'default' => false | |
8139ad13 | 180 | ); |
167ad91e SH |
181 | if (array_key_exists('lock', $configuration)) { |
182 | $this->configstores[$name]['lock'] = $configuration['lock']; | |
183 | unset($this->configstores[$name]['configuration']['lock']); | |
184 | } | |
730cf5ac MS |
185 | // Call instance_created() |
186 | $store = new $class($name, $this->configstores[$name]['configuration']); | |
187 | $store->instance_created(); | |
188 | ||
8139ad13 SH |
189 | $this->config_save(); |
190 | return true; | |
191 | } | |
192 | ||
acf49f4b SH |
193 | /** |
194 | * Adds a new lock instance to the config file. | |
195 | * | |
196 | * @param string $name The name the user gave the instance. PARAM_ALHPANUMEXT | |
197 | * @param string $plugin The plugin we are creating an instance of. | |
198 | * @param string $configuration Configuration data from the config instance. | |
199 | * @throws cache_exception | |
200 | */ | |
201 | public function add_lock_instance($name, $plugin, $configuration = array()) { | |
202 | if (array_key_exists($name, $this->configlocks)) { | |
203 | throw new cache_exception('Duplicate name specificed for cache lock instance. You must provide a unique name.'); | |
204 | } | |
205 | $class = 'cachelock_'.$plugin; | |
206 | if (!class_exists($class)) { | |
d0cac8b5 | 207 | $plugins = core_component::get_plugin_list_with_file('cachelock', 'lib.php'); |
acf49f4b SH |
208 | if (!array_key_exists($plugin, $plugins)) { |
209 | throw new cache_exception('Invalid lock name specified. The plugin does not exist or is not valid.'); | |
210 | } | |
211 | $file = $plugins[$plugin]; | |
212 | if (file_exists($file)) { | |
213 | require_once($file); | |
214 | } | |
215 | if (!class_exists($class)) { | |
216 | throw new cache_exception('Invalid lock plugin specified. The plugin does not contain the required class.'); | |
217 | } | |
218 | } | |
219 | $reflection = new ReflectionClass($class); | |
220 | if (!$reflection->implementsInterface('cache_lock_interface')) { | |
221 | throw new cache_exception('Invalid lock plugin specified. The plugin does not implement the required interface.'); | |
222 | } | |
223 | $this->configlocks[$name] = array_merge($configuration, array( | |
224 | 'name' => $name, | |
225 | 'type' => 'cachelock_'.$plugin, | |
226 | 'default' => false | |
227 | )); | |
228 | $this->config_save(); | |
229 | } | |
230 | ||
231 | /** | |
232 | * Deletes a lock instance given its name. | |
233 | * | |
234 | * @param string $name The name of the plugin, PARAM_ALPHANUMEXT. | |
235 | * @return bool | |
236 | * @throws cache_exception | |
237 | */ | |
238 | public function delete_lock_instance($name) { | |
239 | if (!array_key_exists($name, $this->configlocks)) { | |
240 | throw new cache_exception('The requested store does not exist.'); | |
241 | } | |
242 | if ($this->configlocks[$name]['default']) { | |
243 | throw new cache_exception('You can not delete the default lock.'); | |
244 | } | |
245 | foreach ($this->configstores as $store) { | |
246 | if (isset($store['lock']) && $store['lock'] === $name) { | |
247 | throw new cache_exception('You cannot delete a cache lock that is being used by a store.'); | |
248 | } | |
249 | } | |
250 | unset($this->configlocks[$name]); | |
251 | $this->config_save(); | |
252 | return true; | |
253 | } | |
254 | ||
8139ad13 SH |
255 | /** |
256 | * Sets the mode mappings. | |
257 | * | |
258 | * These determine the default caches for the different modes. | |
259 | * This function also calls save so you should redirect immediately, or at least very shortly after | |
260 | * calling this method. | |
261 | * | |
262 | * @param array $modemappings | |
263 | * @return bool | |
264 | * @throws cache_exception | |
265 | */ | |
266 | public function set_mode_mappings(array $modemappings) { | |
267 | $mappings = array( | |
268 | cache_store::MODE_APPLICATION => array(), | |
269 | cache_store::MODE_SESSION => array(), | |
270 | cache_store::MODE_REQUEST => array(), | |
271 | ); | |
272 | foreach ($modemappings as $mode => $stores) { | |
273 | if (!array_key_exists($mode, $mappings)) { | |
274 | throw new cache_exception('The cache mode for the new mapping does not exist'); | |
275 | } | |
276 | $sort = 0; | |
277 | foreach ($stores as $store) { | |
278 | if (!array_key_exists($store, $this->configstores)) { | |
279 | throw new cache_exception('The instance name for the new mapping does not exist'); | |
280 | } | |
281 | if (array_key_exists($store, $mappings[$mode])) { | |
282 | throw new cache_exception('This cache mapping already exists'); | |
283 | } | |
284 | $mappings[$mode][] = array( | |
285 | 'store' => $store, | |
286 | 'mode' => $mode, | |
287 | 'sort' => $sort++ | |
288 | ); | |
289 | } | |
290 | } | |
291 | $this->configmodemappings = array_merge( | |
292 | $mappings[cache_store::MODE_APPLICATION], | |
293 | $mappings[cache_store::MODE_SESSION], | |
294 | $mappings[cache_store::MODE_REQUEST] | |
295 | ); | |
296 | ||
297 | $this->config_save(); | |
298 | return true; | |
299 | } | |
300 | ||
301 | /** | |
302 | * Edits a give plugin instance. | |
303 | * | |
42f2c59e | 304 | * The plugin instance is determined by its name, hence you cannot rename plugins. |
8139ad13 SH |
305 | * This function also calls save so you should redirect immediately, or at least very shortly after |
306 | * calling this method. | |
307 | * | |
308 | * @param string $name | |
309 | * @param string $plugin | |
310 | * @param array $configuration | |
311 | * @return bool | |
312 | * @throws cache_exception | |
313 | */ | |
26ce56fd | 314 | public function edit_store_instance($name, $plugin, $configuration) { |
8139ad13 SH |
315 | if (!array_key_exists($name, $this->configstores)) { |
316 | throw new cache_exception('The requested instance does not exist.'); | |
317 | } | |
d0cac8b5 | 318 | $plugins = core_component::get_plugin_list_with_file('cachestore', 'lib.php'); |
8139ad13 SH |
319 | if (!array_key_exists($plugin, $plugins)) { |
320 | throw new cache_exception('Invalid plugin name specified. The plugin either does not exist or is not valid.'); | |
321 | } | |
42f2c59e | 322 | $class = 'cachestore_'.$plugin; |
8139ad13 SH |
323 | $file = $plugins[$plugin]; |
324 | if (!class_exists($class)) { | |
325 | if (file_exists($file)) { | |
326 | require_once($file); | |
327 | } | |
328 | if (!class_exists($class)) { | |
42f2c59e | 329 | throw new cache_exception('Invalid cache plugin specified. The plugin does not contain the required class.'.$class); |
8139ad13 SH |
330 | } |
331 | } | |
332 | $this->configstores[$name] = array( | |
333 | 'name' => $name, | |
334 | 'plugin' => $plugin, | |
335 | 'configuration' => $configuration, | |
336 | 'features' => $class::get_supported_features($configuration), | |
337 | 'modes' => $class::get_supported_modes($configuration), | |
42f2c59e SH |
338 | 'mappingsonly' => !empty($configuration['mappingsonly']), |
339 | 'class' => $class, | |
340 | 'default' => $this->configstores[$name]['default'] // Can't change the default. | |
8139ad13 | 341 | ); |
167ad91e SH |
342 | if (array_key_exists('lock', $configuration)) { |
343 | $this->configstores[$name]['lock'] = $configuration['lock']; | |
344 | unset($this->configstores[$name]['configuration']['lock']); | |
345 | } | |
8139ad13 SH |
346 | $this->config_save(); |
347 | return true; | |
348 | } | |
349 | ||
350 | /** | |
351 | * Deletes a store instance. | |
352 | * | |
353 | * This function also calls save so you should redirect immediately, or at least very shortly after | |
354 | * calling this method. | |
355 | * | |
356 | * @param string $name The name of the instance to delete. | |
357 | * @return bool | |
358 | * @throws cache_exception | |
359 | */ | |
26ce56fd | 360 | public function delete_store_instance($name) { |
8139ad13 SH |
361 | if (!array_key_exists($name, $this->configstores)) { |
362 | throw new cache_exception('The requested store does not exist.'); | |
363 | } | |
364 | if ($this->configstores[$name]['default']) { | |
365 | throw new cache_exception('The can not delete the default stores.'); | |
366 | } | |
367 | foreach ($this->configmodemappings as $mapping) { | |
368 | if ($mapping['store'] === $name) { | |
369 | throw new cache_exception('You cannot delete a cache store that has mode mappings.'); | |
370 | } | |
371 | } | |
372 | foreach ($this->configdefinitionmappings as $mapping) { | |
373 | if ($mapping['store'] === $name) { | |
374 | throw new cache_exception('You cannot delete a cache store that has definition mappings.'); | |
375 | } | |
376 | } | |
a037c943 | 377 | |
730cf5ac MS |
378 | // Call instance_deleted() |
379 | $class = 'cachestore_'.$this->configstores[$name]['plugin']; | |
380 | $store = new $class($name, $this->configstores[$name]['configuration']); | |
381 | $store->instance_deleted(); | |
382 | ||
8139ad13 SH |
383 | unset($this->configstores[$name]); |
384 | $this->config_save(); | |
385 | return true; | |
386 | } | |
387 | ||
388 | /** | |
389 | * Creates the default configuration and saves it. | |
390 | * | |
391 | * This function calls config_save, however it is safe to continue using it afterwards as this function should only ever | |
392 | * be called when there is no configuration file already. | |
7383a7e2 | 393 | * |
b0dd08dd | 394 | * @param bool $forcesave If set to true then we will forcefully save the default configuration file. |
7383a7e2 SH |
395 | * @return true|array Returns true if the default configuration was successfully created. |
396 | * Returns a configuration array if it could not be saved. This is a bad situation. Check your error logs. | |
8139ad13 | 397 | */ |
b0dd08dd | 398 | public static function create_default_configuration($forcesave = false) { |
8139ad13 SH |
399 | // HACK ALERT. |
400 | // We probably need to come up with a better way to create the default stores, or at least ensure 100% that the | |
401 | // default store plugins are protected from deletion. | |
8139ad13 | 402 | $writer = new self; |
fbf76dcd | 403 | $writer->configstores = self::get_default_stores(); |
8139ad13 SH |
404 | $writer->configdefinitions = self::locate_definitions(); |
405 | $writer->configmodemappings = array( | |
406 | array( | |
407 | 'mode' => cache_store::MODE_APPLICATION, | |
408 | 'store' => 'default_application', | |
409 | 'sort' => -1 | |
410 | ), | |
411 | array( | |
412 | 'mode' => cache_store::MODE_SESSION, | |
413 | 'store' => 'default_session', | |
414 | 'sort' => -1 | |
415 | ), | |
416 | array( | |
417 | 'mode' => cache_store::MODE_REQUEST, | |
418 | 'store' => 'default_request', | |
419 | 'sort' => -1 | |
420 | ) | |
421 | ); | |
34c84c72 SH |
422 | $writer->configlocks = array( |
423 | 'default_file_lock' => array( | |
167ad91e | 424 | 'name' => 'cachelock_file_default', |
34c84c72 | 425 | 'type' => 'cachelock_file', |
167ad91e SH |
426 | 'dir' => 'filelocks', |
427 | 'default' => true | |
34c84c72 SH |
428 | ) |
429 | ); | |
7383a7e2 SH |
430 | |
431 | $factory = cache_factory::instance(); | |
432 | // We expect the cache to be initialising presently. If its not then something has gone wrong and likely | |
433 | // we are now in a loop. | |
b0dd08dd | 434 | if (!$forcesave && $factory->get_state() !== cache_factory::STATE_INITIALISING) { |
7383a7e2 SH |
435 | return $writer->generate_configuration_array(); |
436 | } | |
437 | $factory->set_state(cache_factory::STATE_SAVING); | |
8139ad13 | 438 | $writer->config_save(); |
7383a7e2 | 439 | return true; |
8139ad13 SH |
440 | } |
441 | ||
fbf76dcd SH |
442 | /** |
443 | * Returns an array of default stores for use. | |
444 | * | |
445 | * @return array | |
446 | */ | |
447 | protected static function get_default_stores() { | |
969eaa3e DW |
448 | global $CFG; |
449 | ||
450 | require_once($CFG->dirroot.'/cache/stores/file/lib.php'); | |
451 | require_once($CFG->dirroot.'/cache/stores/session/lib.php'); | |
452 | require_once($CFG->dirroot.'/cache/stores/static/lib.php'); | |
453 | ||
fbf76dcd SH |
454 | return array( |
455 | 'default_application' => array( | |
456 | 'name' => 'default_application', | |
457 | 'plugin' => 'file', | |
458 | 'configuration' => array(), | |
459 | 'features' => cachestore_file::get_supported_features(), | |
460 | 'modes' => cachestore_file::get_supported_modes(), | |
461 | 'default' => true, | |
462 | ), | |
463 | 'default_session' => array( | |
464 | 'name' => 'default_session', | |
465 | 'plugin' => 'session', | |
466 | 'configuration' => array(), | |
467 | 'features' => cachestore_session::get_supported_features(), | |
468 | 'modes' => cachestore_session::get_supported_modes(), | |
469 | 'default' => true, | |
470 | ), | |
471 | 'default_request' => array( | |
472 | 'name' => 'default_request', | |
473 | 'plugin' => 'static', | |
474 | 'configuration' => array(), | |
475 | 'features' => cachestore_static::get_supported_features(), | |
476 | 'modes' => cachestore_static::get_supported_modes(), | |
477 | 'default' => true, | |
478 | ) | |
479 | ); | |
480 | } | |
481 | ||
482 | /** | |
483 | * Updates the default stores within the MUC config file. | |
484 | */ | |
485 | public static function update_default_config_stores() { | |
486 | $factory = cache_factory::instance(); | |
487 | $factory->updating_started(); | |
488 | $config = $factory->create_config_instance(true); | |
489 | $config->configstores = array_merge($config->configstores, self::get_default_stores()); | |
490 | $config->config_save(); | |
491 | $factory->updating_finished(); | |
492 | } | |
493 | ||
8139ad13 SH |
494 | /** |
495 | * Updates the definition in the configuration from those found in the cache files. | |
496 | * | |
497 | * Calls config_save further down, you should redirect immediately or asap after calling this method. | |
75af47ee SH |
498 | * |
499 | * @param bool $coreonly If set to true only core definitions will be updated. | |
8139ad13 | 500 | */ |
75af47ee | 501 | public static function update_definitions($coreonly = false) { |
9890ecfc SH |
502 | $factory = cache_factory::instance(); |
503 | $factory->updating_started(); | |
504 | $config = $factory->create_config_instance(true); | |
75af47ee | 505 | $config->write_definitions_to_cache(self::locate_definitions($coreonly)); |
9890ecfc | 506 | $factory->updating_finished(); |
8139ad13 SH |
507 | } |
508 | ||
509 | /** | |
510 | * Locates all of the definition files. | |
511 | * | |
75af47ee | 512 | * @param bool $coreonly If set to true only core definitions will be updated. |
8139ad13 SH |
513 | * @return array |
514 | */ | |
75af47ee | 515 | protected static function locate_definitions($coreonly = false) { |
8139ad13 SH |
516 | global $CFG; |
517 | ||
518 | $files = array(); | |
519 | if (file_exists($CFG->dirroot.'/lib/db/caches.php')) { | |
520 | $files['core'] = $CFG->dirroot.'/lib/db/caches.php'; | |
521 | } | |
522 | ||
75af47ee | 523 | if (!$coreonly) { |
46f6f7f2 | 524 | $plugintypes = core_component::get_plugin_types(); |
75af47ee | 525 | foreach ($plugintypes as $type => $location) { |
d0cac8b5 | 526 | $plugins = core_component::get_plugin_list_with_file($type, 'db/caches.php'); |
75af47ee SH |
527 | foreach ($plugins as $plugin => $filepath) { |
528 | $component = clean_param($type.'_'.$plugin, PARAM_COMPONENT); // Standardised plugin name. | |
529 | $files[$component] = $filepath; | |
530 | } | |
8139ad13 SH |
531 | } |
532 | } | |
533 | ||
534 | $definitions = array(); | |
535 | foreach ($files as $component => $file) { | |
536 | $filedefs = self::load_caches_file($file); | |
537 | foreach ($filedefs as $area => $definition) { | |
538 | $area = clean_param($area, PARAM_AREA); | |
539 | $id = $component.'/'.$area; | |
540 | $definition['component'] = $component; | |
541 | $definition['area'] = $area; | |
542 | if (array_key_exists($id, $definitions)) { | |
d4797177 | 543 | debugging('Error: duplicate cache definition found with id: '.$id, DEBUG_DEVELOPER); |
8139ad13 SH |
544 | continue; |
545 | } | |
546 | $definitions[$id] = $definition; | |
547 | } | |
548 | } | |
549 | ||
550 | return $definitions; | |
551 | } | |
552 | ||
553 | /** | |
554 | * Writes the updated definitions for the config file. | |
555 | * @param array $definitions | |
556 | */ | |
557 | private function write_definitions_to_cache(array $definitions) { | |
46e17f04 SH |
558 | |
559 | // Preserve the selected sharing option when updating the definitions. | |
560 | // This is set by the user and should never come from caches.php. | |
561 | foreach ($definitions as $key => $definition) { | |
562 | unset($definitions[$key]['selectedsharingoption']); | |
563 | unset($definitions[$key]['userinputsharingkey']); | |
564 | if (isset($this->configdefinitions[$key]) && isset($this->configdefinitions[$key]['selectedsharingoption'])) { | |
565 | $definitions[$key]['selectedsharingoption'] = $this->configdefinitions[$key]['selectedsharingoption']; | |
566 | } | |
567 | if (isset($this->configdefinitions[$key]) && isset($this->configdefinitions[$key]['userinputsharingkey'])) { | |
568 | $definitions[$key]['userinputsharingkey'] = $this->configdefinitions[$key]['userinputsharingkey']; | |
569 | } | |
570 | } | |
571 | ||
8139ad13 SH |
572 | $this->configdefinitions = $definitions; |
573 | foreach ($this->configdefinitionmappings as $key => $mapping) { | |
574 | if (!array_key_exists($mapping['definition'], $definitions)) { | |
575 | unset($this->configdefinitionmappings[$key]); | |
576 | } | |
577 | } | |
578 | $this->config_save(); | |
579 | } | |
580 | ||
581 | /** | |
582 | * Loads the caches file if it exists. | |
583 | * @param string $file Absolute path to the file. | |
584 | * @return array | |
585 | */ | |
586 | private static function load_caches_file($file) { | |
587 | if (!file_exists($file)) { | |
588 | return array(); | |
589 | } | |
590 | $definitions = array(); | |
170f821b | 591 | include($file); |
8139ad13 SH |
592 | return $definitions; |
593 | } | |
594 | ||
595 | /** | |
596 | * Sets the mappings for a given definition. | |
597 | * | |
598 | * @param string $definition | |
599 | * @param array $mappings | |
600 | * @throws coding_exception | |
601 | */ | |
602 | public function set_definition_mappings($definition, $mappings) { | |
603 | if (!array_key_exists($definition, $this->configdefinitions)) { | |
604 | throw new coding_exception('Invalid definition name passed when updating mappings.'); | |
605 | } | |
606 | foreach ($mappings as $store) { | |
607 | if (!array_key_exists($store, $this->configstores)) { | |
608 | throw new coding_exception('Invalid store name passed when updating definition mappings.'); | |
609 | } | |
610 | } | |
611 | foreach ($this->configdefinitionmappings as $key => $mapping) { | |
612 | if ($mapping['definition'] == $definition) { | |
613 | unset($this->configdefinitionmappings[$key]); | |
614 | } | |
615 | } | |
616 | $sort = count($mappings); | |
617 | foreach ($mappings as $store) { | |
618 | $this->configdefinitionmappings[] = array( | |
619 | 'store' => $store, | |
620 | 'definition' => $definition, | |
621 | 'sort' => $sort | |
622 | ); | |
623 | $sort--; | |
624 | } | |
625 | ||
626 | $this->config_save(); | |
627 | } | |
628 | ||
e0d9b7c0 SH |
629 | /** |
630 | * Update the site identifier stored by the cache API. | |
631 | * | |
632 | * @param string $siteidentifier | |
fe86ebfa | 633 | * @return string The new site identifier. |
e0d9b7c0 SH |
634 | */ |
635 | public function update_site_identifier($siteidentifier) { | |
636 | $this->siteidentifier = md5((string)$siteidentifier); | |
637 | $this->config_save(); | |
fe86ebfa | 638 | return $this->siteidentifier; |
e0d9b7c0 | 639 | } |
46e17f04 SH |
640 | |
641 | /** | |
642 | * Sets the selected sharing options and key for a definition. | |
643 | * | |
644 | * @param string $definition The name of the definition to set for. | |
645 | * @param int $sharingoption The sharing option to set. | |
646 | * @param string|null $userinputsharingkey The user input key or null. | |
647 | * @throws coding_exception | |
648 | */ | |
649 | public function set_definition_sharing($definition, $sharingoption, $userinputsharingkey = null) { | |
650 | if (!array_key_exists($definition, $this->configdefinitions)) { | |
651 | throw new coding_exception('Invalid definition name passed when updating sharing options.'); | |
652 | } | |
653 | if (!($this->configdefinitions[$definition]['sharingoptions'] & $sharingoption)) { | |
654 | throw new coding_exception('Invalid sharing option passed when updating definition.'); | |
655 | } | |
656 | $this->configdefinitions[$definition]['selectedsharingoption'] = (int)$sharingoption; | |
657 | if (!empty($userinputsharingkey)) { | |
658 | $this->configdefinitions[$definition]['userinputsharingkey'] = (string)$userinputsharingkey; | |
659 | } | |
660 | $this->config_save(); | |
661 | } | |
662 | ||
8139ad13 SH |
663 | } |
664 | ||
665 | /** | |
666 | * A cache helper for administration tasks | |
667 | * | |
668 | * @package core | |
669 | * @category cache | |
670 | * @copyright 2012 Sam Hemelryk | |
671 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
672 | */ | |
673 | abstract class cache_administration_helper extends cache_helper { | |
674 | ||
675 | /** | |
676 | * Returns an array containing all of the information about stores a renderer needs. | |
677 | * @return array | |
678 | */ | |
26ce56fd | 679 | public static function get_store_instance_summaries() { |
8139ad13 SH |
680 | $return = array(); |
681 | $default = array(); | |
682 | $instance = cache_config::instance(); | |
683 | $stores = $instance->get_all_stores(); | |
acf49f4b | 684 | $locks = $instance->get_locks(); |
8139ad13 SH |
685 | foreach ($stores as $name => $details) { |
686 | $class = $details['class']; | |
f9599c77 RS |
687 | $store = false; |
688 | if ($class::are_requirements_met()) { | |
689 | $store = new $class($details['name'], $details['configuration']); | |
690 | } | |
acf49f4b | 691 | $lock = (isset($details['lock'])) ? $locks[$details['lock']] : $instance->get_default_lock(); |
8139ad13 SH |
692 | $record = array( |
693 | 'name' => $name, | |
694 | 'plugin' => $details['plugin'], | |
695 | 'default' => $details['default'], | |
f9599c77 | 696 | 'isready' => $store ? $store->is_ready() : false, |
220f1cba | 697 | 'requirementsmet' => $class::are_requirements_met(), |
8139ad13 | 698 | 'mappings' => 0, |
acf49f4b | 699 | 'lock' => $lock, |
8139ad13 SH |
700 | 'modes' => array( |
701 | cache_store::MODE_APPLICATION => | |
f9599c77 | 702 | ($class::get_supported_modes($return) & cache_store::MODE_APPLICATION) == cache_store::MODE_APPLICATION, |
8139ad13 | 703 | cache_store::MODE_SESSION => |
f9599c77 | 704 | ($class::get_supported_modes($return) & cache_store::MODE_SESSION) == cache_store::MODE_SESSION, |
8139ad13 | 705 | cache_store::MODE_REQUEST => |
f9599c77 | 706 | ($class::get_supported_modes($return) & cache_store::MODE_REQUEST) == cache_store::MODE_REQUEST, |
8139ad13 SH |
707 | ), |
708 | 'supports' => array( | |
f9599c77 RS |
709 | 'multipleidentifiers' => $store ? $store->supports_multiple_identifiers() : false, |
710 | 'dataguarantee' => $store ? $store->supports_data_guarantee() : false, | |
711 | 'nativettl' => $store ? $store->supports_native_ttl() : false, | |
8139ad13 SH |
712 | 'nativelocking' => ($store instanceof cache_is_lockable), |
713 | 'keyawareness' => ($store instanceof cache_is_key_aware), | |
dbd2ea4e | 714 | 'searchable' => ($store instanceof cache_is_searchable) |
727c2173 | 715 | ), |
f9599c77 | 716 | 'warnings' => $store ? $store->get_warnings() : array() |
8139ad13 SH |
717 | ); |
718 | if (empty($details['default'])) { | |
719 | $return[$name] = $record; | |
720 | } else { | |
721 | $default[$name] = $record; | |
722 | } | |
723 | } | |
724 | ||
725 | ksort($return); | |
726 | ksort($default); | |
727 | $return = $return + $default; | |
728 | ||
8139ad13 SH |
729 | foreach ($instance->get_definition_mappings() as $mapping) { |
730 | if (!array_key_exists($mapping['store'], $return)) { | |
731 | continue; | |
732 | } | |
733 | $return[$mapping['store']]['mappings']++; | |
734 | } | |
735 | ||
736 | return $return; | |
737 | } | |
738 | ||
739 | /** | |
740 | * Returns an array of information about plugins, everything a renderer needs. | |
1baf3af3 TH |
741 | * |
742 | * @return array for each store, an array containing various information about each store. | |
743 | * See the code below for details | |
8139ad13 | 744 | */ |
26ce56fd | 745 | public static function get_store_plugin_summaries() { |
8139ad13 | 746 | $return = array(); |
d0cac8b5 | 747 | $plugins = core_component::get_plugin_list_with_file('cachestore', 'lib.php', true); |
8139ad13 | 748 | foreach ($plugins as $plugin => $path) { |
6fec1820 | 749 | $class = 'cachestore_'.$plugin; |
8139ad13 | 750 | $return[$plugin] = array( |
6fec1820 | 751 | 'name' => get_string('pluginname', 'cachestore_'.$plugin), |
8139ad13 SH |
752 | 'requirementsmet' => $class::are_requirements_met(), |
753 | 'instances' => 0, | |
754 | 'modes' => array( | |
755 | cache_store::MODE_APPLICATION => ($class::get_supported_modes() & cache_store::MODE_APPLICATION), | |
756 | cache_store::MODE_SESSION => ($class::get_supported_modes() & cache_store::MODE_SESSION), | |
757 | cache_store::MODE_REQUEST => ($class::get_supported_modes() & cache_store::MODE_REQUEST), | |
758 | ), | |
759 | 'supports' => array( | |
760 | 'multipleidentifiers' => ($class::get_supported_features() & cache_store::SUPPORTS_MULTIPLE_IDENTIFIERS), | |
761 | 'dataguarantee' => ($class::get_supported_features() & cache_store::SUPPORTS_DATA_GUARANTEE), | |
762 | 'nativettl' => ($class::get_supported_features() & cache_store::SUPPORTS_NATIVE_TTL), | |
763 | 'nativelocking' => (in_array('cache_is_lockable', class_implements($class))), | |
764 | 'keyawareness' => (array_key_exists('cache_is_key_aware', class_implements($class))), | |
765 | ), | |
7e7e108f | 766 | 'canaddinstance' => ($class::can_add_instance() && $class::are_requirements_met()) |
8139ad13 SH |
767 | ); |
768 | } | |
769 | ||
770 | $instance = cache_config::instance(); | |
771 | $stores = $instance->get_all_stores(); | |
772 | foreach ($stores as $store) { | |
773 | $plugin = $store['plugin']; | |
774 | if (array_key_exists($plugin, $return)) { | |
775 | $return[$plugin]['instances']++; | |
776 | } | |
777 | } | |
778 | ||
779 | return $return; | |
780 | } | |
781 | ||
782 | /** | |
783 | * Returns an array about the definitions. All the information a renderer needs. | |
1baf3af3 TH |
784 | * |
785 | * @return array for each store, an array containing various information about each store. | |
786 | * See the code below for details | |
8139ad13 SH |
787 | */ |
788 | public static function get_definition_summaries() { | |
e28a9539 SH |
789 | $factory = cache_factory::instance(); |
790 | $config = $factory->create_config_instance(); | |
8139ad13 | 791 | $storenames = array(); |
e28a9539 | 792 | foreach ($config->get_all_stores() as $key => $store) { |
8139ad13 SH |
793 | if (!empty($store['default'])) { |
794 | $storenames[$key] = new lang_string('store_'.$key, 'cache'); | |
8139ad13 | 795 | } else { |
e28a9539 | 796 | $storenames[$store['name']] = $store['name']; |
8139ad13 SH |
797 | } |
798 | } | |
e28a9539 SH |
799 | /* @var cache_definition[] $definitions */ |
800 | $definitions = array(); | |
801 | foreach ($config->get_definitions() as $key => $definition) { | |
802 | $definitions[$key] = cache_definition::load($definition['component'].'/'.$definition['area'], $definition); | |
8139ad13 | 803 | } |
8139ad13 | 804 | foreach ($definitions as $id => $definition) { |
8139ad13 | 805 | $mappings = array(); |
e28a9539 SH |
806 | foreach (cache_helper::get_stores_suitable_for_definition($definition) as $store) { |
807 | $mappings[] = $storenames[$store->my_name()]; | |
8139ad13 | 808 | } |
8139ad13 SH |
809 | $return[$id] = array( |
810 | 'id' => $id, | |
e28a9539 SH |
811 | 'name' => $definition->get_name(), |
812 | 'mode' => $definition->get_mode(), | |
813 | 'component' => $definition->get_component(), | |
814 | 'area' => $definition->get_area(), | |
46e17f04 | 815 | 'mappings' => $mappings, |
4350192a | 816 | 'canuselocalstore' => $definition->can_use_localstore(), |
e28a9539 SH |
817 | 'sharingoptions' => self::get_definition_sharing_options($definition->get_sharing_options(), false), |
818 | 'selectedsharingoption' => self::get_definition_sharing_options($definition->get_selected_sharing_option(), true), | |
819 | 'userinputsharingkey' => $definition->get_user_input_sharing_key() | |
8139ad13 SH |
820 | ); |
821 | } | |
822 | return $return; | |
823 | } | |
824 | ||
46e17f04 SH |
825 | /** |
826 | * Given a sharing option hash this function returns an array of strings that can be used to describe it. | |
827 | * | |
828 | * @param int $sharingoption The sharing option hash to get strings for. | |
829 | * @param bool $isselectedoptions Set to true if the strings will be used to view the selected options. | |
830 | * @return array An array of lang_string's. | |
831 | */ | |
832 | public static function get_definition_sharing_options($sharingoption, $isselectedoptions = true) { | |
833 | $options = array(); | |
834 | $prefix = ($isselectedoptions) ? 'sharingselected' : 'sharing'; | |
835 | if ($sharingoption & cache_definition::SHARING_ALL) { | |
836 | $options[cache_definition::SHARING_ALL] = new lang_string($prefix.'_all', 'cache'); | |
837 | } | |
838 | if ($sharingoption & cache_definition::SHARING_SITEID) { | |
839 | $options[cache_definition::SHARING_SITEID] = new lang_string($prefix.'_siteid', 'cache'); | |
840 | } | |
841 | if ($sharingoption & cache_definition::SHARING_VERSION) { | |
842 | $options[cache_definition::SHARING_VERSION] = new lang_string($prefix.'_version', 'cache'); | |
843 | } | |
844 | if ($sharingoption & cache_definition::SHARING_INPUT) { | |
845 | $options[cache_definition::SHARING_INPUT] = new lang_string($prefix.'_input', 'cache'); | |
846 | } | |
847 | return $options; | |
848 | } | |
849 | ||
8139ad13 SH |
850 | /** |
851 | * Returns all of the actions that can be performed on a definition. | |
1baf3af3 TH |
852 | * |
853 | * @param context $context the system context. | |
854 | * @param array $definitionsummary information about this cache, from the array returned by | |
855 | * cache_administration_helper::get_definition_summaries(). Currently only 'sharingoptions' | |
856 | * element is used. | |
857 | * @return array of actions. Each action is an array with two elements, 'text' and 'url'. | |
8139ad13 | 858 | */ |
1baf3af3 | 859 | public static function get_definition_actions(context $context, array $definitionsummary) { |
8139ad13 | 860 | if (has_capability('moodle/site:config', $context)) { |
54519330 SH |
861 | $actions = array(); |
862 | // Edit mappings. | |
863 | $actions[] = array( | |
864 | 'text' => get_string('editmappings', 'cache'), | |
865 | 'url' => new moodle_url('/cache/admin.php', array('action' => 'editdefinitionmapping', 'sesskey' => sesskey())) | |
866 | ); | |
867 | // Edit sharing. | |
1baf3af3 | 868 | if (count($definitionsummary['sharingoptions']) > 1) { |
54519330 | 869 | $actions[] = array( |
46e17f04 SH |
870 | 'text' => get_string('editsharing', 'cache'), |
871 | 'url' => new moodle_url('/cache/admin.php', array('action' => 'editdefinitionsharing', 'sesskey' => sesskey())) | |
54519330 SH |
872 | ); |
873 | } | |
874 | // Purge. | |
875 | $actions[] = array( | |
876 | 'text' => get_string('purge', 'cache'), | |
877 | 'url' => new moodle_url('/cache/admin.php', array('action' => 'purgedefinition', 'sesskey' => sesskey())) | |
8139ad13 | 878 | ); |
54519330 | 879 | return $actions; |
8139ad13 SH |
880 | } |
881 | return array(); | |
882 | } | |
883 | ||
884 | /** | |
885 | * Returns all of the actions that can be performed on a store. | |
886 | * | |
887 | * @param string $name The name of the store | |
1baf3af3 TH |
888 | * @param array $storedetails information about this store, from the array returned by |
889 | * cache_administration_helper::get_store_instance_summaries(). | |
890 | * @return array of actions. Each action is an array with two elements, 'text' and 'url'. | |
8139ad13 | 891 | */ |
26ce56fd | 892 | public static function get_store_instance_actions($name, array $storedetails) { |
8139ad13 | 893 | $actions = array(); |
0601e0ee | 894 | if (has_capability('moodle/site:config', context_system::instance())) { |
8139ad13 SH |
895 | $baseurl = new moodle_url('/cache/admin.php', array('store' => $name, 'sesskey' => sesskey())); |
896 | if (empty($storedetails['default'])) { | |
897 | $actions[] = array( | |
898 | 'text' => get_string('editstore', 'cache'), | |
899 | 'url' => new moodle_url($baseurl, array('action' => 'editstore', 'plugin' => $storedetails['plugin'])) | |
900 | ); | |
901 | $actions[] = array( | |
902 | 'text' => get_string('deletestore', 'cache'), | |
903 | 'url' => new moodle_url($baseurl, array('action' => 'deletestore')) | |
904 | ); | |
905 | } | |
906 | $actions[] = array( | |
907 | 'text' => get_string('purge', 'cache'), | |
50523565 | 908 | 'url' => new moodle_url($baseurl, array('action' => 'purgestore')) |
8139ad13 SH |
909 | ); |
910 | } | |
911 | return $actions; | |
912 | } | |
913 | ||
8139ad13 SH |
914 | /** |
915 | * Returns all of the actions that can be performed on a plugin. | |
916 | * | |
917 | * @param string $name The name of the plugin | |
1baf3af3 TH |
918 | * @param array $plugindetails information about this store, from the array returned by |
919 | * cache_administration_helper::get_store_plugin_summaries(). | |
8139ad13 SH |
920 | * @param array $plugindetails |
921 | * @return array | |
922 | */ | |
26ce56fd | 923 | public static function get_store_plugin_actions($name, array $plugindetails) { |
8139ad13 | 924 | $actions = array(); |
7e7e108f | 925 | if (has_capability('moodle/site:config', context_system::instance())) { |
8139ad13 SH |
926 | if (!empty($plugindetails['canaddinstance'])) { |
927 | $url = new moodle_url('/cache/admin.php', array('action' => 'addstore', 'plugin' => $name, 'sesskey' => sesskey())); | |
928 | $actions[] = array( | |
929 | 'text' => get_string('addinstance', 'cache'), | |
930 | 'url' => $url | |
931 | ); | |
932 | } | |
933 | } | |
934 | return $actions; | |
935 | } | |
936 | ||
937 | /** | |
938 | * Returns a form that can be used to add a store instance. | |
939 | * | |
940 | * @param string $plugin The plugin to add an instance of | |
6fec1820 | 941 | * @return cachestore_addinstance_form |
8139ad13 SH |
942 | * @throws coding_exception |
943 | */ | |
944 | public static function get_add_store_form($plugin) { | |
170f821b | 945 | global $CFG; // Needed for includes. |
bd3b3bba | 946 | $plugins = core_component::get_plugin_list('cachestore'); |
42f2c59e SH |
947 | if (!array_key_exists($plugin, $plugins)) { |
948 | throw new coding_exception('Invalid cache plugin used when trying to create an edit form.'); | |
949 | } | |
950 | $plugindir = $plugins[$plugin]; | |
6fec1820 | 951 | $class = 'cachestore_addinstance_form'; |
8139ad13 SH |
952 | if (file_exists($plugindir.'/addinstanceform.php')) { |
953 | require_once($plugindir.'/addinstanceform.php'); | |
6fec1820 SH |
954 | if (class_exists('cachestore_'.$plugin.'_addinstance_form')) { |
955 | $class = 'cachestore_'.$plugin.'_addinstance_form'; | |
956 | if (!array_key_exists('cachestore_addinstance_form', class_parents($class))) { | |
957 | throw new coding_exception('Cache plugin add instance forms must extend cachestore_addinstance_form'); | |
8139ad13 SH |
958 | } |
959 | } | |
960 | } | |
961 | ||
26ce56fd | 962 | $locks = self::get_possible_locks_for_stores($plugindir, $plugin); |
167ad91e | 963 | |
8139ad13 | 964 | $url = new moodle_url('/cache/admin.php', array('action' => 'addstore')); |
167ad91e | 965 | return new $class($url, array('plugin' => $plugin, 'store' => null, 'locks' => $locks)); |
8139ad13 SH |
966 | } |
967 | ||
968 | /** | |
969 | * Returns a form that can be used to edit a store instance. | |
970 | * | |
971 | * @param string $plugin | |
972 | * @param string $store | |
6fec1820 | 973 | * @return cachestore_addinstance_form |
8139ad13 SH |
974 | * @throws coding_exception |
975 | */ | |
976 | public static function get_edit_store_form($plugin, $store) { | |
170f821b | 977 | global $CFG; // Needed for includes. |
bd3b3bba | 978 | $plugins = core_component::get_plugin_list('cachestore'); |
42f2c59e SH |
979 | if (!array_key_exists($plugin, $plugins)) { |
980 | throw new coding_exception('Invalid cache plugin used when trying to create an edit form.'); | |
981 | } | |
982 | $factory = cache_factory::instance(); | |
983 | $config = $factory->create_config_instance(); | |
984 | $stores = $config->get_all_stores(); | |
985 | if (!array_key_exists($store, $stores)) { | |
986 | throw new coding_exception('Invalid store name given when trying to create an edit form.'); | |
987 | } | |
988 | $plugindir = $plugins[$plugin]; | |
6fec1820 | 989 | $class = 'cachestore_addinstance_form'; |
8139ad13 SH |
990 | if (file_exists($plugindir.'/addinstanceform.php')) { |
991 | require_once($plugindir.'/addinstanceform.php'); | |
6fec1820 SH |
992 | if (class_exists('cachestore_'.$plugin.'_addinstance_form')) { |
993 | $class = 'cachestore_'.$plugin.'_addinstance_form'; | |
994 | if (!array_key_exists('cachestore_addinstance_form', class_parents($class))) { | |
995 | throw new coding_exception('Cache plugin add instance forms must extend cachestore_addinstance_form'); | |
8139ad13 SH |
996 | } |
997 | } | |
998 | } | |
999 | ||
26ce56fd | 1000 | $locks = self::get_possible_locks_for_stores($plugindir, $plugin); |
42f2c59e | 1001 | |
81ede547 SH |
1002 | $url = new moodle_url('/cache/admin.php', array('action' => 'editstore', 'plugin' => $plugin, 'store' => $store)); |
1003 | $editform = new $class($url, array('plugin' => $plugin, 'store' => $store, 'locks' => $locks)); | |
acf49f4b | 1004 | if (isset($stores[$store]['lock'])) { |
25927c42 | 1005 | $editform->set_data(array('lock' => $stores[$store]['lock'])); |
acf49f4b | 1006 | } |
81ede547 SH |
1007 | // See if the cachestore is going to want to load data for the form. |
1008 | // If it has a customised add instance form then it is going to want to. | |
1009 | $storeclass = 'cachestore_'.$plugin; | |
1010 | $storedata = $stores[$store]; | |
2b274ad0 | 1011 | if (array_key_exists('configuration', $storedata) && array_key_exists('cache_is_configurable', class_implements($storeclass))) { |
81ede547 SH |
1012 | $storeclass::config_set_edit_form_data($editform, $storedata['configuration']); |
1013 | } | |
1014 | return $editform; | |
42f2c59e SH |
1015 | } |
1016 | ||
1017 | /** | |
1018 | * Returns an array of suitable lock instances for use with this plugin, or false if the plugin handles locking itself. | |
1019 | * | |
1020 | * @param string $plugindir | |
1021 | * @param string $plugin | |
1022 | * @return array|false | |
1023 | */ | |
26ce56fd | 1024 | protected static function get_possible_locks_for_stores($plugindir, $plugin) { |
42f2c59e SH |
1025 | global $CFG; // Needed for includes. |
1026 | $supportsnativelocking = false; | |
1027 | if (file_exists($plugindir.'/lib.php')) { | |
1028 | require_once($plugindir.'/lib.php'); | |
1029 | $pluginclass = 'cachestore_'.$plugin; | |
1030 | if (class_exists($pluginclass)) { | |
1031 | $supportsnativelocking = array_key_exists('cache_is_lockable', class_implements($pluginclass)); | |
1032 | } | |
1033 | } | |
1034 | ||
1035 | if (!$supportsnativelocking) { | |
1036 | $config = cache_config::instance(); | |
1037 | $locks = array(); | |
1038 | foreach ($config->get_locks() as $lock => $conf) { | |
1039 | if (!empty($conf['default'])) { | |
1040 | $name = get_string($lock, 'cache'); | |
1041 | } else { | |
1042 | $name = $lock; | |
1043 | } | |
1044 | $locks[$lock] = $name; | |
1045 | } | |
1046 | } else { | |
1047 | $locks = false; | |
1048 | } | |
1049 | ||
1050 | return $locks; | |
8139ad13 SH |
1051 | } |
1052 | ||
1053 | /** | |
1054 | * Processes the results of the add/edit instance form data for a plugin returning an array of config information suitable to | |
1055 | * store in configuration. | |
1056 | * | |
1057 | * @param stdClass $data The mform data. | |
1058 | * @return array | |
1059 | * @throws coding_exception | |
1060 | */ | |
1061 | public static function get_store_configuration_from_data(stdClass $data) { | |
1062 | global $CFG; | |
1063 | $file = $CFG->dirroot.'/cache/stores/'.$data->plugin.'/lib.php'; | |
1064 | if (!file_exists($file)) { | |
1065 | throw new coding_exception('Invalid cache plugin provided. '.$file); | |
1066 | } | |
1067 | require_once($file); | |
6fec1820 | 1068 | $class = 'cachestore_'.$data->plugin; |
8139ad13 SH |
1069 | if (!class_exists($class)) { |
1070 | throw new coding_exception('Invalid cache plugin provided.'); | |
1071 | } | |
2b274ad0 SH |
1072 | if (array_key_exists('cache_is_configurable', class_implements($class))) { |
1073 | return $class::config_get_configuration_array($data); | |
8139ad13 SH |
1074 | } |
1075 | return array(); | |
1076 | } | |
1077 | ||
1078 | /** | |
1079 | * Get an array of stores that are suitable to be used for a given definition. | |
1080 | * | |
1081 | * @param string $component | |
1082 | * @param string $area | |
1083 | * @return array Array containing 3 elements | |
1084 | * 1. An array of currently used stores | |
1085 | * 2. An array of suitable stores | |
1086 | * 3. An array of default stores | |
1087 | */ | |
1088 | public static function get_definition_store_options($component, $area) { | |
1089 | $factory = cache_factory::instance(); | |
1090 | $definition = $factory->create_definition($component, $area); | |
1091 | $config = cache_config::instance(); | |
1092 | $currentstores = $config->get_stores_for_definition($definition); | |
1093 | $possiblestores = $config->get_stores($definition->get_mode(), $definition->get_requirements_bin()); | |
1094 | ||
1095 | $defaults = array(); | |
1096 | foreach ($currentstores as $key => $store) { | |
1097 | if (!empty($store['default'])) { | |
1098 | $defaults[] = $key; | |
1099 | unset($currentstores[$key]); | |
1100 | } | |
1101 | } | |
1102 | foreach ($possiblestores as $key => $store) { | |
167ad91e | 1103 | if ($store['default']) { |
8139ad13 SH |
1104 | unset($possiblestores[$key]); |
1105 | $possiblestores[$key] = $store; | |
1106 | } | |
1107 | } | |
1108 | return array($currentstores, $possiblestores, $defaults); | |
1109 | } | |
1110 | ||
1111 | /** | |
1112 | * Get the default stores for all modes. | |
1113 | * | |
1114 | * @return array An array containing sub-arrays, one for each mode. | |
1115 | */ | |
1116 | public static function get_default_mode_stores() { | |
d31fd615 | 1117 | global $OUTPUT; |
8139ad13 | 1118 | $instance = cache_config::instance(); |
d31fd615 SH |
1119 | $adequatestores = cache_helper::get_stores_suitable_for_mode_default(); |
1120 | $icon = new pix_icon('i/warning', new lang_string('inadequatestoreformapping', 'cache')); | |
8139ad13 SH |
1121 | $storenames = array(); |
1122 | foreach ($instance->get_all_stores() as $key => $store) { | |
1123 | if (!empty($store['default'])) { | |
1124 | $storenames[$key] = new lang_string('store_'.$key, 'cache'); | |
1125 | } | |
1126 | } | |
1127 | $modemappings = array( | |
1128 | cache_store::MODE_APPLICATION => array(), | |
1129 | cache_store::MODE_SESSION => array(), | |
1130 | cache_store::MODE_REQUEST => array(), | |
1131 | ); | |
1132 | foreach ($instance->get_mode_mappings() as $mapping) { | |
1133 | $mode = $mapping['mode']; | |
1134 | if (!array_key_exists($mode, $modemappings)) { | |
1135 | debugging('Unknown mode in cache store mode mappings', DEBUG_DEVELOPER); | |
1136 | continue; | |
1137 | } | |
1138 | if (array_key_exists($mapping['store'], $storenames)) { | |
1139 | $modemappings[$mode][$mapping['store']] = $storenames[$mapping['store']]; | |
1140 | } else { | |
1141 | $modemappings[$mode][$mapping['store']] = $mapping['store']; | |
1142 | } | |
d31fd615 SH |
1143 | if (!array_key_exists($mapping['store'], $adequatestores)) { |
1144 | $modemappings[$mode][$mapping['store']] = $modemappings[$mode][$mapping['store']].' '.$OUTPUT->render($icon); | |
1145 | } | |
8139ad13 SH |
1146 | } |
1147 | return $modemappings; | |
1148 | } | |
167ad91e SH |
1149 | |
1150 | /** | |
1151 | * Returns an array summarising the locks available in the system | |
1152 | */ | |
1153 | public static function get_lock_summaries() { | |
1154 | $locks = array(); | |
1155 | $instance = cache_config::instance(); | |
1156 | $stores = $instance->get_all_stores(); | |
1157 | foreach ($instance->get_locks() as $lock) { | |
1158 | $default = !empty($lock['default']); | |
1159 | if ($default) { | |
1160 | $name = new lang_string($lock['name'], 'cache'); | |
1161 | } else { | |
1162 | $name = $lock['name']; | |
1163 | } | |
1164 | $uses = 0; | |
1165 | foreach ($stores as $store) { | |
1166 | if (!empty($store['lock']) && $store['lock'] === $lock['name']) { | |
1167 | $uses++; | |
1168 | } | |
1169 | } | |
1170 | $lockdata = array( | |
1171 | 'name' => $name, | |
1172 | 'default' => $default, | |
acf49f4b SH |
1173 | 'uses' => $uses, |
1174 | 'type' => get_string('pluginname', $lock['type']) | |
167ad91e | 1175 | ); |
acf49f4b | 1176 | $locks[$lock['name']] = $lockdata; |
167ad91e SH |
1177 | } |
1178 | return $locks; | |
1179 | } | |
acf49f4b SH |
1180 | |
1181 | /** | |
1182 | * Returns an array of lock plugins for which we can add an instance. | |
1183 | * | |
1184 | * Suitable for use within an mform select element. | |
1185 | * | |
1186 | * @return array | |
1187 | */ | |
1188 | public static function get_addable_lock_options() { | |
059e08ed | 1189 | $plugins = core_component::get_plugin_list_with_class('cachelock', '', 'lib.php'); |
acf49f4b SH |
1190 | $options = array(); |
1191 | $len = strlen('cachelock_'); | |
1192 | foreach ($plugins as $plugin => $class) { | |
1193 | $method = "$class::can_add_instance"; | |
1194 | if (is_callable($method) && !call_user_func($method)) { | |
1195 | // Can't add an instance of this plugin. | |
1196 | continue; | |
1197 | } | |
1198 | $options[substr($plugin, $len)] = get_string('pluginname', $plugin); | |
1199 | } | |
1200 | return $options; | |
1201 | } | |
1202 | ||
1203 | /** | |
1204 | * Gets the form to use when adding a lock instance. | |
1205 | * | |
1206 | * @param string $plugin | |
1207 | * @param array $lockplugin | |
1208 | * @return cache_lock_form | |
1209 | * @throws coding_exception | |
1210 | */ | |
1211 | public static function get_add_lock_form($plugin, array $lockplugin = null) { | |
1212 | global $CFG; // Needed for includes. | |
bd3b3bba | 1213 | $plugins = core_component::get_plugin_list('cachelock'); |
acf49f4b SH |
1214 | if (!array_key_exists($plugin, $plugins)) { |
1215 | throw new coding_exception('Invalid cache lock plugin requested when trying to create a form.'); | |
1216 | } | |
1217 | $plugindir = $plugins[$plugin]; | |
1218 | $class = 'cache_lock_form'; | |
1219 | if (file_exists($plugindir.'/addinstanceform.php') && in_array('cache_is_configurable', class_implements($class))) { | |
1220 | require_once($plugindir.'/addinstanceform.php'); | |
1221 | if (class_exists('cachelock_'.$plugin.'_addinstance_form')) { | |
1222 | $class = 'cachelock_'.$plugin.'_addinstance_form'; | |
1223 | if (!array_key_exists('cache_lock_form', class_parents($class))) { | |
1224 | throw new coding_exception('Cache lock plugin add instance forms must extend cache_lock_form'); | |
1225 | } | |
1226 | } | |
1227 | } | |
1228 | return new $class(null, array('lock' => $plugin)); | |
1229 | } | |
1230 | ||
1231 | /** | |
1232 | * Gets configuration data from a new lock instance form. | |
1233 | * | |
1234 | * @param string $plugin | |
1235 | * @param stdClass $data | |
1236 | * @return array | |
1237 | * @throws coding_exception | |
1238 | */ | |
1239 | public static function get_lock_configuration_from_data($plugin, $data) { | |
1240 | global $CFG; | |
1241 | $file = $CFG->dirroot.'/cache/locks/'.$plugin.'/lib.php'; | |
1242 | if (!file_exists($file)) { | |
1243 | throw new coding_exception('Invalid cache plugin provided. '.$file); | |
1244 | } | |
1245 | require_once($file); | |
1246 | $class = 'cachelock_'.$plugin; | |
1247 | if (!class_exists($class)) { | |
1248 | throw new coding_exception('Invalid cache plugin provided.'); | |
1249 | } | |
1250 | if (array_key_exists('cache_is_configurable', class_implements($class))) { | |
1251 | return $class::config_get_configuration_array($data); | |
1252 | } | |
1253 | return array(); | |
1254 | } | |
7866b094 | 1255 | } |