Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
namespace core_cache;
18
 
1441 ariadna 19
use core_component;
20
use core\output\pix_icon;
21
use lang_string;
1 efrain 22
 
23
/**
24
 * Administration helper base class.
25
 *
26
 * Defines abstract methods for a subclass to define the admin page.
27
 *
1441 ariadna 28
 * @package     core_cache
1 efrain 29
 * @category    cache
30
 * @author      Peter Burnett <peterburnett@catalyst-au.net>
31
 * @copyright   2020 Catalyst IT
32
 * @copyright  2012 Sam Hemelryk
33
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34
 */
1441 ariadna 35
abstract class administration_helper extends helper {
1 efrain 36
    /**
37
     * Returns an array containing all of the information about stores a renderer needs.
38
     * @return array
39
     */
40
    public static function get_store_instance_summaries(): array {
1441 ariadna 41
        $return = [];
42
        $default = [];
43
        $instance = config::instance();
1 efrain 44
        $stores = $instance->get_all_stores();
45
        $locks = $instance->get_locks();
46
        foreach ($stores as $name => $details) {
47
            $class = $details['class'];
48
            $store = false;
49
            if ($class::are_requirements_met()) {
50
                $store = new $class($details['name'], $details['configuration']);
51
            }
52
            $lock = (isset($details['lock'])) ? $locks[$details['lock']] : $instance->get_default_lock();
1441 ariadna 53
            $record = [
1 efrain 54
                'name' => $name,
55
                'plugin' => $details['plugin'],
56
                'default' => $details['default'],
57
                'isready' => $store ? $store->is_ready() : false,
58
                'requirementsmet' => $class::are_requirements_met(),
59
                'mappings' => 0,
60
                'lock' => $lock,
1441 ariadna 61
                'modes' => [
62
                    store::MODE_APPLICATION =>
63
                        ($class::get_supported_modes($return) & store::MODE_APPLICATION) == store::MODE_APPLICATION,
64
                    store::MODE_SESSION =>
65
                        ($class::get_supported_modes($return) & store::MODE_SESSION) == store::MODE_SESSION,
66
                    store::MODE_REQUEST =>
67
                        ($class::get_supported_modes($return) & store::MODE_REQUEST) == store::MODE_REQUEST,
68
                ],
69
                'supports' => [
1 efrain 70
                    'multipleidentifiers' => $store ? $store->supports_multiple_identifiers() : false,
71
                    'dataguarantee' => $store ? $store->supports_data_guarantee() : false,
72
                    'nativettl' => $store ? $store->supports_native_ttl() : false,
1441 ariadna 73
                    'nativelocking' => ($store instanceof lockable_cache_interface),
74
                    'keyawareness' => ($store instanceof key_aware_cache_interface),
75
                    'searchable' => ($store instanceof searchable_cache_interface),
76
                ],
77
                'warnings' => $store ? $store->get_warnings() : [],
78
            ];
1 efrain 79
            if (empty($details['default'])) {
80
                $return[$name] = $record;
81
            } else {
82
                $default[$name] = $record;
83
            }
84
        }
85
 
86
        ksort($return);
87
        ksort($default);
88
        $return = $return + $default;
89
 
90
        $mappings = $instance->get_definition_mappings();
91
        foreach ($mappings as $mapping) {
92
            if (!array_key_exists($mapping['store'], $return)) {
93
                continue;
94
            }
95
            $return[$mapping['store']]['mappings']++;
96
        }
97
 
98
        // Now get all definitions, and if not mapped, increment the defaults for the mode.
99
        $modemappings = $instance->get_mode_mappings();
100
        foreach ($instance->get_definitions() as $definition) {
101
            // Construct the definition name to search for.
102
            $defname = $definition['component'] . '/' . $definition['area'];
103
            // Skip if definition is already mapped.
104
            if (array_search($defname, array_column($mappings, 'definition')) !== false) {
105
                continue;
106
            }
107
 
108
            $mode = $definition['mode'];
109
            // Get the store name of the default mapping from the mode.
110
            $index = array_search($mode, array_column($modemappings, 'mode'));
111
            $store = $modemappings[$index]['store'];
112
            $return[$store]['mappings']++;
113
        }
114
 
115
        return $return;
116
    }
117
 
118
    /**
119
     * Returns an array of information about plugins, everything a renderer needs.
120
     *
121
     * @return array for each store, an array containing various information about each store.
122
     *     See the code below for details
123
     */
124
    public static function get_store_plugin_summaries(): array {
1441 ariadna 125
        $return = [];
1 efrain 126
        $plugins = \core_component::get_plugin_list_with_file('cachestore', 'lib.php', true);
127
        foreach ($plugins as $plugin => $path) {
1441 ariadna 128
            $class = 'cachestore_' . $plugin;
129
            $return[$plugin] = [
130
                'name' => get_string('pluginname', 'cachestore_' . $plugin),
1 efrain 131
                'requirementsmet' => $class::are_requirements_met(),
132
                'instances' => 0,
1441 ariadna 133
                'modes' => [
134
                    store::MODE_APPLICATION => ($class::get_supported_modes() & store::MODE_APPLICATION),
135
                    store::MODE_SESSION => ($class::get_supported_modes() & store::MODE_SESSION),
136
                    store::MODE_REQUEST => ($class::get_supported_modes() & store::MODE_REQUEST),
137
                ],
138
                'supports' => [
139
                    'multipleidentifiers' => ($class::get_supported_features() & store::SUPPORTS_MULTIPLE_IDENTIFIERS),
140
                    'dataguarantee' => ($class::get_supported_features() & store::SUPPORTS_DATA_GUARANTEE),
141
                    'nativettl' => ($class::get_supported_features() & store::SUPPORTS_NATIVE_TTL),
142
                    'nativelocking' => (in_array(lockable_cache_interface::class, class_implements($class))),
143
                    'keyawareness' => (array_key_exists(key_aware_cache_interface::class, class_implements($class))),
144
                ],
145
                'canaddinstance' => ($class::can_add_instance() && $class::are_requirements_met()),
146
            ];
1 efrain 147
        }
148
 
1441 ariadna 149
        $instance = config::instance();
1 efrain 150
        $stores = $instance->get_all_stores();
151
        foreach ($stores as $store) {
152
            $plugin = $store['plugin'];
153
            if (array_key_exists($plugin, $return)) {
154
                $return[$plugin]['instances']++;
155
            }
156
        }
157
 
158
        return $return;
159
    }
160
 
161
    /**
162
     * Returns an array about the definitions. All the information a renderer needs.
163
     *
164
     * @return array for each store, an array containing various information about each store.
165
     *     See the code below for details
166
     */
167
    public static function get_definition_summaries(): array {
1441 ariadna 168
        $factory = factory::instance();
1 efrain 169
        $config = $factory->create_config_instance();
1441 ariadna 170
        $storenames = [];
1 efrain 171
        foreach ($config->get_all_stores() as $key => $store) {
172
            if (!empty($store['default'])) {
1441 ariadna 173
                $storenames[$key] = new lang_string('store_' . $key, 'cache');
1 efrain 174
            } else {
175
                $storenames[$store['name']] = $store['name'];
176
            }
177
        }
1441 ariadna 178
        /* @var definition[] $definitions */
1 efrain 179
        $definitions = [];
180
        $return = [];
181
        foreach ($config->get_definitions() as $key => $definition) {
1441 ariadna 182
            $definitions[$key] = definition::load($definition['component'] . '/' . $definition['area'], $definition);
1 efrain 183
        }
184
        foreach ($definitions as $id => $definition) {
1441 ariadna 185
            $mappings = [];
186
            foreach (helper::get_stores_suitable_for_definition($definition) as $store) {
1 efrain 187
                $mappings[] = $storenames[$store->my_name()];
188
            }
1441 ariadna 189
            $return[$id] = [
1 efrain 190
                'id' => $id,
191
                'name' => $definition->get_name(),
192
                'mode' => $definition->get_mode(),
193
                'component' => $definition->get_component(),
194
                'area' => $definition->get_area(),
195
                'mappings' => $mappings,
196
                'canuselocalstore' => $definition->can_use_localstore(),
197
                'sharingoptions' => self::get_definition_sharing_options($definition->get_sharing_options(), false),
198
                'selectedsharingoption' => self::get_definition_sharing_options($definition->get_selected_sharing_option(), true),
1441 ariadna 199
                'userinputsharingkey' => $definition->get_user_input_sharing_key(),
200
            ];
1 efrain 201
        }
202
        return $return;
203
    }
204
 
205
    /**
206
     * Get the default stores for all modes.
207
     *
208
     * @return array An array containing sub-arrays, one for each mode.
209
     */
210
    public static function get_default_mode_stores(): array {
211
        global $OUTPUT;
1441 ariadna 212
        $instance = config::instance();
213
        $adequatestores = helper::get_stores_suitable_for_mode_default();
214
        $icon = new pix_icon('i/warning', new lang_string('inadequatestoreformapping', 'cache'));
215
        $storenames = [];
1 efrain 216
        foreach ($instance->get_all_stores() as $key => $store) {
217
            if (!empty($store['default'])) {
1441 ariadna 218
                $storenames[$key] = new lang_string('store_' . $key, 'cache');
1 efrain 219
            }
220
        }
1441 ariadna 221
        $modemappings = [
222
            store::MODE_APPLICATION => [],
223
            store::MODE_SESSION => [],
224
            store::MODE_REQUEST => [],
225
        ];
1 efrain 226
        foreach ($instance->get_mode_mappings() as $mapping) {
227
            $mode = $mapping['mode'];
228
            if (!array_key_exists($mode, $modemappings)) {
229
                debugging('Unknown mode in cache store mode mappings', DEBUG_DEVELOPER);
230
                continue;
231
            }
232
            if (array_key_exists($mapping['store'], $storenames)) {
233
                $modemappings[$mode][$mapping['store']] = $storenames[$mapping['store']];
234
            } else {
235
                $modemappings[$mode][$mapping['store']] = $mapping['store'];
236
            }
237
            if (!array_key_exists($mapping['store'], $adequatestores)) {
1441 ariadna 238
                $modemappings[$mode][$mapping['store']] = $modemappings[$mode][$mapping['store']] . ' ' . $OUTPUT->render($icon);
1 efrain 239
            }
240
        }
241
        return $modemappings;
242
    }
243
 
244
    /**
245
     * Returns an array summarising the locks available in the system.
246
     *
247
     * @return array array of lock summaries.
248
     */
249
    public static function get_lock_summaries(): array {
1441 ariadna 250
        $locks = [];
251
        $instance = config::instance();
1 efrain 252
        $stores = $instance->get_all_stores();
253
        foreach ($instance->get_locks() as $lock) {
254
            $default = !empty($lock['default']);
255
            if ($default) {
1441 ariadna 256
                $name = new lang_string($lock['name'], 'cache');
1 efrain 257
            } else {
258
                $name = $lock['name'];
259
            }
260
            $uses = 0;
261
            foreach ($stores as $store) {
262
                if (!empty($store['lock']) && $store['lock'] === $lock['name']) {
263
                    $uses++;
264
                }
265
            }
1441 ariadna 266
            $lockdata = [
1 efrain 267
                'name' => $name,
268
                'default' => $default,
269
                'uses' => $uses,
1441 ariadna 270
                'type' => get_string('pluginname', $lock['type']),
271
            ];
1 efrain 272
            $locks[$lock['name']] = $lockdata;
273
        }
274
        return $locks;
275
    }
276
 
277
    /**
278
     * Given a sharing option hash this function returns an array of strings that can be used to describe it.
279
     *
280
     * @param int $sharingoption The sharing option hash to get strings for.
281
     * @param bool $isselectedoptions Set to true if the strings will be used to view the selected options.
282
     * @return array An array of lang_string's.
283
     */
284
    public static function get_definition_sharing_options(int $sharingoption, bool $isselectedoptions = true): array {
1441 ariadna 285
        $options = [];
1 efrain 286
        $prefix = ($isselectedoptions) ? 'sharingselected' : 'sharing';
1441 ariadna 287
        if ($sharingoption & definition::SHARING_ALL) {
288
            $options[definition::SHARING_ALL] = new lang_string($prefix . '_all', 'cache');
1 efrain 289
        }
1441 ariadna 290
        if ($sharingoption & definition::SHARING_SITEID) {
291
            $options[definition::SHARING_SITEID] = new lang_string($prefix . '_siteid', 'cache');
1 efrain 292
        }
1441 ariadna 293
        if ($sharingoption & definition::SHARING_VERSION) {
294
            $options[definition::SHARING_VERSION] = new lang_string($prefix . '_version', 'cache');
1 efrain 295
        }
1441 ariadna 296
        if ($sharingoption & definition::SHARING_INPUT) {
297
            $options[definition::SHARING_INPUT] = new lang_string($prefix . '_input', 'cache');
1 efrain 298
        }
299
        return $options;
300
    }
301
 
302
    /**
303
     * Get an array of stores that are suitable to be used for a given definition.
304
     *
305
     * @param string $component
306
     * @param string $area
307
     * @return array Array containing 3 elements
308
     *      1. An array of currently used stores
309
     *      2. An array of suitable stores
310
     *      3. An array of default stores
311
     */
312
    public static function get_definition_store_options(string $component, string $area): array {
1441 ariadna 313
        $factory = factory::instance();
1 efrain 314
        $definition = $factory->create_definition($component, $area);
1441 ariadna 315
        $config = config::instance();
1 efrain 316
        $currentstores = $config->get_stores_for_definition($definition);
317
        $possiblestores = $config->get_stores($definition->get_mode(), $definition->get_requirements_bin());
318
 
1441 ariadna 319
        $defaults = [];
1 efrain 320
        foreach ($currentstores as $key => $store) {
321
            if (!empty($store['default'])) {
322
                $defaults[] = $key;
323
                unset($currentstores[$key]);
324
            }
325
        }
326
        foreach ($possiblestores as $key => $store) {
327
            if ($store['default']) {
328
                unset($possiblestores[$key]);
329
                $possiblestores[$key] = $store;
330
            }
331
        }
1441 ariadna 332
        return [$currentstores, $possiblestores, $defaults];
1 efrain 333
    }
334
 
335
    /**
336
     * This function must be implemented to display options for store plugins.
337
     *
338
     * @param string $name the name of the store plugin.
339
     * @param array $plugindetails array of store plugin details.
340
     * @return array array of actions.
341
     */
342
    public function get_store_plugin_actions(string $name, array $plugindetails): array {
1441 ariadna 343
        return [];
1 efrain 344
    }
345
 
346
    /**
347
     * This function must be implemented to display options for store instances.
348
     *
349
     * @param string $name the store instance name.
350
     * @param array $storedetails array of store instance details.
351
     * @return array array of actions.
352
     */
353
    public function get_store_instance_actions(string $name, array $storedetails): array {
1441 ariadna 354
        return [];
1 efrain 355
    }
356
 
357
    /**
358
     * This function must be implemented to display options for definition mappings.
359
     *
360
     * @param context $context the context for the definition.
361
     * @param array $definitionsummary the definition summary.
362
     * @return array array of actions.
363
     */
364
    public function get_definition_actions(\context $context, array $definitionsummary): array {
1441 ariadna 365
        return [];
1 efrain 366
    }
367
 
368
    /**
369
     * This function must be implemented to get addable locks.
370
     *
371
     * @return array array of locks that are addable.
372
     */
373
    public function get_addable_lock_options(): array {
1441 ariadna 374
        return [];
1 efrain 375
    }
376
 
377
    /**
378
     * This function must be implemented to perform any page actions by a child class.
379
     *
380
     * @param string $action the action to perform.
381
     * @param array $forminfo empty array to be set by actions.
382
     * @return array array of form info.
383
     */
384
    abstract public function perform_cache_actions(string $action, array $forminfo): array;
385
 
386
    /**
387
     * This function must be implemented to display the cache admin page.
388
     *
389
     * @param \core_cache\output\renderer $renderer the renderer used to generate the page.
390
     * @return string the HTML for the page.
391
     */
392
    abstract public function generate_admin_page(\core_cache\output\renderer $renderer): string;
393
 
394
    /**
395
     * Gets usage information about the whole cache system.
396
     *
397
     * This is a slow function and should only be used on an admin information page.
398
     *
399
     * The returned array lists all cache definitions with fields 'cacheid' and 'stores'. For
400
     * each store, the following fields are available:
401
     *
402
     * - name (store name)
403
     * - class (e.g. cachestore_redis)
404
     * - supported (true if we have any information)
405
     * - items (number of items stored)
406
     * - mean (mean size of item)
407
     * - sd (standard deviation for item sizes)
408
     * - margin (margin of error for mean at 95% confidence)
409
     * - storetotal (total usage for store if known, otherwise null)
410
     *
411
     * The storetotal field will be the same for every cache that uses the same store.
412
     *
413
     * @param int $samplekeys Number of keys to sample when checking size of large caches
414
     * @return array Details of cache usage
415
     */
416
    abstract public function get_usage(int $samplekeys): array;
417
}