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_adminpresets;
18
 
19
use memory_xml_output;
20
use moodle_exception;
21
use stdClass;
22
use xml_writer;
23
 
24
defined('MOODLE_INTERNAL') || die();
25
 
26
global $CFG;
27
require_once($CFG->libdir . '/adminlib.php');
28
 
29
/**
30
 * Admin tool presets manager class.
31
 *
32
 * @package          core_adminpresets
33
 * @copyright        2021 Pimenko <support@pimenko.com><pimenko.com>
34
 * @author           Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> code
35
 * @license          http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36
 */
37
class manager {
38
 
39
    /** @var \admin_root The admin root tree with the settings. **/
40
    private $adminroot;
41
 
42
    /** @var array Setting classes mapping, to associated the local/setting class that should be used when there is
43
     * no specific class. */
44
    protected static $settingclassesmap = [
45
            'adminpresets_admin_setting_agedigitalconsentmap' => 'adminpresets_admin_setting_configtext',
46
            'adminpresets_admin_setting_configcolourpicker' => 'adminpresets_admin_setting_configtext',
47
            'adminpresets_admin_setting_configdirectory' => 'adminpresets_admin_setting_configtext',
48
            'adminpresets_admin_setting_configduration_with_advanced' => 'adminpresets_admin_setting_configtext_with_advanced',
49
            'adminpresets_admin_setting_configduration' => 'adminpresets_admin_setting_configtext',
50
            'adminpresets_admin_setting_configempty' => 'adminpresets_admin_setting_configtext',
51
            'adminpresets_admin_setting_configexecutable' => 'adminpresets_admin_setting_configtext',
52
            'adminpresets_admin_setting_configfile' => 'adminpresets_admin_setting_configtext',
53
            'adminpresets_admin_setting_confightmleditor' => 'adminpresets_admin_setting_configtext',
54
            'adminpresets_admin_setting_configmixedhostiplist' => 'adminpresets_admin_setting_configtext',
55
            'adminpresets_admin_setting_configmultiselect_modules' => 'adminpresets_admin_setting_configmultiselect_with_loader',
56
            'adminpresets_admin_setting_configpasswordunmask' => 'adminpresets_admin_setting_configtext',
57
            'adminpresets_admin_setting_configportlist' => 'adminpresets_admin_setting_configtext',
58
            'adminpresets_admin_setting_configselect_with_lock' => 'adminpresets_admin_setting_configselect',
59
            'adminpresets_admin_setting_configtext_trim_lower' => 'adminpresets_admin_setting_configtext',
60
            'adminpresets_admin_setting_configtext_with_maxlength' => 'adminpresets_admin_setting_configtext',
61
            'adminpresets_admin_setting_configtextarea' => 'adminpresets_admin_setting_configtext',
62
            'adminpresets_admin_setting_configthemepreset' => 'adminpresets_admin_setting_configselect',
63
            'adminpresets_admin_setting_countrycodes' => 'adminpresets_admin_setting_configtext',
64
            'adminpresets_admin_setting_courselist_frontpage' => 'adminpresets_admin_setting_configmultiselect_with_loader',
65
            'adminpresets_admin_setting_description' => 'adminpresets_admin_setting_configtext',
66
            'adminpresets_admin_setting_enablemobileservice' => 'adminpresets_admin_setting_configcheckbox',
67
            'adminpresets_admin_setting_filetypes' => 'adminpresets_admin_setting_configtext',
68
            'adminpresets_admin_setting_forcetimezone' => 'adminpresets_admin_setting_configselect',
69
            'adminpresets_admin_setting_grade_profilereport' => 'adminpresets_admin_setting_configmultiselect_with_loader',
70
            'adminpresets_admin_setting_langlist' => 'adminpresets_admin_setting_configtext',
71
            'adminpresets_admin_setting_my_grades_report' => 'adminpresets_admin_setting_configselect',
72
            'adminpresets_admin_setting_pickroles' => 'adminpresets_admin_setting_configmulticheckbox',
73
            'adminpresets_admin_setting_question_behaviour' => 'adminpresets_admin_setting_configmultiselect_with_loader',
74
            'adminpresets_admin_setting_regradingcheckbox' => 'adminpresets_admin_setting_configcheckbox',
75
            'adminpresets_admin_setting_scsscode' => 'adminpresets_admin_setting_configtext',
76
            'adminpresets_admin_setting_servertimezone' => 'adminpresets_admin_setting_configselect',
77
            'adminpresets_admin_setting_sitesetcheckbox' => 'adminpresets_admin_setting_configcheckbox',
78
            'adminpresets_admin_setting_sitesetselect' => 'adminpresets_admin_setting_configselect',
79
            'adminpresets_admin_setting_special_adminseesall' => 'adminpresets_admin_setting_configcheckbox',
80
            'adminpresets_admin_setting_special_backup_auto_destination' => 'adminpresets_admin_setting_configtext',
81
            'adminpresets_admin_setting_special_coursecontact' => 'adminpresets_admin_setting_configmulticheckbox',
82
            'adminpresets_admin_setting_special_coursemanager' => 'adminpresets_admin_setting_configmulticheckbox',
83
            'adminpresets_admin_setting_special_debug' => 'adminpresets_admin_setting_configmultiselect_with_loader',
84
            'adminpresets_admin_setting_special_frontpagedesc' => 'adminpresets_admin_setting_sitesettext',
85
            'adminpresets_admin_setting_special_gradebookroles' => 'adminpresets_admin_setting_configmulticheckbox',
86
            'adminpresets_admin_setting_special_gradeexport' => 'adminpresets_admin_setting_configmulticheckbox',
87
            'adminpresets_admin_setting_special_gradelimiting' => 'adminpresets_admin_setting_configcheckbox',
88
            'adminpresets_admin_setting_special_grademinmaxtouse' => 'adminpresets_admin_setting_configselect',
89
            'adminpresets_admin_setting_special_gradepointdefault' => 'adminpresets_admin_setting_configtext',
90
            'adminpresets_admin_setting_special_gradepointmax' => 'adminpresets_admin_setting_configtext',
91
            'adminpresets_admin_setting_special_registerauth' => 'adminpresets_admin_setting_configmultiselect_with_loader',
92
            'adminpresets_admin_setting_special_selectsetup' => 'adminpresets_admin_setting_configselect',
93
            'adminpresets_admin_settings_country_select' => 'adminpresets_admin_setting_configmultiselect_with_loader',
94
            'adminpresets_admin_settings_coursecat_select' => 'adminpresets_admin_setting_configmultiselect_with_loader',
95
            'adminpresets_admin_settings_h5plib_handler_select' => 'adminpresets_admin_setting_configselect',
96
            'adminpresets_admin_settings_num_course_sections' => 'adminpresets_admin_setting_configmultiselect_with_loader',
97
            'adminpresets_admin_settings_sitepolicy_handler_select' => 'adminpresets_admin_setting_configselect',
98
            'adminpresets_antivirus_clamav_pathtounixsocket_setting' => 'adminpresets_admin_setting_configtext',
99
            'adminpresets_antivirus_clamav_runningmethod_setting' => 'adminpresets_admin_setting_configselect',
100
            'adminpresets_antivirus_clamav_tcpsockethost_setting' => 'adminpresets_admin_setting_configtext',
101
            'adminpresets_auth_db_admin_setting_special_auth_configtext' => 'adminpresets_admin_setting_configtext',
102
            'adminpresets_auth_ldap_admin_setting_special_lowercase_configtext' => 'adminpresets_admin_setting_configtext',
103
            'adminpresets_auth_ldap_admin_setting_special_ntlm_configtext' => 'adminpresets_admin_setting_configtext',
104
            'adminpresets_auth_shibboleth_admin_setting_convert_data' => 'adminpresets_admin_setting_configtext',
105
            'adminpresets_auth_shibboleth_admin_setting_special_idp_configtextarea' => 'adminpresets_admin_setting_configtext',
106
            'adminpresets_auth_shibboleth_admin_setting_special_wayf_select' => 'adminpresets_admin_setting_configselect',
107
            'adminpresets_editor_atto_toolbar_setting' => 'adminpresets_admin_setting_configtext',
108
            'adminpresets_enrol_database_admin_setting_category' => 'adminpresets_admin_setting_configselect',
109
            'adminpresets_enrol_flatfile_role_setting' => 'adminpresets_admin_setting_configtext',
110
            'adminpresets_enrol_ldap_admin_setting_category' => 'adminpresets_admin_setting_configselect',
111
            'adminpresets_format_singleactivity_admin_setting_activitytype' => 'adminpresets_admin_setting_configselect',
112
            'adminpresets_qtype_multichoice_admin_setting_answernumbering' => 'adminpresets_admin_setting_configselect',
113
    ];
114
 
115
    /** @var array Relation between database fields and XML files. **/
116
    protected static $dbxmlrelations = [
117
        'name' => 'NAME',
118
        'comments' => 'COMMENTS',
119
        'timecreated' => 'PRESET_DATE',
120
        'site' => 'SITE_URL',
121
        'author' => 'AUTHOR',
122
        'moodleversion' => 'MOODLE_VERSION',
123
        'moodlerelease' => 'MOODLE_RELEASE'
124
    ];
125
 
126
    /** @var int Non-core preset */
127
    public const NONCORE_PRESET = 0;
128
 
129
    /** @var int Starter preset */
130
    public const STARTER_PRESET = 1;
131
 
132
    /** @var int Full preset */
133
    public const FULL_PRESET = 2;
134
 
135
    /**
136
     * Gets the system settings
137
     *
138
     * Loads the DB $CFG->prefix.'config' values and the
139
     * $CFG->prefix.'config_plugins' values and redirects
140
     * the flow through $this->get_settings()
141
     *
142
     * @return array $settings Array format $array['plugin']['settingname'] = settings_types child class
143
     */
144
    public function get_site_settings(): array {
145
        global $DB;
146
 
147
        // Db configs (to avoid multiple queries).
148
        $dbconfig = $DB->get_records_select('config', '', [], '', 'name, value');
149
 
150
        // Adding site settings in course table.
151
        $frontpagevalues = $DB->get_record_select('course', 'id = 1',
152
                [], 'fullname, shortname, summary');
153
        foreach ($frontpagevalues as $field => $value) {
154
            $dbconfig[$field] = new stdClass();
155
            $dbconfig[$field]->name = $field;
156
            $dbconfig[$field]->value = $value;
157
        }
158
        $sitedbsettings['none'] = $dbconfig;
159
 
160
        // Config plugins.
161
        $configplugins = $DB->get_records('config_plugins');
162
        foreach ($configplugins as $configplugin) {
163
            $sitedbsettings[$configplugin->plugin][$configplugin->name] = new stdClass();
164
            $sitedbsettings[$configplugin->plugin][$configplugin->name]->name = $configplugin->name;
165
            $sitedbsettings[$configplugin->plugin][$configplugin->name]->value = $configplugin->value;
166
        }
167
        // Get an array with the common format.
168
        return $this->get_settings($sitedbsettings, true, []);
169
    }
170
 
171
    /**
172
     * Constructs an array with all the system settings
173
     *
174
     * If a setting value can't be found on the DB it considers
175
     * the default value as the setting value
176
     *
177
     * Settings without plugin are marked as 'none' in the plugin field
178
     *
179
     * Returns an standarized settings array format.
180
     *
181
     * @param array $dbsettings Standarized array,
182
     * format $array['plugin']['name'] = obj('name'=>'settingname', 'value'=>'settingvalue')
183
     * @param boolean $sitedbvalues Indicates if $dbsettings comes from the site db or not
184
     * @param array $settings Array format $array['plugin']['settingname'] = settings_types child class
185
     * @param array|false $children Array of admin_category children or false
186
     * @return \core_adminpresets\local\setting\adminpresets_setting[][] Array format
187
     *    $array['plugin']['settingname'] = adminpresets_setting child class
188
     */
189
    public function get_settings(array $dbsettings, bool $sitedbvalues = false, array $settings = [], $children = false): array {
190
        global $DB;
191
 
192
        // If there are no children, load admin tree and iterate through.
193
        if (!$children) {
194
            $this->adminroot = admin_get_root(false, true);
195
            $children = $this->adminroot->children;
196
        }
197
 
198
        // Iteates through children.
199
        foreach ($children as $key => $child) {
200
 
201
            // We must search category children.
202
            if (is_a($child, 'admin_category')) {
203
 
204
                if ($child->children) {
205
                    $settings = $this->get_settings($dbsettings, $sitedbvalues, $settings, $child->children);
206
                }
207
 
208
                // Settings page.
209
            } else if (is_a($child, 'admin_settingpage')) {
210
 
211
                if (property_exists($child, 'settings')) {
212
 
213
                    foreach ($child->settings as $values) {
214
                        $settingname = $values->name;
215
 
216
                        unset($settingvalue);
217
 
218
                        // Look for his config value.
219
                        if ($values->plugin == '') {
220
                            $values->plugin = 'none';
221
                        }
222
 
223
                        if (!empty($dbsettings[$values->plugin][$settingname])) {
224
                            $settingvalue = $dbsettings[$values->plugin][$settingname]->value;
225
                        }
226
 
227
                        // If no db value found default value.
228
                        if ($sitedbvalues && !isset($settingvalue)) {
229
                            // For settings with multiple values.
230
                            if (is_array($values->defaultsetting)) {
231
 
232
                                if (isset($values->defaultsetting['value'])) {
233
                                    $settingvalue = $values->defaultsetting['value'];
234
                                    // Configtime case, does not have a 'value' default setting.
235
                                } else {
236
                                    $settingvalue = 0;
237
                                }
238
                            } else {
239
                                $settingvalue = $values->defaultsetting;
240
                            }
241
                        }
242
 
243
                        // If there aren't any value loaded, skip that setting.
244
                        if (!isset($settingvalue)) {
245
                            continue;
246
                        }
247
                        // If there is no setting class defined continue.
248
                        if (!$setting = $this->get_setting($values, $settingvalue)) {
249
                            continue;
250
                        }
251
 
252
                        // Settings_types childs with.
253
                        // attributes provides an attributes array.
254
                        if ($attributes = $setting->get_attributes()) {
255
 
256
                            // Look for settings attributes if it is a presets.
257
                            if (!$sitedbvalues) {
258
                                $itemid = $dbsettings[$values->plugin][$settingname]->itemid;
259
                                $attrs = $DB->get_records('adminpresets_it_a',
260
                                        ['itemid' => $itemid], '', 'name, value');
261
                            }
262
                            foreach ($attributes as $defaultvarname => $varname) {
263
 
264
                                unset($attributevalue);
265
 
266
                                // Settings from site.
267
                                if ($sitedbvalues) {
268
                                    if (!empty($dbsettings[$values->plugin][$varname])) {
269
                                        $attributevalue = $dbsettings[$values->plugin][$varname]->value;
270
                                    }
271
 
272
                                    // Settings from a preset.
273
                                } else if (!$sitedbvalues && isset($attrs[$varname])) {
274
                                    $attributevalue = $attrs[$varname]->value;
275
                                }
276
 
277
                                // If no value found, default value,
278
                                // But we may not have a default value for the attribute.
279
                                if (!isset($attributevalue) && !empty($values->defaultsetting[$defaultvarname])) {
280
                                    $attributevalue = $values->defaultsetting[$defaultvarname];
281
                                }
282
 
283
                                // If there is no even a default for this setting will be empty.
284
                                // So we do nothing in this case.
285
                                if (isset($attributevalue)) {
286
                                    $setting->set_attribute_value($varname, $attributevalue);
287
                                }
288
                            }
289
                        }
290
 
291
                        // Adding to general settings array.
292
                        $settings[$values->plugin][$settingname] = $setting;
293
                    }
294
                }
295
            }
296
        }
297
 
298
        return $settings;
299
    }
300
 
301
    /**
302
     * Returns the class type object
303
     *
304
     * @param object $settingdata Setting data
305
     * @param mixed $currentvalue
306
     * @return mixed
307
     */
308
    public function get_setting($settingdata, $currentvalue) {
309
 
310
        $classname = null;
311
 
312
        // Getting the appropriate class to get the correct setting value.
313
        $settingtype = get_class($settingdata);
314
        // Check if it is a setting from a plugin.
315
        $namespacedata = explode('\\', $settingtype);
316
        if (count($namespacedata) > 1) {
317
            $plugindata = explode('_', $namespacedata[0]);
318
            $settingtype = end($namespacedata);
319
        } else {
320
            $plugindata = explode('_', $settingtype, 2);
321
        }
322
 
323
        $types = \core_component::get_plugin_types();
324
        if (array_key_exists($plugindata[0], $types)) {
325
            $plugins = \core_component::get_plugin_list($plugindata[0]);
326
            if (array_key_exists($plugindata[1], $plugins)) {
327
                // Check if there is a specific class for this plugin admin setting.
328
                $settingname = 'adminpresets_' . $settingtype;
329
                $classname = "\\$plugindata[0]_$plugindata[1]\\adminpresets\\$settingname";
330
                if (!class_exists($classname)) {
331
                    $classname = null;
332
                }
333
            }
334
        } else {
335
            $settingname = 'adminpresets_' . $settingtype;
336
            $classname = '\\core_adminpresets\\local\\setting\\' . $settingname;
337
            if (!class_exists($classname)) {
338
                // Check if there is some mapped class that should be used for this setting.
339
                $classname = self::get_settings_class($settingname);
340
            }
341
        }
342
 
343
        if (is_null($classname)) {
344
            // Return the default setting class if there is no specific class for this setting.
345
            $classname = '\\core_adminpresets\\local\\setting\\adminpresets_setting';
346
        }
347
 
348
        return new $classname($settingdata, $currentvalue);
349
    }
350
 
351
    /**
352
     * Returns the settings class mapped to the defined $classname or null if it doesn't exist any associated class.
353
     *
354
     * @param string $classname The classname to get the mapped class.
355
     * @return string|null
356
     */
357
    public static function get_settings_class(string $classname): ?string {
358
        if (array_key_exists($classname, self::$settingclassesmap)) {
359
            return '\\core_adminpresets\\local\\setting\\' . self::$settingclassesmap[$classname];
360
        }
361
 
362
        return null;
363
    }
364
 
365
    /**
366
     * Gets the standarized settings array from DB records
367
     *
368
     * @param array $dbsettings Array of objects
369
     * @return   array Standarized array,
370
     * format $array['plugin']['name'] = obj('name'=>'settingname', 'value'=>'settingvalue')
371
     */
372
    public function get_settings_from_db(array $dbsettings): array {
373
        $settings = [];
374
 
375
        if (!$dbsettings) {
376
            return $settings;
377
        }
378
 
379
        foreach ($dbsettings as $dbsetting) {
380
            $settings[$dbsetting->plugin][$dbsetting->name] = new stdClass();
381
            $settings[$dbsetting->plugin][$dbsetting->name]->itemid = $dbsetting->id;
382
            $settings[$dbsetting->plugin][$dbsetting->name]->name = $dbsetting->name;
383
            $settings[$dbsetting->plugin][$dbsetting->name]->value = $dbsetting->value;
384
        }
385
 
386
        return $settings;
387
    }
388
 
389
 
390
    /**
391
     * Apply a given preset.
392
     *
393
     * @param int $presetid The preset identifier to apply.
394
     * @param bool $simulate Whether this is a simulation or not.
395
     * @return array List with an array with the applied settings and another with the skipped ones.
396
     */
397
    public function apply_preset(int $presetid, bool $simulate = false): array {
398
        global $DB;
399
 
400
        if (!$DB->get_record('adminpresets', ['id' => $presetid])) {
401
            throw new moodle_exception('errornopreset', 'core_adminpresets');
402
        }
403
 
404
        // Apply preset settings.
405
        [$settingsapplied, $settingsskipped, $appid] = $this->apply_settings($presetid, $simulate);
406
 
407
        // Set plugins visibility.
408
        [$pluginsapplied, $pluginsskipped] = $this->apply_plugins($presetid, $simulate, $appid);
409
 
410
        $applied = array_merge($settingsapplied, $pluginsapplied);
411
        $skipped = array_merge($settingsskipped, $pluginsskipped);
412
 
413
        if (!$simulate) {
414
            // Store it in a config setting as the last preset applied.
415
            set_config('lastpresetapplied', $presetid, 'adminpresets');
416
        }
417
 
418
        return [$applied, $skipped];
419
    }
420
 
421
    /**
422
     * Create a preset with the current settings and plugins information.
423
     *
424
     * @param \stdClass $data Preset info, such as name or description, to be used when creating the preset with the current
425
     *                 settings and plugins.
426
     * @return array List with an the presetid created (int), a boolean to define if any setting has been found and
427
     *               another boolean to specify if any plugin has been found.
428
     */
429
    public function export_preset(stdClass $data): array {
430
        global $DB;
431
 
432
        // Admin_preset record.
433
        $presetdata = [
434
            'name' => $data->name ?? '',
435
            'comments' => !empty($data->comments) ? $data->comments['text'] : '',
436
            'author' => $data->author ?? '',
437
        ];
438
        if (!$presetid = helper::create_preset($presetdata)) {
439
            throw new moodle_exception('errorinserting', 'core_adminpresets');
440
        }
441
 
442
        // Store settings.
443
        $settingsfound = false;
444
 
445
        // Site settings.
446
        $sitesettings = $this->get_site_settings();
447
 
448
        // Sensible settings.
449
        $sensiblesettings = explode(',', str_replace(' ', '', get_config('adminpresets', 'sensiblesettings')));
450
        $sensiblesettings = array_combine($sensiblesettings, $sensiblesettings);
451
        foreach ($sitesettings as $plugin => $pluginsettings) {
452
            foreach ($pluginsettings as $settingname => $sitesetting) {
453
                // Avoid sensible data.
454
                if (empty($data->includesensiblesettings) && !empty($sensiblesettings["$settingname@@$plugin"])) {
455
                    continue;
456
                }
457
 
458
                $setting = new stdClass();
459
                $setting->adminpresetid = $presetid;
460
                $setting->plugin = $plugin;
461
                $setting->name = $settingname;
462
                $setting->value = $sitesetting->get_value();
463
                if (!$setting->id = $DB->insert_record('adminpresets_it', $setting)) {
464
                    throw new moodle_exception('errorinserting', 'core_adminpresets');
465
                }
466
 
467
                // Setting attributes must also be exported.
468
                if ($attributes = $sitesetting->get_attributes_values()) {
469
                    foreach ($attributes as $attname => $attvalue) {
470
                        $attr = new stdClass();
471
                        $attr->itemid = $setting->id;
472
                        $attr->name = $attname;
473
                        $attr->value = $attvalue;
474
 
475
                        $DB->insert_record('adminpresets_it_a', $attr);
476
                    }
477
                }
478
                $settingsfound = true;
479
            }
480
        }
481
 
482
        // Store plugins visibility (enabled/disabled).
483
        $pluginsfound = false;
484
        $pluginmanager = \core_plugin_manager::instance();
485
        $types = $pluginmanager->get_plugin_types();
486
        foreach ($types as $plugintype => $notused) {
487
            $plugins = $pluginmanager->get_present_plugins($plugintype);
488
            $pluginclass = \core_plugin_manager::resolve_plugininfo_class($plugintype);
489
            if (!empty($plugins)) {
490
                foreach ($plugins as $pluginname => $plugin) {
491
                    $entry = new stdClass();
492
                    $entry->adminpresetid = $presetid;
493
                    $entry->plugin = $plugintype;
494
                    $entry->name = $pluginname;
495
                    $entry->enabled = $pluginclass::get_enabled_plugin($pluginname);
496
 
497
                    $DB->insert_record('adminpresets_plug', $entry);
498
                    $pluginsfound = true;
499
                }
500
            }
501
        }
502
 
503
        // If there are no settings nor plugins, the admin preset record should be removed.
504
        if (!$settingsfound && !$pluginsfound) {
505
            $DB->delete_records('adminpresets', ['id' => $presetid]);
506
            $presetid = null;
507
        }
508
 
509
        return [$presetid, $settingsfound, $pluginsfound];
510
    }
511
 
512
    /**
513
     * Create the XML content for a given preset.
514
     *
515
     * @param int $presetid The preset to download.
516
     * @return array List with the XML content (string) and a filename proposal based on the preset name (string).
517
     */
518
    public function download_preset(int $presetid): array {
519
        global $DB;
520
 
521
        if (!$preset = $DB->get_record('adminpresets', ['id' => $presetid])) {
522
            throw new moodle_exception('errornopreset', 'core_adminpresets');
523
        }
524
 
525
        // Start.
526
        $xmloutput = new memory_xml_output();
527
        $xmlwriter = new xml_writer($xmloutput);
528
        $xmlwriter->start();
529
 
530
        // Preset data.
531
        $xmlwriter->begin_tag('PRESET');
532
        foreach (static::$dbxmlrelations as $dbname => $xmlname) {
533
            $xmlwriter->full_tag($xmlname, $preset->$dbname);
534
        }
535
 
536
        // We ride through the settings array.
537
        $items = $DB->get_records('adminpresets_it', ['adminpresetid' => $preset->id]);
538
        $allsettings = $this->get_settings_from_db($items);
539
        if ($allsettings) {
540
            $xmlwriter->begin_tag('ADMIN_SETTINGS');
541
 
542
            foreach ($allsettings as $plugin => $settings) {
543
                $tagname = strtoupper($plugin);
544
 
545
                // To aviod xml slash problems.
546
                if (strstr($tagname, '/') != false) {
547
                    $tagname = str_replace('/', '__', $tagname);
548
                }
549
 
550
                $xmlwriter->begin_tag($tagname);
551
 
552
                // One tag for each plugin setting.
553
                if (!empty($settings)) {
554
                    $xmlwriter->begin_tag('SETTINGS');
555
                    foreach ($settings as $setting) {
556
                        // Unset the tag attributes string.
557
                        $attributes = [];
558
 
559
                        // Getting setting attributes, if present.
560
                        $attrs = $DB->get_records('adminpresets_it_a', ['itemid' => $setting->itemid]);
561
                        if ($attrs) {
562
                            foreach ($attrs as $attr) {
563
                                $attributes[$attr->name] = $attr->value;
564
                            }
565
                        }
566
 
567
                        $xmlwriter->full_tag(strtoupper($setting->name), $setting->value, $attributes);
568
                    }
569
 
570
                    $xmlwriter->end_tag('SETTINGS');
571
                }
572
 
573
                $xmlwriter->end_tag(strtoupper($tagname));
574
            }
575
 
576
            $xmlwriter->end_tag('ADMIN_SETTINGS');
577
        }
578
 
579
        // We ride through the plugins array.
580
        $data = $DB->get_records('adminpresets_plug', ['adminpresetid' => $preset->id]);
581
        if ($data) {
582
            $plugins = [];
583
            foreach ($data as $plugin) {
584
                $plugins[$plugin->plugin][] = $plugin;
585
            }
586
 
587
            $xmlwriter->begin_tag('PLUGINS');
588
 
589
            foreach ($plugins as $plugintype => $plugintypes) {
590
                $tagname = strtoupper($plugintype);
591
                $xmlwriter->begin_tag($tagname);
592
 
593
                foreach ($plugintypes as $plugin) {
594
                    $xmlwriter->full_tag(strtoupper($plugin->name), $plugin->enabled);
595
                }
596
 
597
                $xmlwriter->end_tag(strtoupper($tagname));
598
            }
599
 
600
            $xmlwriter->end_tag('PLUGINS');
601
        }
602
 
603
        // End.
604
        $xmlwriter->end_tag('PRESET');
605
        $xmlwriter->stop();
606
        $xmlstr = $xmloutput->get_allcontents();
607
 
608
        $filename = addcslashes($preset->name, '"') . '.xml';
609
 
610
        return [$xmlstr, $filename];
611
    }
612
 
613
    /**
614
     * Import a given XML preset.
615
     *
616
     * @param string $xmlcontent The XML context with the preset to be imported.
617
     * @param string|null $presetname The preset name that will overwrite the one given in the XML file.
618
     * @return array List with an the XML element (SimpleXMLElement|null), the imported preset (stdClass|null), a boolean
619
     *               to define if any setting has been found and another boolean to specify if any plugin has been found.
620
     */
621
    public function import_preset(string $xmlcontent, ?string $presetname = null): array {
622
        global $DB, $USER;
623
 
624
        $settingsfound = false;
625
        $pluginsfound = false;
626
 
627
        try {
628
            $xml = simplexml_load_string($xmlcontent);
629
        } catch (\Exception $exception) {
630
            $xml = false;
631
        }
632
        if (!$xml) {
633
            return [null, null, $settingsfound, $pluginsfound];
634
        }
635
 
636
        // Prepare the preset info.
637
        $preset = new stdClass();
638
        foreach (static::$dbxmlrelations as $dbname => $xmlname) {
639
            $preset->$dbname = (String) $xml->$xmlname;
640
        }
641
        $preset->userid = $USER->id;
642
        $preset->timeimported = time();
643
 
644
        // Overwrite preset name.
645
        if (!empty($presetname)) {
646
            $preset->name = $presetname;
647
        }
648
 
649
        // Create the preset.
650
        if (!$preset->id = $DB->insert_record('adminpresets', $preset)) {
651
            throw new moodle_exception('errorinserting', 'core_adminpresets');
652
        }
653
 
654
        // Process settings.
655
        $sitesettings = $this->get_site_settings();
656
        $xmladminsettings = $xml->ADMIN_SETTINGS[0];
657
        foreach ($xmladminsettings as $plugin => $settings) {
658
            $plugin = strtolower($plugin);
659
            if (strstr($plugin, '__') != false) {
660
                $plugin = str_replace('__', '/', $plugin);
661
            }
662
 
663
            $pluginsettings = $settings->SETTINGS[0];
664
            if ($pluginsettings) {
665
                foreach ($pluginsettings->children() as $name => $setting) {
666
                    $name = strtolower($name);
667
 
668
                    // Default to ''.
669
                    if ($setting->__toString() === false) {
670
                        $value = '';
671
                    } else {
672
                        $value = $setting->__toString();
673
                    }
674
 
675
                    if (empty($sitesettings[$plugin][$name])) {
676
                        debugging('Setting ' . $plugin . '/' . $name . ' not supported by this Moodle version', DEBUG_DEVELOPER);
677
                        continue;
678
                    }
679
 
680
                    // Cleaning the setting value.
681
                    if (!$presetsetting = $this->get_setting($sitesettings[$plugin][$name]->get_settingdata(), $value)) {
682
                        debugging('Setting ' . $plugin . '/' . $name . ' not implemented', DEBUG_DEVELOPER);
683
                        continue;
684
                    }
685
 
686
                    $settingsfound = true;
687
 
688
                    // New item.
689
                    $item = new stdClass();
690
                    $item->adminpresetid = $preset->id;
691
                    $item->plugin = $plugin;
692
                    $item->name = $name;
693
                    $item->value = $presetsetting->get_value();
694
 
695
                    // Insert preset item.
696
                    if (!$item->id = $DB->insert_record('adminpresets_it', $item)) {
697
                        throw new moodle_exception('errorinserting', 'core_adminpresets');
698
                    }
699
 
700
                    // Add setting attributes.
701
                    if ($setting->attributes() && ($itemattributes = $presetsetting->get_attributes())) {
702
                        foreach ($setting->attributes() as $attrname => $attrvalue) {
703
                            $itemattributenames = array_flip($itemattributes);
704
 
705
                            // Check the attribute existence.
706
                            if (!isset($itemattributenames[$attrname])) {
707
                                debugging('The ' . $plugin . '/' . $name . ' attribute ' . $attrname .
708
                                        ' is not supported by this Moodle version', DEBUG_DEVELOPER);
709
                                continue;
710
                            }
711
 
712
                            $attr = new stdClass();
713
                            $attr->itemid = $item->id;
714
                            $attr->name = $attrname;
715
                            $attr->value = $attrvalue->__toString();
716
                            $DB->insert_record('adminpresets_it_a', $attr);
717
                        }
718
                    }
719
                }
720
            }
721
        }
722
 
723
        // Process plugins.
724
        if ($xml->PLUGINS) {
725
            $xmlplugins = $xml->PLUGINS[0];
726
            foreach ($xmlplugins as $plugin => $plugins) {
727
                $pluginname = strtolower($plugin);
728
                foreach ($plugins->children() as $name => $plugin) {
729
                    $pluginsfound = true;
730
 
731
                    // New plugin.
732
                    $entry = new stdClass();
733
                    $entry->adminpresetid = $preset->id;
734
                    $entry->plugin = $pluginname;
735
                    $entry->name = strtolower($name);
736
                    $entry->enabled = $plugin->__toString();
737
 
738
                    // Insert plugin.
739
                    if (!$entry->id = $DB->insert_record('adminpresets_plug', $entry)) {
740
                        throw new moodle_exception('errorinserting', 'core_adminpresets');
741
                    }
742
                }
743
            }
744
        }
745
 
746
        // If there are no valid or selected settings we should delete the admin preset record.
747
        if (!$settingsfound && !$pluginsfound) {
748
            $DB->delete_records('adminpresets', ['id' => $preset->id]);
749
            $preset = null;
750
        }
751
 
752
        return [$xml, $preset, $settingsfound, $pluginsfound];
753
    }
754
 
755
    /**
756
     * Delete given preset.
757
     *
758
     * @param int $presetid Preset identifier to delete.
759
     * @return void
760
     */
761
    public function delete_preset(int $presetid): void {
762
        global $DB;
763
 
764
        // Check the preset exists (cannot delete the pre-installed core "Starter" and "Full" presets).
765
        $preset = $DB->get_record('adminpresets', ['id' => $presetid, 'iscore' => self::NONCORE_PRESET]);
766
        if (!$preset) {
767
            throw new moodle_exception('errordeleting', 'core_adminpresets');
768
        }
769
 
770
        // Deleting the preset applications.
771
        if ($previouslyapplied = $DB->get_records('adminpresets_app', ['adminpresetid' => $presetid], 'id')) {
772
            $appids = array_keys($previouslyapplied);
773
            list($insql, $inparams) = $DB->get_in_or_equal($appids);
774
            $DB->delete_records_select('adminpresets_app_it', "adminpresetapplyid $insql", $inparams);
775
            $DB->delete_records_select('adminpresets_app_it_a', "adminpresetapplyid $insql", $inparams);
776
            $DB->delete_records_select('adminpresets_app_plug', "adminpresetapplyid $insql", $inparams);
777
 
778
            if (!$DB->delete_records('adminpresets_app', ['adminpresetid' => $presetid])) {
779
                throw new moodle_exception('errordeleting', 'core_adminpresets');
780
            }
781
        }
782
 
783
        // Getting items ids and remove advanced items associated to them.
784
        $items = $DB->get_records('adminpresets_it', ['adminpresetid' => $presetid], 'id');
785
        if (!empty($items)) {
786
            $itemsid = array_keys($items);
787
            list($insql, $inparams) = $DB->get_in_or_equal($itemsid);
788
            $DB->delete_records_select('adminpresets_it_a', "itemid $insql", $inparams);
789
        }
790
 
791
        if (!$DB->delete_records('adminpresets_it', ['adminpresetid' => $presetid])) {
792
            throw new moodle_exception('errordeleting', 'core_adminpresets');
793
        }
794
 
795
        // Delete plugins.
796
        if (!$DB->delete_records('adminpresets_plug', ['adminpresetid' => $presetid])) {
797
            throw new moodle_exception('errordeleting', 'core_adminpresets');
798
        }
799
 
800
        // Delete preset.
801
        if (!$DB->delete_records('adminpresets', ['id' => $presetid])) {
802
            throw new moodle_exception('errordeleting', 'core_adminpresets');
803
        }
804
    }
805
 
806
    /**
807
     * Revert a given preset applied previously.
808
     * It backs settings and plugins to their original state before applying the presset and removes
809
     * the applied preset information from DB.
810
     *
811
     * @param int $presetappid The appplied preset identifier to be reverted.
812
     * @return array List with the presetapp removed (or null if there was some error), an array with the rollback settings/plugins
813
     *               changed and an array with the failures.
814
     */
815
    public function revert_preset(int $presetappid): array {
816
        global $DB;
817
 
818
        // To store rollback results.
819
        $presetapp = null;
820
        $rollback = [];
821
        $failures = [];
822
 
823
        // Actual settings.
824
        $sitesettings = $this->get_site_settings();
825
 
826
        if (!$DB->get_record('adminpresets_app', ['id' => $presetappid])) {
827
            throw new moodle_exception('wrongid', 'core_adminpresets');
828
        }
829
 
830
        // Items.
831
        $itemsql = "SELECT cl.id, cl.plugin, cl.name, cl.value, cl.oldvalue, ap.adminpresetapplyid
832
                      FROM {adminpresets_app_it} ap
833
                      JOIN {config_log} cl ON cl.id = ap.configlogid
834
                     WHERE ap.adminpresetapplyid = :presetid";
835
        $itemchanges = $DB->get_records_sql($itemsql, ['presetid' => $presetappid]);
836
        if ($itemchanges) {
837
            foreach ($itemchanges as $change) {
838
                if ($change->plugin == '') {
839
                    $change->plugin = 'none';
840
                }
841
 
842
                // Admin setting.
843
                if (!empty($sitesettings[$change->plugin][$change->name])) {
844
                    $actualsetting = $sitesettings[$change->plugin][$change->name];
845
                    $oldsetting = $this->get_setting($actualsetting->get_settingdata(), $change->oldvalue);
846
 
847
                    $visiblepluginname = $oldsetting->get_settingdata()->plugin;
848
                    if ($visiblepluginname == 'none') {
849
                        $visiblepluginname = 'core';
850
                    }
851
                    $contextdata = [
852
                        'plugin' => $visiblepluginname,
853
                        'visiblename' => $oldsetting->get_settingdata()->visiblename,
854
                        'oldvisiblevalue' => $actualsetting->get_visiblevalue(),
855
                        'visiblevalue' => $oldsetting->get_visiblevalue()
856
                    ];
857
 
858
                    // Check if the actual value is the same set by the preset.
859
                    if ($change->value == $actualsetting->get_value()) {
860
                        $oldsetting->save_value();
861
 
862
                        // Output table.
863
                        $rollback[] = $contextdata;
864
 
865
                        // Deleting the adminpreset applied item instance.
866
                        $deletewhere = [
867
                            'adminpresetapplyid' => $change->adminpresetapplyid,
868
                            'configlogid' => $change->id,
869
                        ];
870
                        $DB->delete_records('adminpresets_app_it', $deletewhere);
871
 
872
                    } else {
873
                        $failures[] = $contextdata;
874
                    }
875
                }
876
            }
877
        }
878
 
879
        // Attributes.
880
        $attrsql = "SELECT cl.id, cl.plugin, cl.name, cl.value, cl.oldvalue, ap.itemname, ap.adminpresetapplyid
881
                      FROM {adminpresets_app_it_a} ap
882
                      JOIN {config_log} cl ON cl.id = ap.configlogid
883
                     WHERE ap.adminpresetapplyid = :presetid";
884
        $attrchanges = $DB->get_records_sql($attrsql, ['presetid' => $presetappid]);
885
        if ($attrchanges) {
886
            foreach ($attrchanges as $change) {
887
                if ($change->plugin == '') {
888
                    $change->plugin = 'none';
889
                }
890
 
891
                // Admin setting of the attribute item.
892
                if (!empty($sitesettings[$change->plugin][$change->itemname])) {
893
                    // Getting the attribute item.
894
                    $actualsetting = $sitesettings[$change->plugin][$change->itemname];
895
 
896
                    $oldsetting = $this->get_setting($actualsetting->get_settingdata(), $actualsetting->get_value());
897
                    $oldsetting->set_attribute_value($change->name, $change->oldvalue);
898
 
899
                    $varname = $change->plugin . '_' . $change->name;
900
 
901
                    // Check if the actual value is the same set by the preset.
902
                    $actualattributes = $actualsetting->get_attributes_values();
903
                    if ($change->value == $actualattributes[$change->name]) {
904
                        $oldsetting->save_attributes_values();
905
 
906
                        // Output table.
907
                        $visiblepluginname = $oldsetting->get_settingdata()->plugin;
908
                        if ($visiblepluginname == 'none') {
909
                            $visiblepluginname = 'core';
910
                        }
911
                        $rollback[] = [
912
                            'plugin' => $visiblepluginname,
913
                            'visiblename' => $oldsetting->get_settingdata()->visiblename,
914
                            'oldvisiblevalue' => $actualsetting->get_visiblevalue(),
915
                            'visiblevalue' => $oldsetting->get_visiblevalue()
916
                        ];
917
 
918
                        // Deleting the adminpreset applied item attribute instance.
919
                        $deletewhere = [
920
                            'adminpresetapplyid' => $change->adminpresetapplyid,
921
                            'configlogid' => $change->id,
922
                        ];
923
                        $DB->delete_records('adminpresets_app_it_a', $deletewhere);
924
 
925
                    } else {
926
                        $visiblepluginname = $oldsetting->get_settingdata()->plugin;
927
                        if ($visiblepluginname == 'none') {
928
                            $visiblepluginname = 'core';
929
                        }
930
                        $failures[] = [
931
                            'plugin' => $visiblepluginname,
932
                            'visiblename' => $oldsetting->get_settingdata()->visiblename,
933
                            'oldvisiblevalue' => $actualsetting->get_visiblevalue(),
934
                            'visiblevalue' => $oldsetting->get_visiblevalue()
935
                        ];
936
                    }
937
                }
938
            }
939
        }
940
 
941
        // Plugins.
942
        $plugins = $DB->get_records('adminpresets_app_plug', ['adminpresetapplyid' => $presetappid]);
943
        if ($plugins) {
944
            $pluginmanager = \core_plugin_manager::instance();
945
            foreach ($plugins as $plugin) {
946
                $pluginclass = \core_plugin_manager::resolve_plugininfo_class($plugin->plugin);
947
                $pluginclass::enable_plugin($plugin->name, (int) $plugin->oldvalue);
948
 
949
                // Get the plugininfo object for this plugin, to get its proper visible name.
950
                $plugininfo = $pluginmanager->get_plugin_info($plugin->plugin . '_' . $plugin->name);
951
                if ($plugininfo != null) {
952
                    $visiblename = $plugininfo->displayname;
953
                } else {
954
                    $visiblename = $plugin->plugin . '_' . $plugin->name;
955
                }
956
 
957
                // Output table.
958
                $rollback[] = [
959
                    'plugin' => $plugin->plugin,
960
                    'visiblename' => $visiblename,
961
                    'oldvisiblevalue' => $plugin->value,
962
                    'visiblevalue' => $plugin->oldvalue,
963
                ];
964
            }
965
            $DB->delete_records('adminpresets_app_plug', ['adminpresetapplyid' => $presetappid]);
966
        }
967
 
968
        // Delete application if no items nor attributes nor plugins of the application remains.
969
        if (!$DB->get_records('adminpresets_app_it', ['adminpresetapplyid' => $presetappid]) &&
970
                !$DB->get_records('adminpresets_app_it_a', ['adminpresetapplyid' => $presetappid]) &&
971
                !$DB->get_records('adminpresets_app_plug', ['adminpresetapplyid' => $presetappid])) {
972
 
973
            $presetapp = $DB->get_record('adminpresets_app', ['id' => $presetappid]);
974
            $DB->delete_records('adminpresets_app', ['id' => $presetappid]);
975
        }
976
 
977
        return [$presetapp, $rollback, $failures];
978
    }
979
 
980
    /**
981
     * Apply settings from a preset.
982
     *
983
     * @param int $presetid The preset identifier to apply.
984
     * @param bool $simulate Whether this is a simulation or not.
985
     * @param int|null $adminpresetapplyid The identifier of the adminpresetapply or null if it hasn't been created previously.
986
     * @return array List with an array with the applied settings, another with the skipped ones and the adminpresetapplyid.
987
     */
988
    protected function apply_settings(int $presetid, bool $simulate = false, ?int $adminpresetapplyid = null): array {
989
        global $DB, $USER;
990
 
991
        $applied = [];
992
        $skipped = [];
993
        if (!$items = $DB->get_records('adminpresets_it', ['adminpresetid' => $presetid])) {
994
            return [$applied, $skipped, $adminpresetapplyid];
995
        }
996
 
997
        $presetdbsettings = $this->get_settings_from_db($items);
998
        // Standarized format: $array['plugin']['settingname'] = child class.
999
        $presetsettings = $this->get_settings($presetdbsettings, false, []);
1000
 
1001
        // Standarized format: $array['plugin']['settingname'] = child class.
1002
        $siteavailablesettings = $this->get_site_settings();
1003
 
1004
        // Set settings values.
1005
        foreach ($presetsettings as $plugin => $pluginsettings) {
1006
            foreach ($pluginsettings as $settingname => $presetsetting) {
1007
                $updatesetting = false;
1008
 
1009
                // Current value (which will become old value if the setting is legit to be applied).
1010
                $sitesetting = $siteavailablesettings[$plugin][$settingname];
1011
 
1012
                // Wrong setting, set_value() method has previously cleaned the value.
1013
                if ($sitesetting->get_value() === false) {
1014
                    debugging($presetsetting->get_settingdata()->plugin . '/' . $presetsetting->get_settingdata()->name .
1015
                            ' setting has a wrong value!', DEBUG_DEVELOPER);
1016
                    continue;
1017
                }
1018
 
1019
                // If the new value is different the setting must be updated.
1020
                if ($presetsetting->get_value() != $sitesetting->get_value()) {
1021
                    $updatesetting = true;
1022
                }
1023
 
1024
                // If one of the setting attributes values is different, setting must also be updated.
1025
                if ($presetsetting->get_attributes_values()) {
1026
 
1027
                    $siteattributesvalues = $presetsetting->get_attributes_values();
1028
                    foreach ($presetsetting->get_attributes_values() as $attributename => $attributevalue) {
1029
 
1030
                        if ($attributevalue !== $siteattributesvalues[$attributename]) {
1031
                            $updatesetting = true;
1032
                        }
1033
                    }
1034
                }
1035
 
1036
                $visiblepluginname = $presetsetting->get_settingdata()->plugin;
1037
                if ($visiblepluginname == 'none') {
1038
                    $visiblepluginname = 'core';
1039
                }
1040
                $data = [
1041
                    'plugin' => $visiblepluginname,
1042
                    'visiblename' => $presetsetting->get_settingdata()->visiblename,
1043
                    'visiblevalue' => $presetsetting->get_visiblevalue(),
1044
                ];
1045
 
1046
                // Saving data.
1047
                if ($updatesetting) {
1048
                    // The preset application it's only saved when differences (in their values) are found.
1049
                    if (empty($applieditem)) {
1050
                        // Save the preset application and store the preset applied id.
1051
                        $presetapplied = new stdClass();
1052
                        $presetapplied->adminpresetid = $presetid;
1053
                        $presetapplied->userid = $USER->id;
1054
                        $presetapplied->time = time();
1055
                        if (!$simulate && !$adminpresetapplyid = $DB->insert_record('adminpresets_app', $presetapplied)) {
1056
                            throw new moodle_exception('errorinserting', 'core_adminpresets');
1057
                        }
1058
                    }
1059
 
1060
                    // Implemented this way because the config_write method of admin_setting class does not return the
1061
                    // config_log inserted id.
1062
                    $applieditem = new stdClass();
1063
                    $applieditem->adminpresetapplyid = $adminpresetapplyid;
1064
                    if (!$simulate && $applieditem->configlogid = $presetsetting->save_value()) {
1065
                        $DB->insert_record('adminpresets_app_it', $applieditem);
1066
                    }
1067
 
1068
                    // For settings with multiple values.
1069
                    if (!$simulate && $attributeslogids = $presetsetting->save_attributes_values()) {
1070
                        foreach ($attributeslogids as $attributelogid) {
1071
                            $applieditemattr = new stdClass();
1072
                            $applieditemattr->adminpresetapplyid = $applieditem->adminpresetapplyid;
1073
                            $applieditemattr->configlogid = $attributelogid;
1074
                            $applieditemattr->itemname = $presetsetting->get_settingdata()->name;
1075
                            $DB->insert_record('adminpresets_app_it_a', $applieditemattr);
1076
                        }
1077
                    }
1078
 
1079
                    // Added to changed values.
1080
                    $data['oldvisiblevalue'] = $sitesetting->get_visiblevalue();
1081
                    $applied[] = $data;
1082
                } else {
1083
                    // Unnecessary changes (actual setting value).
1084
                    $skipped[] = $data;
1085
                }
1086
            }
1087
        }
1088
        return [$applied, $skipped, $adminpresetapplyid];
1089
    }
1090
 
1091
    /**
1092
     * Apply plugins from a preset.
1093
     *
1094
     * @param int $presetid The preset identifier to apply.
1095
     * @param bool $simulate Whether this is a simulation or not.
1096
     * @param int|null $adminpresetapplyid The identifier of the adminpresetapply or null if it hasn't been created previously.
1097
     * @return array List with an array with the applied settings, another with the skipped ones and the adminpresetapplyid.
1098
     */
1099
    protected function apply_plugins(int $presetid, bool $simulate = false, ?int $adminpresetapplyid = null): array {
1100
        global $DB, $USER;
1101
 
1102
        $applied = [];
1103
        $skipped = [];
1104
 
1105
        $strenabled = get_string('enabled', 'core_adminpresets');
1106
        $strdisabled = get_string('disabled', 'core_adminpresets');
1107
 
1108
        $plugins = $DB->get_records('adminpresets_plug', ['adminpresetid' => $presetid]);
1109
        $pluginmanager = \core_plugin_manager::instance();
1110
        foreach ($plugins as $plugin) {
1111
            $pluginclass = \core_plugin_manager::resolve_plugininfo_class($plugin->plugin);
1112
            $oldvalue = $pluginclass::get_enabled_plugin($plugin->name);
1113
 
1114
            // Get the plugininfo object for this plugin, to get its proper visible name.
1115
            $plugininfo = $pluginmanager->get_plugin_info($plugin->plugin . '_' . $plugin->name);
1116
            if ($plugininfo != null) {
1117
                $visiblename = $plugininfo->displayname;
1118
            } else {
1119
                $visiblename = $plugin->plugin . '_' . $plugin->name;
1120
            }
1121
 
1122
            if ($plugin->enabled > 0) {
1123
                $visiblevalue = $strenabled;
1124
            } else if ($plugin->enabled == 0) {
1125
                $visiblevalue = $strdisabled;
1126
            } else {
1127
                $visiblevalue = get_string('disabledwithvalue', 'core_adminpresets', $plugin->enabled);
1128
            }
1129
 
1130
            $data = [
1131
                'plugin' => $plugin->plugin,
1132
                'visiblename' => $visiblename,
1133
                'visiblevalue' => $visiblevalue,
1134
            ];
1135
 
1136
            if ($pluginclass == '\core\plugininfo\orphaned') {
1137
                $skipped[] = $data;
1138
                continue;
1139
            }
1140
 
1141
            // Only change the plugin visibility if it's different to current value.
1142
            if (($plugin->enabled != $oldvalue) && (($plugin->enabled > 0 && !$oldvalue) || ($plugin->enabled < 1 && $oldvalue))) {
1143
                try {
1144
                    if (!$simulate) {
1145
                        $pluginclass::enable_plugin($plugin->name, $plugin->enabled);
1146
 
1147
                        // The preset application it's only saved when values differences are found.
1148
                        if (empty($adminpresetapplyid)) {
1149
                            // Save the preset application and store the preset applied id.
1150
                            $presetapplied = new stdClass();
1151
                            $presetapplied->adminpresetid = $presetid;
1152
                            $presetapplied->userid = $USER->id;
1153
                            $presetapplied->time = time();
1154
                            if (!$adminpresetapplyid = $DB->insert_record('adminpresets_app', $presetapplied)) {
1155
                                throw new moodle_exception('errorinserting', 'core_adminpresets');
1156
                            }
1157
                        }
1158
 
1159
                        // Add plugin to aplied plugins table (for being able to restore in the future if required).
1160
                        $appliedplug = new stdClass();
1161
                        $appliedplug->adminpresetapplyid = $adminpresetapplyid;
1162
                        $appliedplug->plugin = $plugin->plugin;
1163
                        $appliedplug->name = $plugin->name;
1164
                        $appliedplug->value = $plugin->enabled;
1165
                        $appliedplug->oldvalue = $oldvalue;
1166
                        $DB->insert_record('adminpresets_app_plug', $appliedplug);
1167
                    }
1168
 
1169
                    if ($oldvalue > 0) {
1170
                        $oldvisiblevalue = $strenabled;
1171
                    } else if ($oldvalue == 0) {
1172
                        $oldvisiblevalue = $strdisabled;
1173
                    } else {
1174
                        $oldvisiblevalue = get_string('disabledwithvalue', 'core_adminpresets', $oldvalue);
1175
                    }
1176
                    $data['oldvisiblevalue'] = $oldvisiblevalue;
1177
                    $applied[] = $data;
1178
                } catch (\exception $e) {
1179
                    $skipped[] = $data;
1180
                }
1181
            } else {
1182
                $skipped[] = $data;
1183
            }
1184
        }
1185
 
1186
        return [$applied, $skipped, $adminpresetapplyid];
1187
    }
1188
 
1189
}