Proyectos de Subversion Moodle

Rev

| 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_admin\table;
18
 
19
use context_system;
20
use core_plugin_manager;
21
use core_table\dynamic as dynamic_table;
22
use flexible_table;
23
use html_writer;
24
use moodle_url;
25
use stdClass;
26
 
27
defined('MOODLE_INTERNAL') || die();
28
require_once("{$CFG->libdir}/tablelib.php");
29
 
30
/**
31
 * Plugin Management table.
32
 *
33
 * @package    core_admin
34
 * @copyright  2023 Andrew Lyons <andrew@nicols.co.uk>
35
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36
 */
37
abstract class plugin_management_table extends flexible_table implements dynamic_table {
38
 
39
    /** @var \core\plugininfo\base[] The plugin list */
40
    protected array $plugins = [];
41
 
42
    /** @var int The number of enabled plugins of this type */
43
    protected int $enabledplugincount = 0;
44
 
45
    /** @var core_plugin_manager */
46
    protected core_plugin_manager $pluginmanager;
47
 
48
    /** @var string The plugininfo class for this plugintype */
49
    protected string $plugininfoclass;
50
 
51
    public function __construct() {
52
        global $CFG;
53
 
54
        parent::__construct($this->get_table_id());
55
        require_once($CFG->libdir . '/adminlib.php');
56
 
57
        // Fetch the plugininfo class.
58
        $this->pluginmanager = core_plugin_manager::instance();
59
        $this->plugininfoclass = $this->pluginmanager::resolve_plugininfo_class($this->get_plugintype());
60
 
61
        $this->guess_base_url();
62
 
63
        $this->plugins = $this->get_sorted_plugins();
64
        $this->enabledplugincount = count(array_filter($this->plugins, function ($plugin) {
65
            return $plugin->is_enabled();
66
        }));
67
 
68
        $this->setup_column_configuration();
69
        $this->set_filterset(new plugin_management_table_filterset());
70
        $this->setup();
71
    }
72
 
73
    /**
74
     * Get the list of sorted plugins.
75
     *
76
     * @return \core\plugininfo\base[]
77
     */
78
    protected function get_sorted_plugins(): array {
79
        if ($this->plugininfoclass::plugintype_supports_ordering()) {
80
            return $this->plugininfoclass::get_sorted_plugins();
81
        } else {
82
            $plugins = $this->pluginmanager->get_plugins_of_type($this->get_plugintype());
83
            return self::sort_plugins($plugins);
84
        }
85
    }
86
 
87
    /**
88
     * Sort the plugins list.
89
     *
90
     * Note: This only applies to plugins which do not support ordering.
91
     *
92
     * @param \core\plugininfo\base[] $plugins
93
     * @return \core\plugininfo\base[]
94
     */
95
    protected function sort_plugins(array $plugins): array {
96
        // The asort functions work by reference.
97
        \core_collator::asort_objects_by_property($plugins, 'displayname');
98
 
99
        return $plugins;
100
    }
101
 
102
    /**
103
     * Set up the column configuration for this table.
104
     */
105
    protected function setup_column_configuration(): void {
106
        $columnlist = $this->get_column_list();
107
        $this->define_columns(array_keys($columnlist));
108
        $this->define_headers(array_values($columnlist));
109
 
110
        $columnswithhelp = $this->get_columns_with_help();
111
        $columnhelp = array_map(function (string $column) use ($columnswithhelp): ?\renderable {
112
            if (array_key_exists($column, $columnswithhelp)) {
113
                return $columnswithhelp[$column];
114
            }
115
 
116
            return null;
117
        }, array_keys($columnlist));
118
        $this->define_help_for_headers($columnhelp);
119
    }
120
 
121
    /**
122
     * Set the standard order of the plugins.
123
     *
124
     * @param array $plugins
125
     * @return array
126
     */
127
    protected function order_plugins(array $plugins): array {
128
        uasort($plugins, function ($a, $b) {
129
            if ($a->is_enabled() && !$b->is_enabled()) {
130
                return -1;
131
            } else if (!$a->is_enabled() && $b->is_enabled()) {
132
                return 1;
133
            }
134
            return strnatcasecmp($a->name, $b->name);
135
        });
136
 
137
        return $plugins;
138
    }
139
 
140
    /**
141
     * Get the plugintype for this table.
142
     *
143
     * @return string
144
     */
145
    abstract protected function get_plugintype(): string;
146
 
147
    /**
148
     * Get the action URL for this table.
149
     *
150
     * The action URL is used to perform all actions when JS is not available.
151
     *
152
     * @param array $params
153
     * @return moodle_url
154
     */
155
    abstract protected function get_action_url(array $params = []): moodle_url;
156
 
157
    /**
158
     * Provide a default implementation for guessing the base URL from the action URL.
159
     */
160
    public function guess_base_url(): void {
161
        $this->define_baseurl($this->get_action_url());
162
    }
163
 
164
    /**
165
     * Get the web service method used to toggle state.
166
     *
167
     * @return null|string
168
     */
169
    protected function get_toggle_service(): ?string {
170
        return 'core_admin_set_plugin_state';
171
    }
172
 
173
    /**
174
     * Get the web service method used to order plugins.
175
     *
176
     * @return null|string
177
     */
178
    protected function get_sortorder_service(): ?string {
179
        return 'core_admin_set_plugin_order';
180
    }
181
 
182
    /**
183
     * Get the ID of the table.
184
     *
185
     * @return string
186
     */
187
    protected function get_table_id(): string {
188
        return 'plugin_management_table-' . $this->get_plugintype();
189
    }
190
 
191
    /**
192
     * Get a list of the column titles
193
     * @return string[]
194
     */
195
    protected function get_column_list(): array {
196
        $columns = [
197
            'name' => get_string('name', 'core'),
198
            'version' => get_string('version', 'core'),
199
        ];
200
 
201
        if ($this->supports_disabling()) {
202
            $columns['enabled'] = get_string('pluginenabled', 'core_plugin');
203
        }
204
 
205
        if ($this->supports_ordering()) {
206
            $columns['order'] = get_string('order', 'core');
207
        }
208
 
209
        $columns['settings'] = get_string('settings', 'core');
210
        $columns['uninstall'] = get_string('uninstallplugin', 'core_admin');
211
 
212
        return $columns;
213
    }
214
 
215
    protected function get_columns_with_help(): array {
216
        return [];
217
    }
218
 
219
    /**
220
     * Get the context for this table.
221
     *
222
     * @return context_system
223
     */
224
    public function get_context(): context_system {
225
        return context_system::instance();
226
    }
227
 
228
    /**
229
     * Get the table content.
230
     */
231
    public function get_content(): string {
232
        ob_start();
233
        $this->out();
234
        $content = ob_get_contents();
235
        ob_end_clean();
236
        return $content;
237
    }
238
 
239
    /**
240
     * Print the table.
241
     */
242
    public function out(): void {
243
        $plugintype = $this->get_plugintype();
244
        foreach ($this->plugins as $plugininfo) {
245
            $plugin = "{$plugintype}_{$plugininfo->name}";
246
            $rowdata = (object) [
247
                'plugin' => $plugin,
248
                'plugininfo' => $plugininfo,
249
                'name' => $plugininfo->displayname,
250
                'version' => $plugininfo->versiondb,
251
            ];
252
            $this->add_data_keyed(
253
                $this->format_row($rowdata),
254
                $this->get_row_class($rowdata)
255
            );
256
        }
257
 
258
        $this->finish_output(false);
259
    }
260
 
261
    /**
262
     * This table is not downloadable.
263
     * @param bool $downloadable
264
     * @return bool
265
     */
266
    // phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
267
    public function is_downloadable($downloadable = null): bool {
268
        return false;
269
    }
270
 
271
    /**
272
     * Show the name column content.
273
     *
274
     * @param stdClass $row
275
     * @return string
276
     */
277
    protected function col_name(stdClass $row): string {
278
        $status = $row->plugininfo->get_status();
279
        if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
280
            return html_writer::span(
281
                get_string('pluginmissingfromdisk', 'core', $row->plugininfo),
282
                'notifyproblem'
283
            );
284
        }
285
 
286
        if ($row->plugininfo->is_installed_and_upgraded()) {
287
            return $row->plugininfo->displayname;
288
        }
289
 
290
        return html_writer::span(
291
            $row->plugininfo->displayname,
292
            'notifyproblem'
293
        );
294
    }
295
 
296
    /**
297
     * Show the enable/disable column content.
298
     *
299
     * @param stdClass $row
300
     * @return string
301
     */
302
    protected function col_enabled(stdClass $row): string {
303
        global $OUTPUT;
304
 
305
        $enabled = $row->plugininfo->is_enabled();
306
        $params = [
307
            'sesskey' => sesskey(),
308
            'plugin' => $row->plugininfo->name,
309
            'action' => $enabled ? 'disable' : 'enable',
310
        ];
311
 
312
        if ($enabled) {
313
            $icon = $OUTPUT->pix_icon('t/hide', get_string('disableplugin', 'core_admin', $row->plugininfo->displayname));
314
        } else {
315
            $icon = $OUTPUT->pix_icon('t/show', get_string('enableplugin', 'core_admin', $row->plugininfo->displayname));
316
        }
317
 
318
        return html_writer::link(
319
            $this->get_action_url($params),
320
            $icon,
321
            [
322
                'data-toggle-method' => $this->get_toggle_service(),
323
                'data-action' => 'togglestate',
324
                'data-plugin' => $row->plugin,
325
                'data-state' => $enabled ? 1 : 0,
326
            ],
327
        );
328
    }
329
 
330
    protected function col_order(stdClass $row): string {
331
        global $OUTPUT;
332
 
333
        if (!$this->supports_ordering()) {
334
            return '';
335
        }
336
 
337
        if (!$row->plugininfo->is_enabled()) {
338
            return '';
339
        }
340
 
341
        if ($this->enabledplugincount <= 1) {
342
            // There is only one row.
343
            return '';
344
        }
345
 
346
        $hasup = true;
347
        $hasdown = true;
348
 
349
        if (empty($this->currentrow)) {
350
            // This is the top row.
351
            $hasup = false;
352
        }
353
 
354
        if ($this->currentrow === ($this->enabledplugincount - 1)) {
355
            // This is the last row.
356
            $hasdown = false;
357
        }
358
 
359
        if ($this->supports_ordering()) {
360
            $dataattributes = [
361
                'data-method' => $this->get_sortorder_service(),
362
                'data-action' => 'move',
363
                'data-plugin' => $row->plugin,
364
            ];
365
        } else {
366
            $dataattributes = [];
367
        }
368
 
369
        if ($hasup) {
370
            $upicon = html_writer::link(
371
                $this->get_action_url([
372
                    'sesskey' => sesskey(),
373
                    'action' => 'up',
374
                    'plugin' => $row->plugininfo->name,
375
                ]),
376
                $OUTPUT->pix_icon('t/up', get_string('moveup')),
377
                array_merge($dataattributes, ['data-direction' => 'up']),
378
            );
379
        } else {
380
            $upicon = $OUTPUT->spacer();
381
        }
382
 
383
        if ($hasdown) {
384
            $downicon = html_writer::link(
385
                $this->get_action_url([
386
                    'sesskey' => sesskey(),
387
                    'action' => 'down',
388
                    'plugin' => $row->plugininfo->name,
389
                ]),
390
                $OUTPUT->pix_icon('t/down', get_string('movedown')),
391
                array_merge($dataattributes, ['data-direction' => 'down']),
392
            );
393
        } else {
394
            $downicon = $OUTPUT->spacer();
395
        }
396
 
397
        // For now just add the up/down icons.
398
        return html_writer::span($upicon . $downicon);
399
    }
400
 
401
    /**
402
     * Show the settings column content.
403
     *
404
     * @param stdClass $row
405
     * @return string
406
     */
407
    protected function col_settings(stdClass $row): string {
408
        if ($settingsurl = $row->plugininfo->get_settings_url()) {
409
            return html_writer::link($settingsurl, get_string('settings'));
410
        }
411
 
412
        return '';
413
    }
414
 
415
    /**
416
     * Show the Uninstall column content.
417
     *
418
     * @param stdClass $row
419
     * @return string
420
     */
421
    protected function col_uninstall(stdClass $row): string {
422
        $status = $row->plugininfo->get_status();
423
 
424
        if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
425
            return get_string('status_new', 'core_plugin');
426
        }
427
 
428
        if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
429
            $uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
430
        } else {
431
            $uninstall = '';
432
        }
433
 
434
        if ($uninstallurl = $this->pluginmanager->get_uninstall_url($row->plugin)) {
435
            $uninstall .= html_writer::link($uninstallurl, get_string('uninstallplugin', 'core_admin'));
436
        }
437
 
438
        return $uninstall;
439
    }
440
 
441
    /**
442
     * Get the JS module used to manage this table.
443
     *
444
     * This should be a class which extends 'core_admin/plugin_management_table'.
445
     *
446
     * @return string
447
     */
448
    protected function get_table_js_module(): string {
449
        return 'core_admin/plugin_management_table';
450
    }
451
 
452
    /**
453
     * Add JS specific to this implementation.
454
     *
455
     * @return string
456
     */
457
    protected function get_dynamic_table_html_end(): string {
458
        global $PAGE;
459
 
460
        $PAGE->requires->js_call_amd($this->get_table_js_module(), 'init');
461
        return parent::get_dynamic_table_html_end();
462
    }
463
 
464
    /**
465
     * Get any class to add to the row.
466
     *
467
     * @param mixed $row
468
     * @return string
469
     */
470
    protected function get_row_class($row): string {
471
        $plugininfo = $row->plugininfo;
472
        if ($plugininfo->get_status() === core_plugin_manager::PLUGIN_STATUS_MISSING) {
473
            return '';
474
        }
475
 
476
        if (!$plugininfo->is_enabled()) {
477
            return 'dimmed_text';
478
        }
479
        return '';
480
    }
481
 
482
    public static function get_filterset_class(): string {
483
        return self::class . '_filterset';
484
    }
485
 
486
    /**
487
     * Whether this plugin type supports the disabling of plugins.
488
     *
489
     * @return bool
490
     */
491
    protected function supports_disabling(): bool {
492
        return $this->plugininfoclass::plugintype_supports_disabling();
493
    }
494
 
495
    /**
496
     * Whether this table should show ordering fields.
497
     *
498
     * @return bool
499
     */
500
    protected function supports_ordering(): bool {
501
        return $this->plugininfoclass::plugintype_supports_ordering();
502
    }
503
}