Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 400 and 403] [Versions 401 and 403]

   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.
 765          $preset = $DB->get_record('adminpresets', ['id' => $presetid]);
 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  }