From 727f0d4dafd4453f5fca57984886fa25dc49bc99 Mon Sep 17 00:00:00 2001 From: Sara Arjona Date: Tue, 21 Dec 2021 18:08:05 +0100 Subject: [PATCH] MDL-73397 adminpresets: Create new core_adminpresets component The core_adminpresets component has been added and some data and methods from the tool_admin_presets have been moved here. --- adminpresets/classes/helper.php | 387 ++++++ .../adminpresets_admin_setting_bloglevel.php | 57 + ...inpresets_admin_setting_configcheckbox.php | 43 + ...n_setting_configcheckbox_with_advanced.php | 45 + ...admin_setting_configcheckbox_with_lock.php | 45 + ...dminpresets_admin_setting_configiplist.php | 39 + ...sets_admin_setting_configmulticheckbox.php | 32 + ...resets_admin_setting_configmultiselect.php | 73 + ..._setting_configmultiselect_with_loader.php | 32 + ...dminpresets_admin_setting_configselect.php | 64 + ...min_setting_configselect_with_advanced.php | 59 + .../adminpresets_admin_setting_configtext.php | 57 + ...admin_setting_configtext_with_advanced.php | 45 + .../adminpresets_admin_setting_configtime.php | 64 + ...resets_admin_setting_devicedetectregex.php | 43 + ...inpresets_admin_setting_gradecat_combo.php | 59 + ...adminpresets_admin_setting_sitesettext.php | 67 + ...esets_admin_setting_special_backupdays.php | 52 + ...admin_setting_special_calendar_weekend.php | 44 + ...ts_admin_setting_users_with_capability.php | 39 + .../local/setting/adminpresets_setting.php | 311 +++++ .../classes/local/setting/delegation.php | 53 + adminpresets/classes/manager.php | 1170 +++++++++++++++++ adminpresets/classes/privacy/provider.php | 125 ++ .../tests/fixtures/import_settings.xml | 24 + .../fixtures/import_settings_plugins.xml | 38 + ...mport_settings_with_unexisting_setting.xml | 25 + .../tests/fixtures/import_starter_name.xml | 38 + .../tests/fixtures/invalid_xml_file.xml | 23 + .../tests/fixtures/unexisting_category.xml | 17 + .../tests/fixtures/unexisting_setting.xml | 17 + adminpresets/tests/generator/lib.php | 245 ++++ adminpresets/tests/generator_test.php | 203 +++ adminpresets/tests/helper_test.php | 359 +++++ ...inpresets_admin_setting_bloglevel_test.php | 92 ++ ...presets_admin_setting_sitesettext_test.php | 94 ++ .../setting/adminpresets_setting_test.php | 198 +++ adminpresets/tests/manager_test.php | 771 +++++++++++ .../tests/privacy/privacy_provider_test.php | 170 +++ lang/en/adminpresets.php | 53 + lib/components.json | 1 + lib/db/install.php | 4 + lib/db/install.xml | 127 +- lib/db/upgrade.php | 194 +++ lib/tests/component_test.php | 2 +- phpunit.xml.dist | 3 + version.php | 2 +- 47 files changed, 5702 insertions(+), 3 deletions(-) create mode 100644 adminpresets/classes/helper.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_bloglevel.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox_with_advanced.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox_with_lock.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configiplist.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configmulticheckbox.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configmultiselect.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configmultiselect_with_loader.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configselect.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configselect_with_advanced.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configtext.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configtext_with_advanced.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_configtime.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_devicedetectregex.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_gradecat_combo.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_sitesettext.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_special_backupdays.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_special_calendar_weekend.php create mode 100644 adminpresets/classes/local/setting/adminpresets_admin_setting_users_with_capability.php create mode 100644 adminpresets/classes/local/setting/adminpresets_setting.php create mode 100644 adminpresets/classes/local/setting/delegation.php create mode 100644 adminpresets/classes/manager.php create mode 100644 adminpresets/classes/privacy/provider.php create mode 100644 adminpresets/tests/fixtures/import_settings.xml create mode 100644 adminpresets/tests/fixtures/import_settings_plugins.xml create mode 100644 adminpresets/tests/fixtures/import_settings_with_unexisting_setting.xml create mode 100644 adminpresets/tests/fixtures/import_starter_name.xml create mode 100644 adminpresets/tests/fixtures/invalid_xml_file.xml create mode 100644 adminpresets/tests/fixtures/unexisting_category.xml create mode 100644 adminpresets/tests/fixtures/unexisting_setting.xml create mode 100644 adminpresets/tests/generator/lib.php create mode 100644 adminpresets/tests/generator_test.php create mode 100644 adminpresets/tests/helper_test.php create mode 100644 adminpresets/tests/local/setting/adminpresets_admin_setting_bloglevel_test.php create mode 100644 adminpresets/tests/local/setting/adminpresets_admin_setting_sitesettext_test.php create mode 100644 adminpresets/tests/local/setting/adminpresets_setting_test.php create mode 100644 adminpresets/tests/manager_test.php create mode 100644 adminpresets/tests/privacy/privacy_provider_test.php create mode 100644 lang/en/adminpresets.php diff --git a/adminpresets/classes/helper.php b/adminpresets/classes/helper.php new file mode 100644 index 00000000000..1d05522dc48 --- /dev/null +++ b/adminpresets/classes/helper.php @@ -0,0 +1,387 @@ +. + +namespace core_adminpresets; + +/** + * Admin presets helper class. + * + * @package core_adminpresets + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class helper { + + /** + * Create an empty preset. + * + * @param array $data Preset data. Supported values: + * - name. To define the preset name. + * - comments. To change the comments field. + * - author. To update the author field. + * - iscore. Whether the preset is a core preset or not. + * @return int The identifier of the preset created. + */ + public static function create_preset(array $data): int { + global $CFG, $USER, $DB; + + $name = array_key_exists('name', $data) ? $data['name'] : ''; + $comments = array_key_exists('comments', $data) ? $data['comments'] : ''; + $author = array_key_exists('author', $data) ? $data['author'] : fullname($USER); + $iscore = array_key_exists('iscore', $data) ? $data['iscore'] : 0; + + $preset = [ + 'userid' => $USER->id, + 'name' => $name, + 'comments' => $comments, + 'site' => $CFG->wwwroot, + 'author' => $author, + 'moodleversion' => $CFG->version, + 'moodlerelease' => $CFG->release, + 'iscore' => $iscore, + 'timecreated' => time(), + 'timeimported' => 0, + ]; + + $presetid = $DB->insert_record('adminpresets', $preset); + return $presetid; + } + + /** + * Helper method to add a setting item to a preset. + * + * @param int $presetid Preset identifier where the item will belong. + * @param string $name Item name. + * @param string $value Item value. + * @param string|null $plugin Item plugin. + * @param string|null $advname If the item is an advanced setting, the name of the advanced setting should be specified here. + * @param string|null $advvalue If the item is an advanced setting, the value of the advanced setting should be specified here. + * @return int The item identificator. + */ + public static function add_item(int $presetid, string $name, string $value, ?string $plugin = 'none', + ?string $advname = null, ?string $advvalue = null): int { + global $DB; + + $presetitem = [ + 'adminpresetid' => $presetid, + 'plugin' => $plugin, + 'name' => $name, + 'value' => $value, + ]; + $itemid = $DB->insert_record('adminpresets_it', $presetitem); + + if (!empty($advname)) { + $presetadv = [ + 'itemid' => $itemid, + 'name' => $advname, + 'value' => $advvalue, + ]; + $DB->insert_record('adminpresets_it_a', $presetadv); + } + + return $itemid; + } + + /** + * Helper method to add a plugin to a preset. + * + * @param int $presetid Preset identifier where the item will belong. + * @param string $plugin Plugin type. + * @param string $name Plugin name. + * @param int $enabled Whether the plugin will be enabled or not. + * @return int The plugin identificator. + */ + public static function add_plugin(int $presetid, string $plugin, string $name, int $enabled): int { + global $DB; + + $pluginentry = [ + 'adminpresetid' => $presetid, + 'plugin' => $plugin, + 'name' => $name, + 'enabled' => $enabled, + ]; + $pluginid = $DB->insert_record('adminpresets_plug', $pluginentry); + + return $pluginid; + } + + /** + * Apply the given preset. If it's a filename, the preset will be imported and then applied. + * + * @param string $presetnameorfile The preset name to be applied or a valid preset file to be imported and applied. + * @return int|null The preset identifier that has been applied or null if the given value was not valid. + */ + public static function change_default_preset(string $presetnameorfile): ?int { + global $DB; + + $presetid = null; + + // Check if the given variable points to a valid preset file to be imported and applied. + if (is_readable($presetnameorfile)) { + $xmlcontent = file_get_contents($presetnameorfile); + $manager = new manager(); + list($xmlnotused, $preset) = $manager->import_preset($xmlcontent); + if (!is_null($preset)) { + list($applied) = $manager->apply_preset($preset->id); + if (!empty($applied)) { + $presetid = $preset->id; + } + } + } else { + // Check if the given preset exists; if that's the case, it will be applied. + $stringmanager = get_string_manager(); + if ($stringmanager->string_exists($presetnameorfile . 'preset', 'core_adminpresets')) { + $params = ['name' => get_string($presetnameorfile . 'preset', 'core_adminpresets')]; + } else { + $params = ['name' => $presetnameorfile]; + } + if ($preset = $DB->get_record('adminpresets', $params)) { + $manager = new manager(); + list($applied) = $manager->apply_preset($preset->id); + if (!empty($applied)) { + $presetid = $preset->id; + } + } + } + + return $presetid; + } + + /** + * Helper method to create default site admin presets and initialize them. + */ + public static function create_default_presets(): void { + // Create the "Starter" site admin preset. + $data = [ + 'name' => get_string('starterpreset', 'core_adminpresets'), + 'comments' => get_string('starterpresetdescription', 'core_adminpresets'), + 'iscore' => 1, + ]; + $presetid = static::create_preset($data); + + // Add settings to the "Starter" site admin preset. + static::add_item($presetid, 'usecomments', '0'); + static::add_item($presetid, 'usetags', '0'); + static::add_item($presetid, 'enablenotes', '0'); + static::add_item($presetid, 'enableblogs', '0'); + static::add_item($presetid, 'enablebadges', '0'); + static::add_item($presetid, 'enableanalytics', '0'); + static::add_item($presetid, 'enabled', '0', 'core_competency'); + static::add_item($presetid, 'pushcourseratingstouserplans', '0', 'core_competency'); + + static::add_item($presetid, 'showdataretentionsummary', '0', 'tool_dataprivacy'); + static::add_item($presetid, 'forum_maxattachments', '3'); + static::add_item($presetid, 'customusermenuitems', 'grades,grades|/grade/report/mygrades.php|t/grades +calendar,core_calendar|/calendar/view.php?view=month|i/calendar +preferences,moodle|/user/preferences.php|t/preferences'); + + // Modules: Hide chat, database, external tool (lti), IMS content package (imscp), lesson, SCORM, survey, wiki, workshop. + static::add_plugin($presetid, 'mod', 'chat', false); + static::add_plugin($presetid, 'mod', 'data', false); + static::add_plugin($presetid, 'mod', 'lti', false); + static::add_plugin($presetid, 'mod', 'imscp', false); + static::add_plugin($presetid, 'mod', 'lesson', false); + static::add_plugin($presetid, 'mod', 'scorm', false); + static::add_plugin($presetid, 'mod', 'survey', false); + static::add_plugin($presetid, 'mod', 'wiki', false); + static::add_plugin($presetid, 'mod', 'workshop', false); + + // Availability restrictions: Hide Grouping, User profile. + static::add_plugin($presetid, 'availability', 'grouping', false); + static::add_plugin($presetid, 'availability', 'profile', false); + + // Blocks: Disable Activities, Blog menu, Blog tags, Comments, Course completion status, Course/site summary, Courses, + // Flickr, Global search, Latest badges, Learning plans, Logged in user, Login, Main menu, Mentees, Network servers, + // Private files, Recent blog entries, RSS feeds, Search forums, Section links,Self completion, Social activities, + // Tags, YouTube. + static::add_plugin($presetid, 'block', 'activity_modules', false); + static::add_plugin($presetid, 'block', 'blog_menu', false); + static::add_plugin($presetid, 'block', 'blog_tags', false); + static::add_plugin($presetid, 'block', 'comments', false); + static::add_plugin($presetid, 'block', 'completionstatus', false); + static::add_plugin($presetid, 'block', 'course_summary', false); + static::add_plugin($presetid, 'block', 'course_list', false); + static::add_plugin($presetid, 'block', 'tag_flickr', false); + static::add_plugin($presetid, 'block', 'globalsearch', false); + static::add_plugin($presetid, 'block', 'badges', false); + static::add_plugin($presetid, 'block', 'lp', false); + static::add_plugin($presetid, 'block', 'myprofile', false); + static::add_plugin($presetid, 'block', 'login', false); + static::add_plugin($presetid, 'block', 'site_main_menu', false); + static::add_plugin($presetid, 'block', 'mentees', false); + static::add_plugin($presetid, 'block', 'mnet_hosts', false); + static::add_plugin($presetid, 'block', 'private_files', false); + static::add_plugin($presetid, 'block', 'blog_recent', false); + static::add_plugin($presetid, 'block', 'rss_client', false); + static::add_plugin($presetid, 'block', 'search_forums', false); + static::add_plugin($presetid, 'block', 'section_links', false); + static::add_plugin($presetid, 'block', 'selfcompletion', false); + static::add_plugin($presetid, 'block', 'social_activities', false); + static::add_plugin($presetid, 'block', 'tags', false); + static::add_plugin($presetid, 'block', 'tag_youtube', false); + static::add_plugin($presetid, 'block', 'feedback', false); + + // Course formats: Disable Social format. + static::add_plugin($presetid, 'format', 'social', false); + + // Data formats: Disable Javascript Object Notation (.json). + static::add_plugin($presetid, 'dataformat', 'json', false); + + // Enrolments: Disable Cohort sync. + static::add_plugin($presetid, 'enrol', 'cohort', false); + + // Filter: Disable MathJax, Activity names auto-linking. + static::add_plugin($presetid, 'filter', 'mathjaxloader', TEXTFILTER_DISABLED); + static::add_plugin($presetid, 'filter', 'activitynames', TEXTFILTER_DISABLED); + + // Question behaviours: Disable Adaptive mode (no penalties), Deferred feedback with CBM, Immediate feedback with CBM. + static::add_plugin($presetid, 'qbehaviour', 'adaptivenopenalty', false); + static::add_plugin($presetid, 'qbehaviour', 'deferredcbm', false); + static::add_plugin($presetid, 'qbehaviour', 'immediatecbm', false); + + // Question types: Disable Calculated, Calculated multichoice, Calculated simple, Description, Drag and drop markers, + // Drag and drop onto image, Embedded answers (Cloze), Essay, Numerical, Random short-answer matching. + static::add_plugin($presetid, 'qtype', 'calculated', false); + static::add_plugin($presetid, 'qtype', 'calculatedmulti', false); + static::add_plugin($presetid, 'qtype', 'calculatedsimple', false); + static::add_plugin($presetid, 'qtype', 'description', false); + static::add_plugin($presetid, 'qtype', 'ddmarker', false); + static::add_plugin($presetid, 'qtype', 'ddimageortext', false); + static::add_plugin($presetid, 'qtype', 'multianswer', false); + static::add_plugin($presetid, 'qtype', 'essay', false); + static::add_plugin($presetid, 'qtype', 'numerical', false); + static::add_plugin($presetid, 'qtype', 'randomsamatch', false); + + // Repositories: Disable Server files, URL downloader, Wikimedia. + static::add_plugin($presetid, 'repository', 'local', false); + static::add_plugin($presetid, 'repository', 'url', false); + static::add_plugin($presetid, 'repository', 'wikimedia', false); + + // Text editors: Disable TinyMCE HTML editor. + static::add_plugin($presetid, 'editor', 'tinymce', false); + + // Create the "Full" site admin preset. + $data = [ + 'name' => get_string('fullpreset', 'core_adminpresets'), + 'comments' => get_string('fullpresetdescription', 'core_adminpresets'), + 'iscore' => 1, + ]; + $presetid = static::create_preset($data); + + // Add settings to the "Full" site admin preset. + static::add_item($presetid, 'usecomments', '1'); + static::add_item($presetid, 'usetags', '1'); + static::add_item($presetid, 'enablenotes', '1'); + static::add_item($presetid, 'enableblogs', '1'); + static::add_item($presetid, 'enablebadges', '1'); + static::add_item($presetid, 'enableanalytics', '1'); + static::add_item($presetid, 'enabled', '1', 'core_competency'); + static::add_item($presetid, 'pushcourseratingstouserplans', '1', 'core_competency'); + + static::add_item($presetid, 'showdataretentionsummary', '1', 'tool_dataprivacy'); + static::add_item($presetid, 'forum_maxattachments', '9'); + // In that case, the indentation coding style can't follow the rules to guarantee the setting value is created properly. + static::add_item($presetid, 'customusermenuitems', 'grades,grades|/grade/report/mygrades.php|t/grades +calendar,core_calendar|/calendar/view.php?view=month|i/calendar +messages,message|/message/index.php|t/message +preferences,moodle|/user/preferences.php|t/preferences' + ); + + // Modules: Enable chat, database, external tool (lti), IMS content package (imscp), lesson, SCORM, survey, wiki, workshop. + static::add_plugin($presetid, 'mod', 'chat', true); + static::add_plugin($presetid, 'mod', 'data', true); + static::add_plugin($presetid, 'mod', 'lti', true); + static::add_plugin($presetid, 'mod', 'imscp', true); + static::add_plugin($presetid, 'mod', 'lesson', true); + static::add_plugin($presetid, 'mod', 'scorm', true); + static::add_plugin($presetid, 'mod', 'survey', true); + static::add_plugin($presetid, 'mod', 'wiki', true); + static::add_plugin($presetid, 'mod', 'workshop', true); + + // Availability restrictions: Enable Grouping, User profile. + static::add_plugin($presetid, 'availability', 'grouping', true); + static::add_plugin($presetid, 'availability', 'profile', true); + + // Blocks: Enable Activities, Blog menu, Blog tags, Comments, Course completion status, Course/site summary, Courses, + // Flickr, Global search, Latest badges, Learning plans, Logged in user, Login, Main menu, Mentees, Network servers, + // Private files, Recent blog entries, RSS feeds, Search forums, Section links,Self completion, Social activities, + // Tags, YouTube. + static::add_plugin($presetid, 'block', 'activity_modules', true); + static::add_plugin($presetid, 'block', 'blog_menu', true); + static::add_plugin($presetid, 'block', 'blog_tags', true); + static::add_plugin($presetid, 'block', 'comments', true); + static::add_plugin($presetid, 'block', 'completionstatus', true); + static::add_plugin($presetid, 'block', 'course_summary', true); + static::add_plugin($presetid, 'block', 'course_list', true); + static::add_plugin($presetid, 'block', 'tag_flickr', true); + static::add_plugin($presetid, 'block', 'globalsearch', true); + static::add_plugin($presetid, 'block', 'badges', true); + static::add_plugin($presetid, 'block', 'lp', true); + static::add_plugin($presetid, 'block', 'myprofile', true); + static::add_plugin($presetid, 'block', 'login', true); + static::add_plugin($presetid, 'block', 'site_main_menu', true); + static::add_plugin($presetid, 'block', 'mentees', true); + static::add_plugin($presetid, 'block', 'mnet_hosts', true); + static::add_plugin($presetid, 'block', 'private_files', true); + static::add_plugin($presetid, 'block', 'blog_recent', true); + static::add_plugin($presetid, 'block', 'rss_client', true); + static::add_plugin($presetid, 'block', 'search_forums', true); + static::add_plugin($presetid, 'block', 'section_links', true); + static::add_plugin($presetid, 'block', 'selfcompletion', true); + static::add_plugin($presetid, 'block', 'social_activities', true); + static::add_plugin($presetid, 'block', 'tags', true); + static::add_plugin($presetid, 'block', 'feedback', true); + + // Course formats: Enable Social format. + static::add_plugin($presetid, 'format', 'social', true); + + // Data formats: Enable Javascript Object Notation (.json). + static::add_plugin($presetid, 'dataformat', 'json', true); + + // Enrolments: Enable Cohort sync. + static::add_plugin($presetid, 'enrol', 'cohort', true); + + // Filter: Enable MathJax, Activity names auto-linking. + static::add_plugin($presetid, 'filter', 'mathjaxloader', TEXTFILTER_ON); + static::add_plugin($presetid, 'filter', 'activitynames', TEXTFILTER_ON); + + // Question behaviours: Enable Adaptive mode (no penalties), Deferred feedback with CBM, Immediate feedback with CBM. + static::add_plugin($presetid, 'qbehaviour', 'adaptivenopenalty', true); + static::add_plugin($presetid, 'qbehaviour', 'deferredcbm', true); + static::add_plugin($presetid, 'qbehaviour', 'immediatecbm', true); + + // Question types: Enable Calculated, Calculated multichoice, Calculated simple, Description, Drag and drop markers, + // Drag and drop onto image, Embedded answers (Cloze), Essay, Numerical, Random short-answer matching. + static::add_plugin($presetid, 'qtype', 'calculated', true); + static::add_plugin($presetid, 'qtype', 'calculatedmulti', true); + static::add_plugin($presetid, 'qtype', 'calculatedsimple', true); + static::add_plugin($presetid, 'qtype', 'description', true); + static::add_plugin($presetid, 'qtype', 'ddmarker', true); + static::add_plugin($presetid, 'qtype', 'ddimageortext', true); + static::add_plugin($presetid, 'qtype', 'multianswer', true); + static::add_plugin($presetid, 'qtype', 'essay', true); + static::add_plugin($presetid, 'qtype', 'numerical', true); + static::add_plugin($presetid, 'qtype', 'randomsamatch', true); + + // Repositories: Enable Server files, URL downloader, Wikimedia. + static::add_plugin($presetid, 'repository', 'local', true); + static::add_plugin($presetid, 'repository', 'url', true); + static::add_plugin($presetid, 'repository', 'wikimedia', true); + + // Text editors: Enable TinyMCE HTML editor. + static::add_plugin($presetid, 'editor', 'tinymce', true); + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_bloglevel.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_bloglevel.php new file mode 100644 index 00000000000..3e69dd02d57 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_bloglevel.php @@ -0,0 +1,57 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Select setting for blog's bloglevel setting. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_bloglevel extends adminpresets_admin_setting_configselect { + + /** + * Extended to change the block visibility. + * + * @param bool $name Setting name to store. + * @param mixed $value Setting value to store. + * @return int|false config_log inserted id or false whenever the value has not been saved. + */ + public function save_value($name = false, $value = null) { + global $DB; + + if (!$id = parent::save_value($name, $value)) { + return false; + } + + // Object values if no arguments. + if ($value === null) { + $value = $this->value; + } + + // Pasted from admin_setting_bloglevel (can't use write_config). + if ($value == 0) { + $DB->set_field('block', 'visible', 0, ['name' => 'blog_menu']); + } else { + $DB->set_field('block', 'visible', 1, ['name' => 'blog_menu']); + } + + return $id; + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox.php new file mode 100644 index 00000000000..f58145f8b72 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox.php @@ -0,0 +1,43 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Checkbox setting. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configcheckbox extends adminpresets_setting { + + protected function set_value($value) { + $this->value = clean_param($value, PARAM_BOOL); + return true; + } + + protected function set_visiblevalue() { + if ($this->value) { + $str = get_string('yes'); + } else { + $str = get_string('no'); + } + + $this->visiblevalue = $str; + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox_with_advanced.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox_with_advanced.php new file mode 100644 index 00000000000..dcf0ef64461 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox_with_advanced.php @@ -0,0 +1,45 @@ +. + +namespace core_adminpresets\local\setting; + +use admin_setting; + +/** + * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configcheckbox_with_advanced extends adminpresets_admin_setting_configcheckbox { + + public function __construct(admin_setting $settingdata, $dbsettingvalue) { + // To look for other values. + $this->attributes = ['adv' => $settingdata->name . '_adv']; + parent::__construct($settingdata, $dbsettingvalue); + } + + /** + * Uses delegation + */ + protected function set_visiblevalue() { + parent::set_visiblevalue(); + $value = $this->attributesvalues[$this->attributes['adv']]; + $this->visiblevalue .= $this->delegation->extra_set_visiblevalue($value, 'advanced'); + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox_with_lock.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox_with_lock.php new file mode 100644 index 00000000000..f387ca4f60a --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configcheckbox_with_lock.php @@ -0,0 +1,45 @@ +. + +namespace core_adminpresets\local\setting; + +use admin_setting; + +/** + * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configcheckbox_with_lock extends adminpresets_admin_setting_configcheckbox { + + public function __construct(admin_setting $settingdata, $dbsettingvalue) { + // To look for other values. + $this->attributes = ['locked' => $settingdata->name . '_locked']; + parent::__construct($settingdata, $dbsettingvalue); + } + + /** + * Uses delegation + */ + protected function set_visiblevalue() { + parent::set_visiblevalue(); + $value = $this->attributesvalues[$this->attributes['locked']]; + $this->visiblevalue .= $this->delegation->extra_set_visiblevalue($value, 'locked'); + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configiplist.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configiplist.php new file mode 100644 index 00000000000..8bdd571d1e3 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configiplist.php @@ -0,0 +1,39 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Used to validate a textarea used for ip addresses. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configiplist extends adminpresets_admin_setting_configtext { + + protected function set_value($value) { + // Check ip format. + if ($this->settingdata->validate($value) !== true) { + $this->value = false; + return false; + } + + $this->value = $value; + return true; + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configmulticheckbox.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configmulticheckbox.php new file mode 100644 index 00000000000..bfc35c9143e --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configmulticheckbox.php @@ -0,0 +1,32 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Class to be extended by multicheckbox settings. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configmulticheckbox extends adminpresets_admin_setting_configmultiselect { + + public function set_behaviors() { + $this->behaviors['loadchoices'] = &$this->settingdata; + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configmultiselect.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configmultiselect.php new file mode 100644 index 00000000000..2052ac5c054 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configmultiselect.php @@ -0,0 +1,73 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Extends the base class and lists the selected values separated by comma. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configmultiselect extends adminpresets_setting { + + /** + * Ensure that the $value values are setting choices. + * + * @param mixed $value Setting value + * @return mixed Returns false if wrong param value + */ + protected function set_value($value) { + if ($value) { + $options = explode(',', $value); + foreach ($options as $option) { + + foreach ($this->settingdata->choices as $key => $choice) { + + if ($key == $option) { + $this->value = $option; + return true; + } + } + } + + $value = implode(',', $options); + } + + $this->value = $value; + } + + protected function set_visiblevalue() { + $values = explode(',', $this->value); + $visiblevalues = []; + + foreach ($values as $value) { + + if (!empty($this->settingdata->choices[$value])) { + $visiblevalues[] = $this->settingdata->choices[$value]; + } + } + + if (empty($visiblevalues)) { + $this->visiblevalue = ''; + return false; + } + + $this->visiblevalue = implode(', ', $visiblevalues); + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configmultiselect_with_loader.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configmultiselect_with_loader.php new file mode 100644 index 00000000000..c0e42c6d866 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configmultiselect_with_loader.php @@ -0,0 +1,32 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Generalizes a configmultipleselect with load_choices(). + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configmultiselect_with_loader extends adminpresets_admin_setting_configmultiselect { + + public function set_behaviors() { + $this->behaviors['loadchoices'] = &$this->settingdata; + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configselect.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configselect.php new file mode 100644 index 00000000000..17a4ed7c698 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configselect.php @@ -0,0 +1,64 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Select one value from list. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configselect extends adminpresets_setting { + + /** + * Sets the setting value cleaning it. + * + * @param mixed $value must be one of the setting choices. + * @return bool true if the value one of the setting choices + */ + protected function set_value($value) { + // When we intantiate the class we need the choices. + if (empty($this->settindata->choices) && method_exists($this->settingdata, 'load_choices')) { + $this->settingdata->load_choices(); + } + + if (!is_null($this->settingdata->choices) and is_array($this->settingdata->choices)) { + foreach ($this->settingdata->choices as $key => $choice) { + + if ($key == $value) { + $this->value = $value; + return true; + } + } + } + + $this->value = false; + return false; + } + + protected function set_visiblevalue() { + // Just to avoid heritage problems. + if (empty($this->settingdata->choices[$this->value])) { + $this->visiblevalue = ''; + } else { + $this->visiblevalue = $this->settingdata->choices[$this->value]; + } + + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configselect_with_advanced.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configselect_with_advanced.php new file mode 100644 index 00000000000..55d82ab90c1 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configselect_with_advanced.php @@ -0,0 +1,59 @@ +. + +namespace core_adminpresets\local\setting; + +use admin_setting; + +/** + * Adds support for the "advanced" attribute. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configselect_with_advanced extends adminpresets_admin_setting_configselect { + + /** @var string Name of the advanced setting. **/ + protected $advancedkey; + + public function __construct(admin_setting $settingdata, $dbsettingvalue) { + // Getting the advanced defaultsetting attribute name. + if (is_array($settingdata->defaultsetting)) { + foreach ($settingdata->defaultsetting as $key => $defaultvalue) { + if ($key != 'value') { + $this->advancedkey = $key; + } + } + } + + // To look for other values. + $this->attributes = [$this->advancedkey => $settingdata->name . '_adv']; + parent::__construct($settingdata, $dbsettingvalue); + } + + /** + * Funcionality used by other _with_advanced settings + */ + protected function set_visiblevalue() { + parent::set_visiblevalue(); + if (!is_null($this->attributesvalues)) { + $attribute = $this->attributes[$this->advancedkey]; + $this->visiblevalue .= $this->delegation->extra_set_visiblevalue($this->attributesvalues[$attribute], 'advanced'); + } + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configtext.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configtext.php new file mode 100644 index 00000000000..fbac4c15a8c --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configtext.php @@ -0,0 +1,57 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Basic text setting, cleans the param using the admin_setting paramtext attribute. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configtext extends adminpresets_setting { + + /** + * Validates the value using paramtype attribute + * + * @param mixed $value + * @return boolean Cleaned or not, but always true. + */ + protected function set_value($value) { + $this->value = $value; + + if (empty($this->settingdata->paramtype)) { + + // For configfile, configpasswordunmask.... + $this->settingdata->paramtype = 'RAW'; + } + + $paramtype = 'PARAM_' . strtoupper($this->settingdata->paramtype); + + // Regexp. + if (!defined($paramtype)) { + $this->value = preg_replace($this->settingdata->paramtype, '', $this->value); + + // Standard moodle param type. + } else { + $this->value = clean_param($this->value, constant($paramtype)); + } + + return true; + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configtext_with_advanced.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configtext_with_advanced.php new file mode 100644 index 00000000000..c7538b64d49 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configtext_with_advanced.php @@ -0,0 +1,45 @@ +. + +namespace core_adminpresets\local\setting; + +use admin_setting; + +/** + * Adds the advanced attribute. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configtext_with_advanced extends adminpresets_admin_setting_configtext { + + public function __construct(admin_setting $settingdata, $dbsettingvalue) { + // To look for other values. + $this->attributes = ['fix' => $settingdata->name . '_adv']; + parent::__construct($settingdata, $dbsettingvalue); + } + + /** + * Delegates + */ + protected function set_visiblevalue() { + parent::set_visiblevalue(); + $value = $this->attributesvalues[$this->attributes['fix']]; + $this->visiblevalue .= $this->delegation->extra_set_visiblevalue($value, 'advanced'); + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_configtime.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_configtime.php new file mode 100644 index 00000000000..da45019b714 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_configtime.php @@ -0,0 +1,64 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Time selector. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_configtime extends adminpresets_setting { + + /** + * To check that the value is one of the options + * + * @param string $name + * @param mixed $value + */ + public function set_attribute_value($name, $value) { + for ($i = 0; $i < 60; $i = $i + 5) { + $minutes[$i] = $i; + } + + if (!empty($minutes[$value])) { + $this->attributesvalues[$name] = $value; + } else { + $this->attributesvalues[$name] = $this->settingdata->defaultsetting['m']; + } + } + + protected function set_value($value) { + $this->attributes = ['m' => $this->settingdata->name2]; + + for ($i = 0; $i < 24; $i++) { + $hours[$i] = $i; + } + + if (empty($hours[$value])) { + $this->value = false; + } + + $this->value = $value; + } + + protected function set_visiblevalue() { + $this->visiblevalue = $this->value . ':' . $this->attributesvalues[$this->settingdata->name2]; + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_devicedetectregex.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_devicedetectregex.php new file mode 100644 index 00000000000..0ff584f6873 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_devicedetectregex.php @@ -0,0 +1,43 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Reimplementation to allow human friendly view of the selected regexps. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_devicedetectregex extends adminpresets_admin_setting_configtext { + + public function set_visiblevalue() { + $values = json_decode($this->get_value()); + + if (!$values) { + parent::set_visiblevalue(); + return; + } + + $this->visiblevalue = ''; + foreach ($values as $key => $value) { + $this->visiblevalue .= $key . ' = ' . $value . ', '; + } + $this->visiblevalue = rtrim($this->visiblevalue, ', '); + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_gradecat_combo.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_gradecat_combo.php new file mode 100644 index 00000000000..e31a590ec51 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_gradecat_combo.php @@ -0,0 +1,59 @@ +. + +namespace core_adminpresets\local\setting; + +use admin_setting; + +/** + * A select with force and advanced options + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_gradecat_combo extends adminpresets_admin_setting_configselect { + + /** + * One db value for two setting attributes + * + * @param admin_setting $settingdata + * @param mixed $dbsettingvalue + */ + public function __construct(admin_setting $settingdata, $dbsettingvalue) { + // The set_attribute_value() method will mod the VARNAME_flag value. + $this->attributes = ['forced' => $settingdata->name . '_flag', 'adv' => $settingdata->name . '_flag']; + parent::__construct($settingdata, $dbsettingvalue); + } + + /** + * Special treatment! the value be extracted from the $value argument + */ + protected function set_visiblevalue() { + parent::set_visiblevalue(); + + $flagvalue = $this->attributesvalues[$this->settingdata->name . '_flag']; + + if (isset($flagvalue)) { + $forcedvalue = (($flagvalue % 2) == 1); + $advancedvalue = ($flagvalue >= 2); + + $this->visiblevalue .= $this->delegation->extra_set_visiblevalue($forcedvalue, 'forced'); + $this->visiblevalue .= $this->delegation->extra_set_visiblevalue($advancedvalue, 'advanced'); + } + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_sitesettext.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_sitesettext.php new file mode 100644 index 00000000000..01e96d71e28 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_sitesettext.php @@ -0,0 +1,67 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Reimplemented to store values in course table, not in config or config_plugins. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_sitesettext extends adminpresets_admin_setting_configtext { + + /** + * Overwritten to store the value in the course table + * + * @param bool $name + * @param mixed $value + * @return int|false config_log inserted id or false whenever the new value is the same as old value. + */ + public function save_value($name = false, $value = null) { + global $DB; + + // Object values if no arguments. + if ($value === null) { + $value = $this->value; + } + if (!$name) { + $name = $this->settingdata->name; + } + + $sitecourse = $DB->get_record('course', ['id' => 1]); + $actualvalue = $sitecourse->{$name}; + + // If it's the same value skip. + if ($actualvalue == $value) { + return false; + } + + // Plugin name or ''. + $plugin = $this->settingdata->plugin; + if ($plugin == 'none' || $plugin == '') { + $plugin = null; + } + + // Updating mdl_course. + $sitecourse->{$name} = $value; + $DB->update_record('course', $sitecourse); + + return $this->to_log($plugin, $name, $this->value, $actualvalue); + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_special_backupdays.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_special_backupdays.php new file mode 100644 index 00000000000..a40df998bc5 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_special_backupdays.php @@ -0,0 +1,52 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Special control for selecting days to backup. + * + * It doesn't specify loadchoices behavior because is set_visiblevalue who needs it. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_special_backupdays extends adminpresets_setting { + + protected function set_value($value) { + $this->value = clean_param($value, PARAM_SEQUENCE); + } + + protected function set_visiblevalue() { + $this->settingdata->load_choices(); + + $days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; + + $selecteddays = []; + + $week = str_split($this->value); + foreach ($week as $key => $day) { + if ($day) { + $index = $days[$key]; + $selecteddays[] = $this->settingdata->choices[$index]; + } + } + + $this->visiblevalue = implode(', ', $selecteddays); + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_special_calendar_weekend.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_special_calendar_weekend.php new file mode 100644 index 00000000000..f848d0584f6 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_special_calendar_weekend.php @@ -0,0 +1,44 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Special admin control for calendar weekend. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_special_calendar_weekend extends adminpresets_setting { + + protected function set_visiblevalue() { + if (!$this->value) { + parent::set_visiblevalue(); + return; + } + + $days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; + for ($i = 0; $i < 7; $i++) { + if ($this->value & (1 << $i)) { + $settings[] = get_string($days[$i], 'calendar'); + } + } + + $this->visiblevalue = implode(', ', $settings); + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_admin_setting_users_with_capability.php b/adminpresets/classes/local/setting/adminpresets_admin_setting_users_with_capability.php new file mode 100644 index 00000000000..e46db3afbad --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_admin_setting_users_with_capability.php @@ -0,0 +1,39 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Extends configselect to reuse set_valuevisible. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_admin_setting_users_with_capability extends adminpresets_admin_setting_configmultiselect { + + protected function set_behaviors() { + $this->behaviors['loadchoices'] = &$this->settingdata; + } + + protected function set_value($value) { + // Dirty hack (the value stored in the DB is ''). + $this->settingdata->choices[''] = $this->settingdata->choices['$@NONE@$']; + + return parent::set_value($value); + } +} diff --git a/adminpresets/classes/local/setting/adminpresets_setting.php b/adminpresets/classes/local/setting/adminpresets_setting.php new file mode 100644 index 00000000000..d8d69fe5b35 --- /dev/null +++ b/adminpresets/classes/local/setting/adminpresets_setting.php @@ -0,0 +1,311 @@ +. + +namespace core_adminpresets\local\setting; + +use admin_setting; +use moodle_exception; +use stdClass; + +/** + * Admin tool presets plugin to load some settings. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class adminpresets_setting { + + /** + * @var admin_setting + */ + protected $settingdata; + + /** + * @var delegation + */ + protected $delegation; + + /** + * The setting DB value + * + * @var mixed + */ + protected $value; + + /** + * Stores the visible value of the setting DB value + * + * @var string + */ + protected $visiblevalue; + + /** + * Text to display on the TreeView + * + * @var string + */ + protected $text; + + /** + * For multiple value settings, used to look for the other values + * + * @var string + */ + protected $attributes = false; + + /** + * To store the setting attributes + * + * @var array + */ + protected $attributesvalues; + + /** + * Stores the setting data and the selected value + * + * @param admin_setting $settingdata admin_setting subclass + * @param mixed $dbsettingvalue Actual value + */ + public function __construct(admin_setting $settingdata, $dbsettingvalue) { + $this->settingdata = $settingdata; + $this->delegation = new delegation(); + + if ($this->settingdata->plugin == '') { + $this->settingdata->plugin = 'none'; + } + + // Applies specific children behaviors. + $this->set_behaviors(); + $this->apply_behaviors(); + + // Cleaning value. + $this->set_value($dbsettingvalue); + } + + /** + * Each class can overwrite this method to specify extra processes + */ + protected function set_behaviors() { + } + + /** + * Applies the children class specific behaviors + * + * See admin_presets_delegation() for the available extra behaviors + */ + protected function apply_behaviors() { + if (!empty($this->behaviors)) { + + foreach ($this->behaviors as $behavior => $arguments) { + + // The arguments of the behavior depends on the caller. + $methodname = 'extra_' . $behavior; + $this->delegation->{$methodname}($arguments); + } + } + } + + /** + * Returns the TreeView node identifier + */ + public function get_id() { + return $this->settingdata->name . '@@' . $this->settingdata->plugin; + } + + public function get_value() { + return $this->value; + } + + /** + * Sets the setting value cleaning it + * + * Child classes should overwrite method to clean more acurately + * + * @param mixed $value Setting value + * @return mixed Returns false if wrong param value + */ + protected function set_value($value) { + $this->value = $value; + } + + public function get_visiblevalue() { + return $this->visiblevalue; + } + + /** + * Sets the visible name for the setting selected value + * + * In most cases the child classes will overwrite + */ + protected function set_visiblevalue() { + $this->visiblevalue = $this->value; + } + + public function get_description() { + // PARAM_TEXT clean because the alt attribute does not support html. + $description = clean_param($this->settingdata->description, PARAM_TEXT); + return $this->encode_string($description); + } + + /** + * Encodes a string to send it to js + * + * @param string $string + * @return string + */ + protected function encode_string($string) { + $encoded = rawurlencode($string); + return $encoded; + } + + public function get_text() { + return $this->encode_string($this->text); + } + + /** + * Sets the text to display on the settings tree + * + * Default format: I'm a setting visible name (setting value: "VALUE") + */ + public function set_text() { + $this->set_visiblevalue(); + + $namediv = '
' . $this->settingdata->visiblename . '
'; + $valuediv = '
' . $this->visiblevalue . '
'; + + $this->text = $namediv . $valuediv . '
'; + } + + public function get_attributes() { + return $this->attributes; + } + + public function get_attributes_values() { + return $this->attributesvalues; + } + + public function get_settingdata() { + return $this->settingdata; + } + + public function set_attribute_value($name, $value) { + $this->attributesvalues[$name] = $value; + } + + /** + * Saves the setting attributes values + * + * @return array Array of inserted ids (in config_log) + */ + public function save_attributes_values() { + // Plugin name or null. + $plugin = $this->settingdata->plugin; + if ($plugin == 'none' || $plugin == '') { + $plugin = null; + } + + if (!$this->attributesvalues) { + return false; + } + + // To store inserted ids. + $ids = []; + foreach ($this->attributesvalues as $name => $value) { + + // Getting actual setting. + $actualsetting = get_config($plugin, $name); + + // If it's the actual setting get off. + if ($value == $actualsetting) { + return false; + } + + if ($id = $this->save_value($name, $value)) { + $ids[] = $id; + } + } + + return $ids; + } + + /** + * Stores the setting into database, logs the change and returns the config_log inserted id + * + * @param bool $name Setting name to store. + * @param mixed $value Setting value to store. + * @return int|false config_log inserted id or false whenever the new value is the same as old value. + * @throws dml_exception + * @throws moodle_exception + */ + public function save_value($name = false, $value = null) { + // Object values if no arguments. + if ($value === null) { + $value = $this->value; + } + if (!$name) { + $name = $this->settingdata->name; + } + + // Plugin name or null. + $plugin = $this->settingdata->plugin; + if ($plugin == 'none' || $plugin == '') { + $plugin = null; + } + + // Getting the actual value. + $actualvalue = get_config($plugin, $name); + + // If it's the same it's not necessary. + if ($actualvalue == $value) { + return false; + } + + set_config($name, $value, $plugin); + + return $this->to_log($plugin, $name, $value, $actualvalue); + } + + /** + * Copy of config_write method of the admin_setting class + * + * @param string $plugin + * @param string $name + * @param mixed $value + * @param mixed $actualvalue + * @return integer The stored config_log id + */ + protected function to_log($plugin, $name, $value, $actualvalue) { + global $DB, $USER; + + // Log the change (pasted from admin_setting class). + $log = new stdClass(); + $log->userid = during_initial_install() ? 0 : $USER->id; // 0 as user id during install. + $log->timemodified = time(); + $log->plugin = $plugin; + $log->name = $name; + $log->value = $value; + $log->oldvalue = $actualvalue; + + // Getting the inserted config_log id. + if (!$id = $DB->insert_record('config_log', $log)) { + throw new moodle_exception('errorinserting', 'core_adminpresets'); + } + + return $id; + } +} diff --git a/adminpresets/classes/local/setting/delegation.php b/adminpresets/classes/local/setting/delegation.php new file mode 100644 index 00000000000..25cf42af27f --- /dev/null +++ b/adminpresets/classes/local/setting/delegation.php @@ -0,0 +1,53 @@ +. + +namespace core_adminpresets\local\setting; + +use admin_setting; + +/** + * Cross-class methods + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class delegation { + + /** + * Adds a piece of string to the $type setting + * + * @param boolean $value + * @param string $type Indicates the "extra" setting + * @return string + */ + public function extra_set_visiblevalue(bool $value, string $type): string { + // Adding the advanced value to the text string if present. + if ($value) { + $string = get_string('markedas' . $type, 'core_adminpresets'); + } else { + $string = get_string('markedasnon' . $type, 'core_adminpresets'); + } + + // Adding the advanced state. + return ', ' . $string; + } + + public function extra_loadchoices(admin_setting &$adminsetting) { + $adminsetting->load_choices(); + } +} diff --git a/adminpresets/classes/manager.php b/adminpresets/classes/manager.php new file mode 100644 index 00000000000..2f03e902cc8 --- /dev/null +++ b/adminpresets/classes/manager.php @@ -0,0 +1,1170 @@ +. + +namespace core_adminpresets; + +use memory_xml_output; +use moodle_exception; +use stdClass; +use xml_writer; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir . '/adminlib.php'); + +/** + * Admin tool presets manager class. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class manager { + + /** @var \admin_root The admin root tree with the settings. **/ + private $adminroot; + + /** @var array Setting classes mapping, to associated the local/setting class that should be used when there is + * no specific class. */ + protected static $settingclassesmap = [ + 'adminpresets_admin_setting_agedigitalconsentmap' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configcolourpicker' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configdirectory' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configduration_with_advanced' => 'adminpresets_admin_setting_configtext_with_advanced', + 'adminpresets_admin_setting_configduration' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configempty' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configexecutable' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configfile' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_confightmleditor' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configmixedhostiplist' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configmultiselect_modules' => 'adminpresets_admin_setting_configmultiselect_with_loader', + 'adminpresets_admin_setting_configpasswordunmask' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configportlist' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configselect_with_lock' => 'adminpresets_admin_setting_configselect', + 'adminpresets_admin_setting_configtext_trim_lower' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configtext_with_maxlength' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configtextarea' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_configthemepreset' => 'adminpresets_admin_setting_configselect', + 'adminpresets_admin_setting_countrycodes' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_courselist_frontpage' => 'adminpresets_admin_setting_configmultiselect_with_loader', + 'adminpresets_admin_setting_description' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_enablemobileservice' => 'adminpresets_admin_setting_configcheckbox', + 'adminpresets_admin_setting_filetypes' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_forcetimezone' => 'adminpresets_admin_setting_configselect', + 'adminpresets_admin_setting_grade_profilereport' => 'adminpresets_admin_setting_configmultiselect_with_loader', + 'adminpresets_admin_setting_langlist' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_my_grades_report' => 'adminpresets_admin_setting_configselect', + 'adminpresets_admin_setting_pickroles' => 'adminpresets_admin_setting_configmulticheckbox', + 'adminpresets_admin_setting_question_behaviour' => 'adminpresets_admin_setting_configmultiselect_with_loader', + 'adminpresets_admin_setting_regradingcheckbox' => 'adminpresets_admin_setting_configcheckbox', + 'adminpresets_admin_setting_scsscode' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_servertimezone' => 'adminpresets_admin_setting_configselect', + 'adminpresets_admin_setting_sitesetcheckbox' => 'adminpresets_admin_setting_configcheckbox', + 'adminpresets_admin_setting_sitesetselect' => 'adminpresets_admin_setting_configselect', + 'adminpresets_admin_setting_special_adminseesall' => 'adminpresets_admin_setting_configcheckbox', + 'adminpresets_admin_setting_special_backup_auto_destination' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_special_coursecontact' => 'adminpresets_admin_setting_configmulticheckbox', + 'adminpresets_admin_setting_special_coursemanager' => 'adminpresets_admin_setting_configmulticheckbox', + 'adminpresets_admin_setting_special_debug' => 'adminpresets_admin_setting_configmultiselect_with_loader', + 'adminpresets_admin_setting_special_frontpagedesc' => 'adminpresets_admin_setting_sitesettext', + 'adminpresets_admin_setting_special_gradebookroles' => 'adminpresets_admin_setting_configmulticheckbox', + 'adminpresets_admin_setting_special_gradeexport' => 'adminpresets_admin_setting_configmulticheckbox', + 'adminpresets_admin_setting_special_gradelimiting' => 'adminpresets_admin_setting_configcheckbox', + 'adminpresets_admin_setting_special_grademinmaxtouse' => 'adminpresets_admin_setting_configselect', + 'adminpresets_admin_setting_special_gradepointdefault' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_special_gradepointmax' => 'adminpresets_admin_setting_configtext', + 'adminpresets_admin_setting_special_registerauth' => 'adminpresets_admin_setting_configmultiselect_with_loader', + 'adminpresets_admin_setting_special_selectsetup' => 'adminpresets_admin_setting_configselect', + 'adminpresets_admin_settings_country_select' => 'adminpresets_admin_setting_configmultiselect_with_loader', + 'adminpresets_admin_settings_coursecat_select' => 'adminpresets_admin_setting_configmultiselect_with_loader', + 'adminpresets_admin_settings_h5plib_handler_select' => 'adminpresets_admin_setting_configselect', + 'adminpresets_admin_settings_num_course_sections' => 'adminpresets_admin_setting_configmultiselect_with_loader', + 'adminpresets_admin_settings_sitepolicy_handler_select' => 'adminpresets_admin_setting_configselect', + 'adminpresets_antivirus_clamav_pathtounixsocket_setting' => 'adminpresets_admin_setting_configtext', + 'adminpresets_antivirus_clamav_runningmethod_setting' => 'adminpresets_admin_setting_configselect', + 'adminpresets_antivirus_clamav_tcpsockethost_setting' => 'adminpresets_admin_setting_configtext', + 'adminpresets_auth_db_admin_setting_special_auth_configtext' => 'adminpresets_admin_setting_configtext', + 'adminpresets_auth_ldap_admin_setting_special_lowercase_configtext' => 'adminpresets_admin_setting_configtext', + 'adminpresets_auth_ldap_admin_setting_special_ntlm_configtext' => 'adminpresets_admin_setting_configtext', + 'adminpresets_auth_shibboleth_admin_setting_convert_data' => 'adminpresets_admin_setting_configtext', + 'adminpresets_auth_shibboleth_admin_setting_special_idp_configtextarea' => 'adminpresets_admin_setting_configtext', + 'adminpresets_auth_shibboleth_admin_setting_special_wayf_select' => 'adminpresets_admin_setting_configselect', + 'adminpresets_editor_atto_toolbar_setting' => 'adminpresets_admin_setting_configtext', + 'adminpresets_editor_tinymce_json_setting_textarea' => 'adminpresets_admin_setting_configtext', + 'adminpresets_enrol_database_admin_setting_category' => 'adminpresets_admin_setting_configselect', + 'adminpresets_enrol_flatfile_role_setting' => 'adminpresets_admin_setting_configtext', + 'adminpresets_enrol_ldap_admin_setting_category' => 'adminpresets_admin_setting_configselect', + 'adminpresets_format_singleactivity_admin_setting_activitytype' => 'adminpresets_admin_setting_configselect', + 'adminpresets_qtype_multichoice_admin_setting_answernumbering' => 'adminpresets_admin_setting_configselect', + ]; + + /** @var array Relation between database fields and XML files. **/ + protected static $dbxmlrelations = [ + 'name' => 'NAME', + 'comments' => 'COMMENTS', + 'timecreated' => 'PRESET_DATE', + 'site' => 'SITE_URL', + 'author' => 'AUTHOR', + 'moodleversion' => 'MOODLE_VERSION', + 'moodlerelease' => 'MOODLE_RELEASE' + ]; + + /** + * Gets the system settings + * + * Loads the DB $CFG->prefix.'config' values and the + * $CFG->prefix.'config_plugins' values and redirects + * the flow through $this->get_settings() + * + * @return array $settings Array format $array['plugin']['settingname'] = settings_types child class + */ + public function get_site_settings(): array { + global $DB; + + // Db configs (to avoid multiple queries). + $dbconfig = $DB->get_records_select('config', '', [], '', 'name, value'); + + // Adding site settings in course table. + $frontpagevalues = $DB->get_record_select('course', 'id = 1', + [], 'fullname, shortname, summary'); + foreach ($frontpagevalues as $field => $value) { + $dbconfig[$field] = new stdClass(); + $dbconfig[$field]->name = $field; + $dbconfig[$field]->value = $value; + } + $sitedbsettings['none'] = $dbconfig; + + // Config plugins. + $configplugins = $DB->get_records('config_plugins'); + foreach ($configplugins as $configplugin) { + $sitedbsettings[$configplugin->plugin][$configplugin->name] = new stdClass(); + $sitedbsettings[$configplugin->plugin][$configplugin->name]->name = $configplugin->name; + $sitedbsettings[$configplugin->plugin][$configplugin->name]->value = $configplugin->value; + } + // Get an array with the common format. + return $this->get_settings($sitedbsettings, true, []); + } + + /** + * Constructs an array with all the system settings + * + * If a setting value can't be found on the DB it considers + * the default value as the setting value + * + * Settings without plugin are marked as 'none' in the plugin field + * + * Returns an standarized settings array format. + * + * @param array $dbsettings Standarized array, + * format $array['plugin']['name'] = obj('name'=>'settingname', 'value'=>'settingvalue') + * @param boolean $sitedbvalues Indicates if $dbsettings comes from the site db or not + * @param array $settings Array format $array['plugin']['settingname'] = settings_types child class + * @param array|false $children Array of admin_category children or false + * @return array Array format $array['plugin']['settingname'] = settings_types child class + */ + public function get_settings(array $dbsettings, bool $sitedbvalues = false, array $settings = [], $children = false): array { + global $DB; + + // If there are no children, load admin tree and iterate through. + if (!$children) { + $this->adminroot = admin_get_root(false, true); + $children = $this->adminroot->children; + } + + // Iteates through children. + foreach ($children as $key => $child) { + + // We must search category children. + if (is_a($child, 'admin_category')) { + + if ($child->children) { + $settings = $this->get_settings($dbsettings, $sitedbvalues, $settings, $child->children); + } + + // Settings page. + } else if (is_a($child, 'admin_settingpage')) { + + if (property_exists($child, 'settings')) { + + foreach ($child->settings as $values) { + $settingname = $values->name; + + unset($settingvalue); + + // Look for his config value. + if ($values->plugin == '') { + $values->plugin = 'none'; + } + + if (!empty($dbsettings[$values->plugin][$settingname])) { + $settingvalue = $dbsettings[$values->plugin][$settingname]->value; + } + + // If no db value found default value. + if ($sitedbvalues && !isset($settingvalue)) { + // For settings with multiple values. + if (is_array($values->defaultsetting)) { + + if (isset($values->defaultsetting['value'])) { + $settingvalue = $values->defaultsetting['value']; + // Configtime case, does not have a 'value' default setting. + } else { + $settingvalue = 0; + } + } else { + $settingvalue = $values->defaultsetting; + } + } + + // If there aren't any value loaded, skip that setting. + if (!isset($settingvalue)) { + continue; + } + // If there is no setting class defined continue. + if (!$setting = $this->get_setting($values, $settingvalue)) { + continue; + } + + // Settings_types childs with. + // attributes provides an attributes array. + if ($attributes = $setting->get_attributes()) { + + // Look for settings attributes if it is a presets. + if (!$sitedbvalues) { + $itemid = $dbsettings[$values->plugin][$settingname]->itemid; + $attrs = $DB->get_records('adminpresets_it_a', + ['itemid' => $itemid], '', 'name, value'); + } + foreach ($attributes as $defaultvarname => $varname) { + + unset($attributevalue); + + // Settings from site. + if ($sitedbvalues) { + if (!empty($dbsettings[$values->plugin][$varname])) { + $attributevalue = $dbsettings[$values->plugin][$varname]->value; + } + + // Settings from a preset. + } else if (!$sitedbvalues && isset($attrs[$varname])) { + $attributevalue = $attrs[$varname]->value; + } + + // If no value found, default value, + // But we may not have a default value for the attribute. + if (!isset($attributevalue) && !empty($values->defaultsetting[$defaultvarname])) { + $attributevalue = $values->defaultsetting[$defaultvarname]; + } + + // If there is no even a default for this setting will be empty. + // So we do nothing in this case. + if (isset($attributevalue)) { + $setting->set_attribute_value($varname, $attributevalue); + } + } + } + + // Setting the text. + $setting->set_text(); + + // Adding to general settings array. + $settings[$values->plugin][$settingname] = $setting; + } + } + } + } + + return $settings; + } + + /** + * Returns the class type object + * + * @param object $settingdata Setting data + * @param mixed $currentvalue + * @return mixed + */ + public function get_setting($settingdata, $currentvalue) { + + $classname = null; + + // Getting the appropiate class to get the correct setting value. + $settingtype = get_class($settingdata); + + // Check if it is a setting from a plugin. + $plugindata = explode('_', $settingtype); + $types = \core_component::get_plugin_types(); + if (array_key_exists($plugindata[0], $types)) { + $plugins = \core_component::get_plugin_list($plugindata[0]); + if (array_key_exists($plugindata[1], $plugins)) { + // Check if there is a specific class for this plugin admin setting. + $settingname = 'adminpresets_' . $settingtype; + $classname = "\\$plugindata[0]_$plugindata[1]\\adminpresets\\$settingname"; + if (!class_exists($classname)) { + $classname = null; + } + } + } else { + $settingname = 'adminpresets_' . $settingtype; + $classname = '\\core_adminpresets\\local\\setting\\' . $settingname; + if (!class_exists($classname)) { + // Check if there is some mapped class that should be used for this setting. + $classname = self::get_settings_class($settingname); + } + } + + if (is_null($classname)) { + // Return the default setting class if there is no specific class for this setting. + $classname = '\\core_adminpresets\\local\\setting\\adminpresets_setting'; + } + + return new $classname($settingdata, $currentvalue); + } + + /** + * Returns the settings class mapped to the defined $classname or null if it doesn't exist any associated class. + * + * @param string $classname The classname to get the mapped class. + * @return string|null + */ + public static function get_settings_class(string $classname): ?string { + if (array_key_exists($classname, self::$settingclassesmap)) { + return '\\core_adminpresets\\local\\setting\\' . self::$settingclassesmap[$classname]; + } + + return null; + } + + /** + * Gets the standarized settings array from DB records + * + * @param array $dbsettings Array of objects + * @return array Standarized array, + * format $array['plugin']['name'] = obj('name'=>'settingname', 'value'=>'settingvalue') + */ + public function get_settings_from_db(array $dbsettings): array { + $settings = []; + + if (!$dbsettings) { + return $settings; + } + + foreach ($dbsettings as $dbsetting) { + $settings[$dbsetting->plugin][$dbsetting->name] = new stdClass(); + $settings[$dbsetting->plugin][$dbsetting->name]->itemid = $dbsetting->id; + $settings[$dbsetting->plugin][$dbsetting->name]->name = $dbsetting->name; + $settings[$dbsetting->plugin][$dbsetting->name]->value = $dbsetting->value; + } + + return $settings; + } + + + /** + * Apply a given preset. + * + * @param int $presetid The preset identifier to apply. + * @param bool $simulate Whether this is a simulation or not. + * @return array List with an array with the applied settings and another with the skipped ones. + */ + public function apply_preset(int $presetid, bool $simulate = false): array { + global $DB; + + if (!$DB->get_record('adminpresets', ['id' => $presetid])) { + throw new moodle_exception('errornopreset', 'core_adminpresets'); + } + + // Apply preset settings. + [$settingsapplied, $settingsskipped, $appid] = $this->apply_settings($presetid, $simulate); + + // Set plugins visibility. + [$pluginsapplied, $pluginsskipped] = $this->apply_plugins($presetid, $simulate, $appid); + + $applied = array_merge($settingsapplied, $pluginsapplied); + $skipped = array_merge($settingsskipped, $pluginsskipped); + + if (!$simulate) { + // Store it in a config setting as the last preset applied. + set_config('lastpresetapplied', $presetid, 'tool_admin_presets'); + } + + return [$applied, $skipped]; + } + + /** + * Create a preset with the current settings and plugins information. + * + * @param \stdClass $data Preset info, such as name or description, to be used when creating the preset with the current + * settings and plugins. + * @return array List with an the presetid created (int), a boolean to define if any setting has been found and + * another boolean to specify if any plugin has been found. + */ + public function export_preset(stdClass $data): array { + global $DB; + + // Admin_preset record. + $presetdata = [ + 'name' => $data->name ?? '', + 'comments' => !empty($data->comments) ? $data->comments['text'] : '', + 'author' => $data->author ?? '', + ]; + if (!$presetid = helper::create_preset($presetdata)) { + throw new moodle_exception('errorinserting', 'core_adminpresets'); + } + + // Store settings. + $settingsfound = false; + + // Site settings. + $sitesettings = $this->get_site_settings(); + + // Sensible settings. + $sensiblesettings = explode(',', str_replace(' ', '', get_config('tool_admin_presets', 'sensiblesettings'))); + $sensiblesettings = array_combine($sensiblesettings, $sensiblesettings); + foreach ($sitesettings as $plugin => $pluginsettings) { + foreach ($pluginsettings as $settingname => $sitesetting) { + // Avoid sensible data. + if (empty($data->includesensiblesettings) && !empty($sensiblesettings["$settingname@@$plugin"])) { + continue; + } + + $setting = new stdClass(); + $setting->adminpresetid = $presetid; + $setting->plugin = $plugin; + $setting->name = $settingname; + $setting->value = $sitesetting->get_value(); + if (!$setting->id = $DB->insert_record('adminpresets_it', $setting)) { + throw new moodle_exception('errorinserting', 'core_adminpresets'); + } + + // Setting attributes must also be exported. + if ($attributes = $sitesetting->get_attributes_values()) { + foreach ($attributes as $attname => $attvalue) { + $attr = new stdClass(); + $attr->itemid = $setting->id; + $attr->name = $attname; + $attr->value = $attvalue; + + $DB->insert_record('adminpresets_it_a', $attr); + } + } + $settingsfound = true; + } + } + + // Store plugins visibility (enabled/disabled). + $pluginsfound = false; + $pluginmanager = \core_plugin_manager::instance(); + $types = $pluginmanager->get_plugin_types(); + foreach ($types as $plugintype => $notused) { + $plugins = $pluginmanager->get_present_plugins($plugintype); + $pluginclass = \core_plugin_manager::resolve_plugininfo_class($plugintype); + if (!empty($plugins)) { + foreach ($plugins as $pluginname => $plugin) { + $entry = new stdClass(); + $entry->adminpresetid = $presetid; + $entry->plugin = $plugintype; + $entry->name = $pluginname; + $entry->enabled = $pluginclass::get_enabled_plugin($pluginname); + + $DB->insert_record('adminpresets_plug', $entry); + $pluginsfound = true; + } + } + } + + // If there are no settings nor plugins, the admin preset record should be removed. + if (!$settingsfound && !$pluginsfound) { + $DB->delete_records('adminpresets', ['id' => $presetid]); + $presetid = null; + } + + return [$presetid, $settingsfound, $pluginsfound]; + } + + /** + * Create the XML content for a given preset. + * + * @param int $presetid The preset to download. + * @return array List with the XML content (string) and a filename proposal based on the preset name (string). + */ + public function download_preset(int $presetid): array { + global $DB; + + if (!$preset = $DB->get_record('adminpresets', ['id' => $presetid])) { + throw new moodle_exception('errornopreset', 'core_adminpresets'); + } + + // Start. + $xmloutput = new memory_xml_output(); + $xmlwriter = new xml_writer($xmloutput); + $xmlwriter->start(); + + // Preset data. + $xmlwriter->begin_tag('PRESET'); + foreach (static::$dbxmlrelations as $dbname => $xmlname) { + $xmlwriter->full_tag($xmlname, $preset->$dbname); + } + + // We ride through the settings array. + $items = $DB->get_records('adminpresets_it', ['adminpresetid' => $preset->id]); + $allsettings = $this->get_settings_from_db($items); + if ($allsettings) { + $xmlwriter->begin_tag('ADMIN_SETTINGS'); + + foreach ($allsettings as $plugin => $settings) { + $tagname = strtoupper($plugin); + + // To aviod xml slash problems. + if (strstr($tagname, '/') != false) { + $tagname = str_replace('/', '__', $tagname); + } + + $xmlwriter->begin_tag($tagname); + + // One tag for each plugin setting. + if (!empty($settings)) { + $xmlwriter->begin_tag('SETTINGS'); + foreach ($settings as $setting) { + // Unset the tag attributes string. + $attributes = []; + + // Getting setting attributes, if present. + $attrs = $DB->get_records('adminpresets_it_a', ['itemid' => $setting->itemid]); + if ($attrs) { + foreach ($attrs as $attr) { + $attributes[$attr->name] = $attr->value; + } + } + + $xmlwriter->full_tag(strtoupper($setting->name), $setting->value, $attributes); + } + + $xmlwriter->end_tag('SETTINGS'); + } + + $xmlwriter->end_tag(strtoupper($tagname)); + } + + $xmlwriter->end_tag('ADMIN_SETTINGS'); + } + + // We ride through the plugins array. + $data = $DB->get_records('adminpresets_plug', ['adminpresetid' => $preset->id]); + if ($data) { + $plugins = []; + foreach ($data as $plugin) { + $plugins[$plugin->plugin][] = $plugin; + } + + $xmlwriter->begin_tag('PLUGINS'); + + foreach ($plugins as $plugintype => $plugintypes) { + $tagname = strtoupper($plugintype); + $xmlwriter->begin_tag($tagname); + + foreach ($plugintypes as $plugin) { + $xmlwriter->full_tag(strtoupper($plugin->name), $plugin->enabled); + } + + $xmlwriter->end_tag(strtoupper($tagname)); + } + + $xmlwriter->end_tag('PLUGINS'); + } + + // End. + $xmlwriter->end_tag('PRESET'); + $xmlwriter->stop(); + $xmlstr = $xmloutput->get_allcontents(); + + $filename = addcslashes($preset->name, '"') . '.xml'; + + return [$xmlstr, $filename]; + } + + /** + * Import a given XML preset. + * + * @param string $xmlcontent The XML context with the preset to be imported. + * @param string|null $presetname The preset name that will overwrite the one given in the XML file. + * @return array List with an the XML element (SimpleXMLElement|null), the imported preset (stdClass|null), a boolean + * to define if any setting has been found and another boolean to specify if any plugin has been found. + */ + public function import_preset(string $xmlcontent, ?string $presetname = null): array { + global $DB, $USER; + + $settingsfound = false; + $pluginsfound = false; + + try { + $xml = simplexml_load_string($xmlcontent); + } catch (\Exception $exception) { + $xml = false; + } + if (!$xml) { + return [null, null, $settingsfound, $pluginsfound]; + } + + // Prepare the preset info. + $preset = new stdClass(); + foreach (static::$dbxmlrelations as $dbname => $xmlname) { + $preset->$dbname = (String) $xml->$xmlname; + } + $preset->userid = $USER->id; + $preset->timeimported = time(); + + // Overwrite preset name. + if (!empty($presetname)) { + $preset->name = $presetname; + } + + // Create the preset. + if (!$preset->id = $DB->insert_record('adminpresets', $preset)) { + throw new moodle_exception('errorinserting', 'core_adminpresets'); + } + + // Process settings. + $sitesettings = $this->get_site_settings(); + $xmladminsettings = $xml->ADMIN_SETTINGS[0]; + foreach ($xmladminsettings as $plugin => $settings) { + $plugin = strtolower($plugin); + if (strstr($plugin, '__') != false) { + $plugin = str_replace('__', '/', $plugin); + } + + $pluginsettings = $settings->SETTINGS[0]; + if ($pluginsettings) { + foreach ($pluginsettings->children() as $name => $setting) { + $name = strtolower($name); + + // Default to ''. + if ($setting->__toString() === false) { + $value = ''; + } else { + $value = $setting->__toString(); + } + + if (empty($sitesettings[$plugin][$name])) { + debugging('Setting ' . $plugin . '/' . $name . ' not supported by this Moodle version', DEBUG_DEVELOPER); + continue; + } + + // Cleaning the setting value. + if (!$presetsetting = $this->get_setting($sitesettings[$plugin][$name]->get_settingdata(), $value)) { + debugging('Setting ' . $plugin . '/' . $name . ' not implemented', DEBUG_DEVELOPER); + continue; + } + + $settingsfound = true; + + // New item. + $item = new stdClass(); + $item->adminpresetid = $preset->id; + $item->plugin = $plugin; + $item->name = $name; + $item->value = $presetsetting->get_value(); + + // Insert preset item. + if (!$item->id = $DB->insert_record('adminpresets_it', $item)) { + throw new moodle_exception('errorinserting', 'core_adminpresets'); + } + + // Add setting attributes. + if ($setting->attributes() && ($itemattributes = $presetsetting->get_attributes())) { + foreach ($setting->attributes() as $attrname => $attrvalue) { + $itemattributenames = array_flip($itemattributes); + + // Check the attribute existence. + if (!isset($itemattributenames[$attrname])) { + debugging('The ' . $plugin . '/' . $name . ' attribute ' . $attrname . + ' is not supported by this Moodle version', DEBUG_DEVELOPER); + continue; + } + + $attr = new stdClass(); + $attr->itemid = $item->id; + $attr->name = $attrname; + $attr->value = $attrvalue->__toString(); + $DB->insert_record('adminpresets_it_a', $attr); + } + } + } + } + } + + // Process plugins. + if ($xml->PLUGINS) { + $xmlplugins = $xml->PLUGINS[0]; + foreach ($xmlplugins as $plugin => $plugins) { + $pluginname = strtolower($plugin); + foreach ($plugins->children() as $name => $plugin) { + $pluginsfound = true; + + // New plugin. + $entry = new stdClass(); + $entry->adminpresetid = $preset->id; + $entry->plugin = $pluginname; + $entry->name = strtolower($name); + $entry->enabled = $plugin->__toString(); + + // Insert plugin. + if (!$entry->id = $DB->insert_record('adminpresets_plug', $entry)) { + throw new moodle_exception('errorinserting', 'core_adminpresets'); + } + } + } + } + + // If there are no valid or selected settings we should delete the admin preset record. + if (!$settingsfound && !$pluginsfound) { + $DB->delete_records('adminpresets', ['id' => $preset->id]); + $preset = null; + } + + return [$xml, $preset, $settingsfound, $pluginsfound]; + } + + /** + * Delete given preset. + * + * @param int $presetid Preset identifier to delete. + * @return void + */ + public function delete_preset(int $presetid): void { + global $DB; + + // Check the preset exists. + $preset = $DB->get_record('adminpresets', ['id' => $presetid]); + if (!$preset) { + throw new moodle_exception('errordeleting', 'core_adminpresets'); + } + + // Deleting the preset applications. + if ($previouslyapplied = $DB->get_records('adminpresets_app', ['adminpresetid' => $presetid], 'id')) { + $appids = array_keys($previouslyapplied); + list($insql, $inparams) = $DB->get_in_or_equal($appids); + $DB->delete_records_select('adminpresets_app_it', "adminpresetapplyid $insql", $inparams); + $DB->delete_records_select('adminpresets_app_it_a', "adminpresetapplyid $insql", $inparams); + $DB->delete_records_select('adminpresets_app_plug', "adminpresetapplyid $insql", $inparams); + + if (!$DB->delete_records('adminpresets_app', ['adminpresetid' => $presetid])) { + throw new moodle_exception('errordeleting', 'core_adminpresets'); + } + } + + // Getting items ids and remove advanced items associated to them. + $items = $DB->get_records('adminpresets_it', ['adminpresetid' => $presetid], 'id'); + if (!empty($items)) { + $itemsid = array_keys($items); + list($insql, $inparams) = $DB->get_in_or_equal($itemsid); + $DB->delete_records_select('adminpresets_it_a', "itemid $insql", $inparams); + } + + if (!$DB->delete_records('adminpresets_it', ['adminpresetid' => $presetid])) { + throw new moodle_exception('errordeleting', 'core_adminpresets'); + } + + // Delete plugins. + if (!$DB->delete_records('adminpresets_plug', ['adminpresetid' => $presetid])) { + throw new moodle_exception('errordeleting', 'core_adminpresets'); + } + + // Delete preset. + if (!$DB->delete_records('adminpresets', ['id' => $presetid])) { + throw new moodle_exception('errordeleting', 'core_adminpresets'); + } + } + + /** + * Revert a given preset applied previously. + * It backs settings and plugins to their original state before applying the presset and removes + * the applied preset information from DB. + * + * @param int $presetappid The appplied preset identifier to be reverted. + * @return array List with the presetapp removed (or null if there was some error), an array with the rollback settings/plugins + * changed and an array with the failures. + */ + public function revert_preset(int $presetappid): array { + global $DB; + + // To store rollback results. + $presetapp = null; + $rollback = []; + $failures = []; + + // Actual settings. + $sitesettings = $this->get_site_settings(); + + if (!$DB->get_record('adminpresets_app', ['id' => $presetappid])) { + throw new moodle_exception('wrongid', 'core_adminpresets'); + } + + // Items. + $itemsql = "SELECT cl.id, cl.plugin, cl.name, cl.value, cl.oldvalue, ap.adminpresetapplyid + FROM {adminpresets_app_it} ap + JOIN {config_log} cl ON cl.id = ap.configlogid + WHERE ap.adminpresetapplyid = :presetid"; + $itemchanges = $DB->get_records_sql($itemsql, ['presetid' => $presetappid]); + if ($itemchanges) { + foreach ($itemchanges as $change) { + if ($change->plugin == '') { + $change->plugin = 'none'; + } + + // Admin setting. + if (!empty($sitesettings[$change->plugin][$change->name])) { + $actualsetting = $sitesettings[$change->plugin][$change->name]; + $oldsetting = $this->get_setting($actualsetting->get_settingdata(), $change->oldvalue); + $oldsetting->set_text(); + + $visiblepluginname = $oldsetting->get_settingdata()->plugin; + if ($visiblepluginname == 'none') { + $visiblepluginname = 'core'; + } + $contextdata = [ + 'plugin' => $visiblepluginname, + 'visiblename' => $oldsetting->get_settingdata()->visiblename, + 'oldvisiblevalue' => $actualsetting->get_visiblevalue(), + 'visiblevalue' => $oldsetting->get_visiblevalue() + ]; + + // Check if the actual value is the same set by the preset. + if ($change->value == $actualsetting->get_value()) { + $oldsetting->save_value(); + + // Output table. + $rollback[] = $contextdata; + + // Deleting the adminpreset applied item instance. + $deletewhere = [ + 'adminpresetapplyid' => $change->adminpresetapplyid, + 'configlogid' => $change->id, + ]; + $DB->delete_records('adminpresets_app_it', $deletewhere); + + } else { + $failures[] = $contextdata; + } + } + } + } + + // Attributes. + $attrsql = "SELECT cl.id, cl.plugin, cl.name, cl.value, cl.oldvalue, ap.itemname, ap.adminpresetapplyid + FROM {adminpresets_app_it_a} ap + JOIN {config_log} cl ON cl.id = ap.configlogid + WHERE ap.adminpresetapplyid = :presetid"; + $attrchanges = $DB->get_records_sql($attrsql, ['presetid' => $presetappid]); + if ($attrchanges) { + foreach ($attrchanges as $change) { + if ($change->plugin == '') { + $change->plugin = 'none'; + } + + // Admin setting of the attribute item. + if (!empty($sitesettings[$change->plugin][$change->itemname])) { + // Getting the attribute item. + $actualsetting = $sitesettings[$change->plugin][$change->itemname]; + + $oldsetting = $this->get_setting($actualsetting->get_settingdata(), $actualsetting->get_value()); + $oldsetting->set_attribute_value($change->name, $change->oldvalue); + $oldsetting->set_text(); + + $varname = $change->plugin . '_' . $change->name; + + // Check if the actual value is the same set by the preset. + $actualattributes = $actualsetting->get_attributes_values(); + if ($change->value == $actualattributes[$change->name]) { + $oldsetting->save_attributes_values(); + + // Output table. + $visiblepluginname = $oldsetting->get_settingdata()->plugin; + if ($visiblepluginname == 'none') { + $visiblepluginname = 'core'; + } + $rollback[] = [ + 'plugin' => $visiblepluginname, + 'visiblename' => $oldsetting->get_settingdata()->visiblename, + 'oldvisiblevalue' => $actualsetting->get_visiblevalue(), + 'visiblevalue' => $oldsetting->get_visiblevalue() + ]; + + // Deleting the adminpreset applied item attribute instance. + $deletewhere = [ + 'adminpresetapplyid' => $change->adminpresetapplyid, + 'configlogid' => $change->id, + ]; + $DB->delete_records('adminpresets_app_it_a', $deletewhere); + + } else { + $visiblepluginname = $oldsetting->get_settingdata()->plugin; + if ($visiblepluginname == 'none') { + $visiblepluginname = 'core'; + } + $failures[] = [ + 'plugin' => $visiblepluginname, + 'visiblename' => $oldsetting->get_settingdata()->visiblename, + 'oldvisiblevalue' => $actualsetting->get_visiblevalue(), + 'visiblevalue' => $oldsetting->get_visiblevalue() + ]; + } + } + } + } + + // Plugins. + $plugins = $DB->get_records('adminpresets_app_plug', ['adminpresetapplyid' => $presetappid]); + if ($plugins) { + foreach ($plugins as $plugin) { + $pluginclass = \core_plugin_manager::resolve_plugininfo_class($plugin->plugin); + $pluginclass::enable_plugin($plugin->name, (int) $plugin->oldvalue); + + $visiblename = $plugin->plugin . '_' . $plugin->name; + if (get_string_manager()->string_exists('pluginname', $plugin->plugin . '_' . $plugin->name)) { + $visiblename = get_string('pluginname', $plugin->plugin . '_' . $plugin->name); + } + + // Output table. + $rollback[] = [ + 'plugin' => $plugin->plugin, + 'visiblename' => $visiblename, + 'oldvisiblevalue' => $plugin->value, + 'visiblevalue' => $plugin->oldvalue, + ]; + } + $DB->delete_records('adminpresets_app_plug', ['adminpresetapplyid' => $presetappid]); + } + + // Delete application if no items nor attributes nor plugins of the application remains. + if (!$DB->get_records('adminpresets_app_it', ['adminpresetapplyid' => $presetappid]) && + !$DB->get_records('adminpresets_app_it_a', ['adminpresetapplyid' => $presetappid]) && + !$DB->get_records('adminpresets_app_plug', ['adminpresetapplyid' => $presetappid])) { + + $presetapp = $DB->get_record('adminpresets_app', ['id' => $presetappid]); + $DB->delete_records('adminpresets_app', ['id' => $presetappid]); + } + + return [$presetapp, $rollback, $failures]; + } + + /** + * Apply settings from a preset. + * + * @param int $presetid The preset identifier to apply. + * @param bool $simulate Whether this is a simulation or not. + * @param int|null $adminpresetapplyid The identifier of the adminpresetapply or null if it hasn't been created previously. + * @return array List with an array with the applied settings, another with the skipped ones and the adminpresetapplyid. + */ + protected function apply_settings(int $presetid, bool $simulate = false, ?int $adminpresetapplyid = null): array { + global $DB, $USER; + + $applied = []; + $skipped = []; + if (!$items = $DB->get_records('adminpresets_it', ['adminpresetid' => $presetid])) { + return [$applied, $skipped, $adminpresetapplyid]; + } + + $presetdbsettings = $this->get_settings_from_db($items); + // Standarized format: $array['plugin']['settingname'] = child class. + $presetsettings = $this->get_settings($presetdbsettings, false, []); + + // Standarized format: $array['plugin']['settingname'] = child class. + $siteavailablesettings = $this->get_site_settings(); + + // Set settings values. + foreach ($presetsettings as $plugin => $pluginsettings) { + foreach ($pluginsettings as $settingname => $presetsetting) { + $updatesetting = false; + + // Current value (which will become old value if the setting is legit to be applied). + $sitesetting = $siteavailablesettings[$plugin][$settingname]; + + // Wrong setting, set_value() method has previously cleaned the value. + if ($sitesetting->get_value() === false) { + debugging($presetsetting->get_settingdata()->plugin . '/' . $presetsetting->get_settingdata()->name . + ' setting has a wrong value!', DEBUG_DEVELOPER); + continue; + } + + // If the new value is different the setting must be updated. + if ($presetsetting->get_value() != $sitesetting->get_value()) { + $updatesetting = true; + } + + // If one of the setting attributes values is different, setting must also be updated. + if ($presetsetting->get_attributes_values()) { + + $siteattributesvalues = $presetsetting->get_attributes_values(); + foreach ($presetsetting->get_attributes_values() as $attributename => $attributevalue) { + + if ($attributevalue !== $siteattributesvalues[$attributename]) { + $updatesetting = true; + } + } + } + + $visiblepluginname = $presetsetting->get_settingdata()->plugin; + if ($visiblepluginname == 'none') { + $visiblepluginname = 'core'; + } + $data = [ + 'plugin' => $visiblepluginname, + 'visiblename' => $presetsetting->get_settingdata()->visiblename, + 'visiblevalue' => $presetsetting->get_visiblevalue(), + ]; + + // Saving data. + if ($updatesetting) { + // The preset application it's only saved when differences (in their values) are found. + if (empty($applieditem)) { + // Save the preset application and store the preset applied id. + $presetapplied = new stdClass(); + $presetapplied->adminpresetid = $presetid; + $presetapplied->userid = $USER->id; + $presetapplied->time = time(); + if (!$simulate && !$adminpresetapplyid = $DB->insert_record('adminpresets_app', $presetapplied)) { + throw new moodle_exception('errorinserting', 'core_adminpresets'); + } + } + + // Implemented this way because the config_write method of admin_setting class does not return the + // config_log inserted id. + $applieditem = new stdClass(); + $applieditem->adminpresetapplyid = $adminpresetapplyid; + if (!$simulate && $applieditem->configlogid = $presetsetting->save_value()) { + $DB->insert_record('adminpresets_app_it', $applieditem); + } + + // For settings with multiple values. + if (!$simulate && $attributeslogids = $presetsetting->save_attributes_values()) { + foreach ($attributeslogids as $attributelogid) { + $applieditemattr = new stdClass(); + $applieditemattr->adminpresetapplyid = $applieditem->adminpresetapplyid; + $applieditemattr->configlogid = $attributelogid; + $applieditemattr->itemname = $presetsetting->get_settingdata()->name; + $DB->insert_record('adminpresets_app_it_a', $applieditemattr); + } + } + + // Added to changed values. + $data['oldvisiblevalue'] = $sitesetting->get_visiblevalue(); + $applied[] = $data; + } else { + // Unnecessary changes (actual setting value). + $skipped[] = $data; + } + } + } + return [$applied, $skipped, $adminpresetapplyid]; + } + + /** + * Apply plugins from a preset. + * + * @param int $presetid The preset identifier to apply. + * @param bool $simulate Whether this is a simulation or not. + * @param int|null $adminpresetapplyid The identifier of the adminpresetapply or null if it hasn't been created previously. + * @return array List with an array with the applied settings, another with the skipped ones and the adminpresetapplyid. + */ + protected function apply_plugins(int $presetid, bool $simulate = false, ?int $adminpresetapplyid = null): array { + global $DB, $USER; + + $applied = []; + $skipped = []; + + $strenabled = get_string('enabled', 'core_adminpresets'); + $strdisabled = get_string('disabled', 'core_adminpresets'); + + $plugins = $DB->get_records('adminpresets_plug', ['adminpresetid' => $presetid]); + foreach ($plugins as $plugin) { + $pluginclass = \core_plugin_manager::resolve_plugininfo_class($plugin->plugin); + $oldvalue = $pluginclass::get_enabled_plugin($plugin->name); + + $visiblename = $plugin->plugin . '_' . $plugin->name; + if (get_string_manager()->string_exists('pluginname', $plugin->plugin . '_' . $plugin->name)) { + $visiblename = get_string('pluginname', $plugin->plugin . '_' . $plugin->name); + } + if ($plugin->enabled > 0) { + $visiblevalue = $strenabled; + } else if ($plugin->enabled == 0) { + $visiblevalue = $strdisabled; + } else { + $visiblevalue = get_string('disabledwithvalue', 'core_adminpresets', $plugin->enabled); + } + + $data = [ + 'plugin' => $plugin->plugin, + 'visiblename' => $visiblename, + 'visiblevalue' => $visiblevalue, + ]; + + if ($pluginclass == '\core\plugininfo\orphaned') { + $skipped[] = $data; + continue; + } + + // Only change the plugin visibility if it's different to current value. + if (($plugin->enabled != $oldvalue) && (($plugin->enabled > 0 && !$oldvalue) || ($plugin->enabled < 1 && $oldvalue))) { + try { + if (!$simulate) { + $pluginclass::enable_plugin($plugin->name, $plugin->enabled); + + // The preset application it's only saved when values differences are found. + if (empty($adminpresetapplyid)) { + // Save the preset application and store the preset applied id. + $presetapplied = new stdClass(); + $presetapplied->adminpresetid = $presetid; + $presetapplied->userid = $USER->id; + $presetapplied->time = time(); + if (!$adminpresetapplyid = $DB->insert_record('adminpresets_app', $presetapplied)) { + throw new moodle_exception('errorinserting', 'core_adminpresets'); + } + } + + // Add plugin to aplied plugins table (for being able to restore in the future if required). + $appliedplug = new stdClass(); + $appliedplug->adminpresetapplyid = $adminpresetapplyid; + $appliedplug->plugin = $plugin->plugin; + $appliedplug->name = $plugin->name; + $appliedplug->value = $plugin->enabled; + $appliedplug->oldvalue = $oldvalue; + $DB->insert_record('adminpresets_app_plug', $appliedplug); + } + + if ($oldvalue > 0) { + $oldvisiblevalue = $strenabled; + } else if ($oldvalue == 0) { + $oldvisiblevalue = $strdisabled; + } else { + $oldvisiblevalue = get_string('disabledwithvalue', 'core_adminpresets', $oldvalue); + } + $data['oldvisiblevalue'] = $oldvisiblevalue; + $applied[] = $data; + } catch (\exception $e) { + $skipped[] = $data; + } + } else { + $skipped[] = $data; + } + } + + return [$applied, $skipped, $adminpresetapplyid]; + } + +} diff --git a/adminpresets/classes/privacy/provider.php b/adminpresets/classes/privacy/provider.php new file mode 100644 index 00000000000..c552b0b45f7 --- /dev/null +++ b/adminpresets/classes/privacy/provider.php @@ -0,0 +1,125 @@ +. + +namespace core_adminpresets\privacy; + +use core_privacy\local\metadata\collection; +use core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\approved_userlist; +use core_privacy\local\request\contextlist; +use core_privacy\local\request\userlist; + +/** + * Admin presets this file handle privacy provider. + * + * @package core_adminpresets + * @copyright 2021 Pimenko + * @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó code + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements + \core_privacy\local\metadata\provider, + \core_privacy\local\request\subsystem\provider, + \core_privacy\local\request\core_userlist_provider { + + /** + * Returns information about the user data stored in this component. + * + * @param collection $collection A list of information about this component + * @return collection The collection object filled out with information about this component. + */ + public static function get_metadata(collection $collection) : collection { + // These tables are really data about site configuration and not user data. + + // The adminpresets includes information about which user performed a configuration change using the admin_presets + // tool. + // This is not considered to be user data. + $collection->add_database_table('adminpresets', [ + 'userid' => 'privacy:metadata:adminpresets:userid', + 'name' => 'privacy:metadata:adminpresets:name', + 'comments' => 'privacy:metadata:adminpresets:comments', + 'site' => 'privacy:metadata:adminpresets:site', + 'moodlerelease' => 'privacy:metadata:adminpresets:moodlerelease', + 'timecreated' => 'privacy:metadata:adminpresets:timecreated', + ], 'privacy:metadata:adminpresets'); + + // The adminpresets_app includes information about which user performed configuration change using the admin_presets + // tool. + // This is not considered to be user data. + $collection->add_database_table('adminpresets_app', [ + 'adminpresetid' => 'privacy:metadata:adminpresets_app:adminpresetid', + 'userid' => 'privacy:metadata:adminpresets_app:userid', + 'time' => 'privacy:metadata:adminpresets_app:time', + ], 'privacy:metadata:adminpresets_app'); + + return $collection; + } + + /** + * Get the list of contexts that contain user information for the specified user. + * + * @param int $userid The user to search. + * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. + */ + public static function get_contexts_for_userid(int $userid) : contextlist { + return new contextlist(); + } + + /** + * Get the list of users who have data within a context. + * + * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination. + */ + public static function get_users_in_context(userlist $userlist) { + // Don't add any user. + } + + /** + * Export all user data for the specified user, in the specified contexts. + * + * @param approved_contextlist $contextlist The approved contexts to export information for. + */ + public static function export_user_data(approved_contextlist $contextlist) { + // None of the core tables should be exported. + } + + /** + * Delete all data for all users in the specified context. + * + * @param context $context The specific context to delete data for. + */ + public static function delete_data_for_all_users_in_context(\context $context) { + // None of the the data from these tables should be deleted. + } + + /** + * Delete all user data for the specified user, in the specified contexts. + * + * @param approved_contextlist $contextlist The approved contexts and user information to delete information for. + */ + public static function delete_data_for_user(approved_contextlist $contextlist) { + // None of the the data from these tables should be deleted. + } + + /** + * Delete multiple users within a single context. + * + * @param approved_userlist $userlist The approved context and user information to delete information for. + */ + public static function delete_data_for_users(approved_userlist $userlist) { + // None of the the data from these tables should be deleted. + } +} diff --git a/adminpresets/tests/fixtures/import_settings.xml b/adminpresets/tests/fixtures/import_settings.xml new file mode 100644 index 00000000000..42d7dcabc88 --- /dev/null +++ b/adminpresets/tests/fixtures/import_settings.xml @@ -0,0 +1,24 @@ + + + Imported preset + <p dir="ltr" style="text-align:left;">Porfolios enabled, Emojipicker enabled, Lesson media width set to 900 and maxanswers set to 2 and disabled advanced</p> + 1631615985 + http://demo.moodle + Ada Lovelace + 2021091100 + 4.0dev (Build: 20210911) + + + + 2 + 900 + + + + + 1 + 1 + + + + diff --git a/adminpresets/tests/fixtures/import_settings_plugins.xml b/adminpresets/tests/fixtures/import_settings_plugins.xml new file mode 100644 index 00000000000..c09ac84cfc9 --- /dev/null +++ b/adminpresets/tests/fixtures/import_settings_plugins.xml @@ -0,0 +1,38 @@ + + + Imported preset + <p dir="ltr" style="text-align:left;">Porfolios enabled, Emojipicker enabled, Lesson media width set to 900 and maxanswers set to 2 and disabled advanced. Plugins: disabled block_html, mod_database and mod_chat plugins and enabled atto_html, block_activity_modules and mod_lesson.</p> + 1631615985 + http://demo.moodle + Ada Lovelace + 2021091100 + 4.0dev (Build: 20210911) + + + + 2 + 900 + + + + + 1 + 1 + + + + + + 1 + + + 0 + 1 + + + 0 + 0 + 1 + + + diff --git a/adminpresets/tests/fixtures/import_settings_with_unexisting_setting.xml b/adminpresets/tests/fixtures/import_settings_with_unexisting_setting.xml new file mode 100644 index 00000000000..f0cecc581cc --- /dev/null +++ b/adminpresets/tests/fixtures/import_settings_with_unexisting_setting.xml @@ -0,0 +1,25 @@ + + + Imported preset + <p dir="ltr" style="text-align:left;">Porfolios enabled, Emojipicker enabled, Lesson media width set to 900 and maxanswers set to 2 and disabled advanced</p> + 1631615985 + http://demo.moodle + Ada Lovelace + 2021091100 + 4.0dev (Build: 20210911) + + + + 2 + 900 + + + + + 0 + 1 + 1 + + + + diff --git a/adminpresets/tests/fixtures/import_starter_name.xml b/adminpresets/tests/fixtures/import_starter_name.xml new file mode 100644 index 00000000000..a3265077d1e --- /dev/null +++ b/adminpresets/tests/fixtures/import_starter_name.xml @@ -0,0 +1,38 @@ + + + Starter + <p dir="ltr" style="text-align:left;">Badges disabled, Emojipicker enabled, Lesson media width set to 900 and maxanswers set to 2 and disabled advanced. Plugins: disabled block_html, mod_database and mod_chat plugins and enabled atto_html and block_activity_modules.</p> + 1631615985 + http://demo.moodle + Ada Lovelace + 2021091100 + 4.0dev (Build: 20210911) + + + + 2 + 900 + + + + + 1 + 0 + + + + + + 1 + + + 0 + 1 + + + 0 + 0 + 1 + + + diff --git a/adminpresets/tests/fixtures/invalid_xml_file.xml b/adminpresets/tests/fixtures/invalid_xml_file.xml new file mode 100644 index 00000000000..6e262822d63 --- /dev/null +++ b/adminpresets/tests/fixtures/invalid_xml_file.xml @@ -0,0 +1,23 @@ + + + Imported preset - Invalid XML file + + 1631615985 + http://demo.moodle + Ada Lovelace + 2021091100 + 4.0dev (Build: 20210911) + + + + 2 + 900 + + + + + 1 + 1 + + + diff --git a/adminpresets/tests/fixtures/unexisting_category.xml b/adminpresets/tests/fixtures/unexisting_category.xml new file mode 100644 index 00000000000..47762f460eb --- /dev/null +++ b/adminpresets/tests/fixtures/unexisting_category.xml @@ -0,0 +1,17 @@ + + + Imported preset with unexisting category + + 1631615985 + http://demo.moodle + Ada Lovelace + 2021091100 + 4.0dev (Build: 20210911) + + + + 1 + + + + diff --git a/adminpresets/tests/fixtures/unexisting_setting.xml b/adminpresets/tests/fixtures/unexisting_setting.xml new file mode 100644 index 00000000000..984d6f01c77 --- /dev/null +++ b/adminpresets/tests/fixtures/unexisting_setting.xml @@ -0,0 +1,17 @@ + + + Imported preset with unexisting setting + + 1631615985 + http://demo.moodle + Ada Lovelace + 2021091100 + 4.0dev (Build: 20210911) + + + + 0 + + + + diff --git a/adminpresets/tests/generator/lib.php b/adminpresets/tests/generator/lib.php new file mode 100644 index 00000000000..41e514746a1 --- /dev/null +++ b/adminpresets/tests/generator/lib.php @@ -0,0 +1,245 @@ +. + +defined('MOODLE_INTERNAL') || die(); + +use core_adminpresets\local\setting\adminpresets_setting; +use core_adminpresets\manager; +use core_adminpresets\helper; + +global $CFG; +require_once($CFG->libdir . '/adminlib.php'); + +/** + * Data generator for adminpresets component. + * + * @package core_adminpresets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class core_adminpresets_generator extends \component_generator_base { + + /** + * Create a preset. This preset will have only 3 settings and 3 plugins. + * Settings: + * - none.enablebadges = 0 + * - none.allowemojipicker = 1 + * - mod_lesson.mediawidth = 900 + * - mod_lesson.maxanswers = 2 with advanced disabled. + * Plugins: + * - enrol_guest = 0 + * - mod_glossary = 0 + * - qtype_truefalse = 1 + * + * @param array $data Preset data. Supported values: + * - name. To define the preset name. + * - comments. To change the comments field. + * - author. To set the author. + * - applypreset. Whether the preset should be applied too or not. + * @return int Identifier of the preset created. + */ + public function create_preset(array $data = []): int { + global $DB, $USER, $CFG; + + if (!isset($data['name'])) { + $data['name'] = 'Preset default name'; + } + if (!isset($data['comments'])) { + $data['comments'] = 'Preset default comment'; + } + if (!isset($data['author'])) { + $data['author'] = 'Default author'; + } + + $preset = [ + 'userid' => $USER->id, + 'name' => $data['name'], + 'comments' => $data['comments'], + 'site' => $CFG->wwwroot, + 'author' => $data['author'], + 'moodleversion' => $CFG->version, + 'moodlerelease' => $CFG->release, + 'timecreated' => time(), + 'timeimported' => 0, + ]; + + $presetid = $DB->insert_record('adminpresets', $preset); + $preset['id'] = $presetid; + + // Setting: enablebadges = 0. + helper::add_item($presetid, 'enablebadges', '0'); + // Setting: allowemojipicker = 1. + helper::add_item($presetid, 'allowemojipicker', '1'); + // Setting: mediawidth = 900. + helper::add_item($presetid, 'mediawidth', '900', 'mod_lesson'); + // Setting: maxanswers = 2 (with advanced disabled). + helper::add_item($presetid, 'maxanswers', '2', 'mod_lesson', 'maxanswers_adv', 0); + + // Plugin: enrol_guest = 0. + helper::add_plugin($presetid, 'enrol', 'guest', 0); + // Plugin: mod_glossary = 0. + helper::add_plugin($presetid, 'mod', 'glossary', 0); + // Plugin: qtype_truefalse. + helper::add_plugin($presetid, 'qtype', 'truefalse', 1); + + // Check if the preset should be created as applied preset too, to fill in the rest of the tables. + $applypreset = isset($data['applypreset']) && $data['applypreset']; + if ($applypreset) { + $presetapp = [ + 'adminpresetid' => $presetid, + 'userid' => $USER->id, + 'time' => time(), + ]; + $appid = $DB->insert_record('adminpresets_app', $presetapp); + + $this->apply_setting($appid, 'enablebadges', '1', '0'); + // The allowemojipicker setting shouldn't be applied because the value matches the current one. + $this->apply_setting($appid, 'mediawidth', '640', '900', 'mod_lesson'); + $this->apply_setting($appid, 'maxanswers', '5', '2', 'mod_lesson'); + $this->apply_setting($appid, 'maxanswers_adv', '1', '0', 'mod_lesson', 'maxanswers'); + + $this->apply_plugin($appid, 'enrol', 'guest', 1, 0); + $this->apply_plugin($appid, 'mod', 'glossary', 1, 0); + // The qtype_truefalse plugin shouldn't be applied because the value matches the current one. + } + + return $presetid; + } + + /** + * Helper method to create an applied setting item. + * + * @param int $appid The applied preset identifier. + * @param string $name The setting name. + * @param string $oldvalue The setting old value. + * @param string $newvalue The setting new value. + * @param string|null $plugin The setting plugin (or null if none). + * @param string|null $itemname Whether it should be treated as advanced item or not. + * + * @return bool|int true or new id. + */ + private function apply_setting(int $appid, string $name, string $oldvalue, string $newvalue, ?string $plugin = null, + ?string $itemname = null) { + global $DB; + + set_config($name, $newvalue, $plugin); + $configlogid = $this->add_to_config_log($name, $oldvalue, $newvalue, $plugin); + $presetappitem = [ + 'adminpresetapplyid' => $appid, + 'configlogid' => $configlogid, + ]; + $table = 'adminpresets_app_it'; + if (!is_null($itemname)) { + $table = 'adminpresets_app_it_a'; + $presetappitem['itemname'] = $itemname; + } + $appitemid = $DB->insert_record($table, $presetappitem); + + return $appitemid; + + } + + /** + * Helper method to create an applied plugin. + * + * @param int $appid The applied preset identifier. + * @param string $plugin The plugin type. + * @param string $name The plugin name. + * @param int $oldvalue The setting old value. + * @param int $newvalue The setting new value. + * + * @return bool|int true or new id. + */ + private function apply_plugin(int $appid, string $plugin, string $name, int $oldvalue, int $newvalue) { + global $DB; + + // Change plugin visibility. + $pluginclass = \core_plugin_manager::resolve_plugininfo_class($plugin); + $pluginclass::enable_plugin($name, $newvalue); + + // Create entry in applied plugins table. + $presetappplug = [ + 'adminpresetapplyid' => $appid, + 'plugin' => $plugin, + 'name' => $name, + 'value' => $newvalue, + 'oldvalue' => $oldvalue, + ]; + $appplugid = $DB->insert_record('adminpresets_app_plug', $presetappplug); + + return $appplugid; + } + + /** + * Helper method to add entry in config_log. + * + * @param string $name The setting name. + * @param string $oldvalue The setting old value. + * @param string $value The setting new value. + * @param string|null $plugin The setting plugin (or null if the setting doesn't belong to any plugin). + * @return int The id of the config_log entry created. + */ + private function add_to_config_log(string $name, string $oldvalue, string $value, ?string $plugin = null): int { + global $DB, $USER; + + $log = new stdClass(); + $log->userid = $USER->id; + $log->timemodified = time(); + $log->name = $name; + $log->oldvalue = $oldvalue; + $log->value = $value; + $log->plugin = $plugin; + $id = $DB->insert_record('config_log', $log); + + return $id; + } + + /** + * Helper method to access to a protected property. + * + * @param string|object $object The class. + * @param string $property The private/protected property in $object to access. + * @return mixed The current value of the property. + */ + public function access_protected($object, string $property) { + $reflection = new ReflectionClass($object); + $property = $reflection->getProperty($property); + $property->setAccessible(true); + return $property->getValue($object); + } + + + /** + * Given a tree category and setting name, it gets the adminpresets_setting class. + * + * @param string $category Tree category name where the setting is located. + * @param string $settingname Setting name to get the class. + * @return adminpresets_setting + */ + public function get_admin_preset_setting(string $category, string $settingname): adminpresets_setting { + $adminroot = admin_get_root(); + + // Set method accessibility. + $method = new ReflectionMethod(manager::class, 'get_setting'); + $method->setAccessible(true); + + // Get the proper adminpresets_setting instance. + $settingpage = $adminroot->locate($category); + $settingdata = $settingpage->settings->$settingname; + return $method->invokeArgs(new manager(), [$settingdata, '']); + } +} diff --git a/adminpresets/tests/generator_test.php b/adminpresets/tests/generator_test.php new file mode 100644 index 00000000000..ec8ccb8004e --- /dev/null +++ b/adminpresets/tests/generator_test.php @@ -0,0 +1,203 @@ +. + +namespace core_adminpresets; + +/** + * Tests for the data generator. + * + * @package core_adminpresets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass core_adminpresets_generator + */ +class generator_test extends \advanced_testcase { + + /** + * Test the behaviour of create_preset() method. + * + * @covers ::create_preset + * @dataProvider create_preset_provider + * + * @param string|null $name Preset name field. + * @param string|null $comments Preset comments field. + * @param string|null $author Preset author field. + * @param bool $applypreset Whether the preset should be applied or not. + */ + public function test_create_preset(?string $name = null, ?string $comments = null, ?string $author = null, + bool $applypreset = false): void { + global $CFG, $DB; + + $this->resetAfterTest(); + + $data = []; + if (isset($name)) { + $data['name'] = $name; + } else { + // Set the default value used in the generator. + $name = 'Preset default name'; + } + if (isset($comments)) { + // Set the default value used in the generator. + $data['comments'] = $comments; + } else { + // Set the default value used in the generator. + $comments = 'Preset default comment'; + } + if (isset($author)) { + $data['author'] = $author; + } else { + $author = 'Default author'; + } + if ($applypreset) { + $data['applypreset'] = $applypreset; + } + + // Create a preset. + $presetid = $this->getDataGenerator()->get_plugin_generator('core_adminpresets')->create_preset($data); + + // Check the preset data. + $preset = $DB->get_record('adminpresets', ['id' => $presetid]); + + $this->assertEquals($name, $preset->name); + $this->assertEquals($comments, $preset->comments); + $this->assertEquals($author, $preset->author); + $this->assertEquals($CFG->version, $preset->moodleversion); + $this->assertEquals($CFG->release, $preset->moodlerelease); + $this->assertEquals($CFG->wwwroot, $preset->site); + + // Check the settings. + $settings = $DB->get_records('adminpresets_it', ['adminpresetid' => $presetid]); + $this->assertCount(4, $settings); + // These are the settings created in the generator. Check the results match them. + $expectedsettings = [ + 'enablebadges' => 0, + 'allowemojipicker' => 1, + 'mediawidth' => 900, + 'maxanswers' => 2, + ]; + foreach ($settings as $setting) { + $this->assertArrayHasKey($setting->name, $expectedsettings); + $this->assertEquals($expectedsettings[$setting->name], $setting->value); + } + + // Check the advanced settings (should be only one). + $settingsid = array_keys($settings); + list($insql, $inparams) = $DB->get_in_or_equal($settingsid); + $advsettings = $DB->get_records_select('adminpresets_it_a', 'itemid ' . $insql, $inparams); + $this->assertCount(1, $advsettings); + $advsetting = reset($advsettings); + $this->assertEquals('maxanswers_adv', $advsetting->name); + $this->assertEquals(0, $advsetting->value); + + // Check the plugins. + $plugins = $DB->get_records('adminpresets_plug', ['adminpresetid' => $presetid]); + $this->assertCount(3, $plugins); + // These are the plugins created in the generator. Check the results match them. + $expectedplugins = [ + 'enrol' => [ + 'guest' => 0, + ], + 'mod' => [ + 'glossary' => 0, + ], + 'qtype' => [ + 'truefalse' => 1, + ], + ]; + foreach ($plugins as $plugin) { + $this->assertArrayHasKey($plugin->plugin, $expectedplugins); + $this->assertArrayHasKey($plugin->name, $expectedplugins[$plugin->plugin]); + $this->assertEquals($expectedplugins[$plugin->plugin][$plugin->name], $plugin->enabled); + } + + if ($applypreset) { + // Verify that the preset has been applied. + $apps = $DB->get_records('adminpresets_app', ['adminpresetid' => $presetid]); + $this->assertCount(1, $apps); + $app = reset($apps); + + // Check the applied settings. + $appsettings = $DB->get_records('adminpresets_app_it', ['adminpresetapplyid' => $app->id]); + $this->assertCount(3, $appsettings); + // These are the settings created in the generator (all but the allowemojipicker because it hasn't changed). + $expectedappsettings = $expectedsettings; + unset($expectedappsettings['allowemojipicker']); + // Check the results match the expected settings applied. + foreach ($appsettings as $appsetting) { + $configlog = $DB->get_record('config_log', ['id' => $appsetting->configlogid]); + $this->assertArrayHasKey($configlog->name, $expectedappsettings); + $this->assertEquals($expectedappsettings[$configlog->name], $configlog->value); + } + + $appsettings = $DB->get_records('adminpresets_app_it_a', ['adminpresetapplyid' => $app->id]); + $this->assertCount(1, $appsettings); + $appsetting = reset($appsettings); + $configlog = $DB->get_record('config_log', ['id' => $appsetting->configlogid]); + $this->assertEquals('maxanswers_adv', $configlog->name); + $this->assertEquals(0, $configlog->value); + + // Check the applied plugins. + $appplugins = $DB->get_records('adminpresets_app_plug', ['adminpresetapplyid' => $app->id]); + $this->assertCount(2, $appplugins); + // These are the plugins created in the generator (all but the qtype_truefalse because it hasn't changed). + $expectedappplugins = $expectedplugins; + unset($expectedappplugins['qtype']); + // Check the results match the expected plugins applied. + foreach ($appplugins as $appplugin) { + $this->assertArrayHasKey($appplugin->plugin, $expectedappplugins); + $this->assertArrayHasKey($appplugin->name, $expectedappplugins[$appplugin->plugin]); + $this->assertEquals($expectedappplugins[$appplugin->plugin][$appplugin->name], $appplugin->value); + } + } + } + + /** + * Data provider for test_create_preset(). + * + * @return array + */ + public function create_preset_provider(): array { + return [ + 'Default values' => [ + ], + 'Name not empty' => [ + 'name' => 'Preset xaxi name', + ], + 'Comment not empty' => [ + 'name' => null, + 'comments' => 'This is a different comment', + ], + 'Author not empty' => [ + 'name' => null, + 'comments' => null, + 'author' => 'Ada Lovelace', + ], + 'No default values for all the fields' => [ + 'name' => 'Preset with a super-nice name', + 'comments' => 'This is a comment different from the previous one', + 'author' => 'Alejandro Sanz', + ], + 'Apply preset' => [ + 'name' => null, + 'comments' => null, + 'author' => null, + 'applypreset' => true, + ] + ]; + } +} diff --git a/adminpresets/tests/helper_test.php b/adminpresets/tests/helper_test.php new file mode 100644 index 00000000000..81fc4500a9a --- /dev/null +++ b/adminpresets/tests/helper_test.php @@ -0,0 +1,359 @@ +. + +namespace core_adminpresets; + +/** + * Tests for the helper class. + * + * @package core_adminpresets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass helper + */ +class helper_test extends \advanced_testcase { + + /** + * Test the behaviour of create_preset() method. + * + * @covers ::create_preset + * @dataProvider create_preset_provider + * + * @param string|null $name Preset name field. + * @param string|null $comments Preset comments field. + */ + public function test_create_preset(?string $name = null, ?string $comments = null): void { + global $CFG, $DB, $USER; + + $this->resetAfterTest(); + + $data = []; + if (isset($name)) { + $data['name'] = $name; + } + if (isset($comments)) { + $data['comments'] = $comments; + } + + // Create a preset. + $presetid = helper::create_preset($data); + + // Check the preset data. + $preset = $DB->get_record('adminpresets', ['id' => $presetid]); + + $this->assertEquals($name, $preset->name); + $this->assertEquals($comments, $preset->comments); + $this->assertEquals(fullname($USER), $preset->author); + $this->assertEquals($CFG->version, $preset->moodleversion); + $this->assertEquals($CFG->release, $preset->moodlerelease); + $this->assertEquals($CFG->wwwroot, $preset->site); + + // Check the preset is empty and hasn't settings or plugins. + $settings = $DB->get_records('adminpresets_it', ['adminpresetid' => $presetid]); + $this->assertCount(0, $settings); + $plugins = $DB->get_records('adminpresets_plug', ['adminpresetid' => $presetid]); + $this->assertCount(0, $plugins); + } + + /** + * Data provider for test_create_preset(). + * + * @return array + */ + public function create_preset_provider(): array { + return [ + 'Default values' => [ + ], + 'Name not empty' => [ + 'name' => 'Preset xaxi name', + ], + 'Comments not empty' => [ + 'name' => null, + 'comments' => 'This is a different comment', + ], + 'Name and comments not empty' => [ + 'name' => 'Preset with a super-nice name', + 'comments' => 'This is a comment different from the previous one', + ], + ]; + } + + /** + * Test the behaviour of add_item() method. + * + * @covers ::add_item + * @dataProvider add_item_provider + * + * @param string $name Item name. + * @param string $value Item value. + * @param string|null $plugin Item plugin. + * @param string|null $advname If the item is an advanced setting, the name of the advanced setting should be specified here. + * @param string|null $advvalue If the item is an advanced setting, the value of the advanced setting should be specified here. + */ + public function test_add_item(string $name, string $value, ?string $plugin = 'none', ?string $advname = null, + ?string $advvalue = null): void { + global $DB; + + $this->resetAfterTest(); + + // Create a preset. + $presetid = helper::create_preset([]); + $this->assertEquals(1, $DB->count_records('adminpresets', ['id' => $presetid])); + + // Add items. + $itemid = helper::add_item($presetid, $name, $value, $plugin, $advname, $advvalue); + + // Check settings have been created. + $settings = $DB->get_records('adminpresets_it', ['adminpresetid' => $presetid]); + $this->assertCount(1, $settings); + + $setting = reset($settings); + $this->assertEquals($itemid, $setting->id); + $this->assertEquals($name, $setting->name); + $this->assertEquals($value, $setting->value); + $this->assertEquals($plugin, $setting->plugin); + + if ($advname) { + // Check settings have been created. + $advsettings = $DB->get_records('adminpresets_it_a', ['itemid' => $itemid]); + $this->assertCount(1, $advsettings); + + $advsetting = reset($advsettings); + $this->assertEquals($advname, $advsetting->name); + $this->assertEquals($advvalue, $advsetting->value); + } else { + // Check no advanced items have been created. + $this->assertEquals(0, $DB->count_records('adminpresets_it_a', ['itemid' => $itemid])); + } + + // Check no plugins have been created. + $this->assertEquals(0, $DB->count_records('adminpresets_plug', ['adminpresetid' => $presetid])); + } + + /** + * Data provider for test_add_item(). + * + * @return array + */ + public function add_item_provider(): array { + return [ + 'Setting without plugin' => [ + 'name' => 'settingname', + 'value' => 'thisisthevalue', + ], + 'Setting with plugin' => [ + 'name' => 'settingname', + 'value' => 'thisisthevalue', + 'plugin' => 'pluginname', + ], + 'Setting with advanced item' => [ + 'name' => 'settingname', + 'value' => 'thevalue', + 'plugin' => 'pluginname', + 'advname' => 'advsettingname', + 'advvalue' => 'advsettingvalue', + ], + ]; + } + + /** + * Test the behaviour of add_plugin() method. + * + * @covers ::add_plugin + * @dataProvider add_plugin_provider + * + * @param string $type Plugin type. + * @param string $name Plugin name. + * @param mixed $enabled Whether the plugin will be enabled or not. + */ + public function test_add_plugin(string $type, string $name, $enabled = 0): void { + global $DB; + + $this->resetAfterTest(); + + // Create a preset. + $presetid = helper::create_preset([]); + $this->assertEquals(1, $DB->count_records('adminpresets', ['id' => $presetid])); + + // Add plugin. + $pluginid = helper::add_plugin($presetid, $type, $name, $enabled); + + // Check plugin has been created. + $pluggins = $DB->get_records('adminpresets_plug', ['adminpresetid' => $presetid]); + $this->assertCount(1, $pluggins); + + $plugin = reset($pluggins); + $this->assertEquals($pluginid, $plugin->id); + $this->assertEquals($type, $plugin->plugin); + $this->assertEquals($name, $plugin->name); + $this->assertEquals((int) $enabled, $plugin->enabled); + + // Check no settings have been created. + $this->assertEquals(0, $DB->count_records('adminpresets_it', ['adminpresetid' => $presetid])); + } + + /** + * Data provider for test_add_plugin(). + * + * @return array + */ + public function add_plugin_provider(): array { + return [ + 'Plugin: enabled (using int)' => [ + 'type' => 'plugintype', + 'name' => 'pluginname', + 'enabled' => 1, + ], + 'Plugin: enabled (using bool)' => [ + 'type' => 'plugintype', + 'name' => 'pluginname', + 'enabled' => true, + ], + 'Plugin: disabled (using int)' => [ + 'type' => 'plugintype', + 'name' => 'pluginname', + 'enabled' => 0, + ], + 'Plugin: disabled (using bool)' => [ + 'type' => 'plugintype', + 'name' => 'pluginname', + 'enabled' => false, + ], + 'Plugin: negative int value' => [ + 'type' => 'plugintype', + 'name' => 'pluginname', + 'enabled' => -9999, + ], + ]; + } + + /** + * Test the behaviour of change_default_preset() method. + * + * @covers ::change_default_preset + * @dataProvider change_default_preset_provider + * + * @param string $preset The preset name to apply or the path to the XML to be imported and applied. + * @param array|null $settings A few settings to check (with their expected values). + * @param array|null $plugins A few module plugins to check (with their expected values for the visibility). + */ + public function test_change_default_preset(string $preset, ?array $settings = null, ?array $plugins = null): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // We need to change some of the default values; otherwise, the full preset won't be applied, because all the settings + // and plugins are the same. + set_config('enableanalytics', '0'); + + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $generator->create_preset(['name' => 'Preset 1']); + + $presetid = helper::change_default_preset($preset); + + if (empty($settings) && empty($plugins)) { + // The preset hasn't been applied. + $this->assertNull($presetid); + } else { + // The preset has been applied. Check the settings and plugins are the expected. + $this->assertNotEmpty($presetid); + + // Check the setting values have changed accordingly with the ones defined in the preset. + foreach ($settings as $settingname => $settingvalue) { + $this->assertEquals($settingvalue, get_config('core', $settingname)); + } + + // Check the plugins visibility have changed accordingly with the ones defined in the preset. + $enabledplugins = \core\plugininfo\mod::get_enabled_plugins(); + foreach ($plugins as $pluginname => $pluginvalue) { + if ($pluginvalue) { + $this->assertArrayHasKey($pluginname, $enabledplugins); + } else { + $this->assertArrayNotHasKey($pluginname, $enabledplugins); + } + } + } + } + + /** + * Data provider for test_change_default_preset(). + * + * @return array + */ + public function change_default_preset_provider(): array { + return [ + 'Starter preset' => [ + 'preset' => 'starter', + 'settings' => [ + 'enablebadges' => 0, + 'enableportfolios' => 0, + ], + 'plugins' => [ + 'assign' => 1, + 'chat' => 0, + 'data' => 0, + 'lesson' => 0, + ], + ], + 'Full preset' => [ + 'preset' => 'full', + 'settings' => [ + 'enablebadges' => 1, + 'enableportfolios' => 0, + ], + 'plugins' => [ + 'assign' => 1, + 'chat' => 1, + 'data' => 1, + 'lesson' => 1, + ], + ], + 'Preset 1, created manually' => [ + 'preset' => 'Preset 1', + 'settings' => [ + 'enablebadges' => 0, + 'allowemojipicker' => 1, + ], + 'plugins' => [ + 'assign' => 1, + 'glossary' => 0, + ], + ], + 'Unexisting preset name' => [ + 'preset' => 'unexisting', + ], + 'Valid XML file' => [ + 'preset' => __DIR__ . '/fixtures/import_settings_plugins.xml', + 'settings' => [ + 'allowemojipicker' => 1, + 'enableportfolios' => 1, + ], + 'plugins' => [ + 'assign' => 1, + 'chat' => 0, + 'data' => 0, + 'lesson' => 1, + ], + ], + 'Invalid XML file' => [ + 'preset' => __DIR__ . '/fixtures/invalid_xml_file.xml', + ], + 'Unexisting XML file' => [ + 'preset' => __DIR__ . '/fixtures/unexisting.xml', + ], + ]; + } +} diff --git a/adminpresets/tests/local/setting/adminpresets_admin_setting_bloglevel_test.php b/adminpresets/tests/local/setting/adminpresets_admin_setting_bloglevel_test.php new file mode 100644 index 00000000000..d59108abbc5 --- /dev/null +++ b/adminpresets/tests/local/setting/adminpresets_admin_setting_bloglevel_test.php @@ -0,0 +1,92 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Tests for the adminpresets_admin_setting_bloglevel class. + * + * @package core_adminpresets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \core_adminpresets\local\setting\adminpresets_admin_setting_bloglevel + */ +class adminpresets_admin_setting_bloglevel_test extends \advanced_testcase { + + /** + * Test the behaviour of save_value() method. + * + * @covers ::save_value + * @dataProvider save_value_provider + * + * @param int $settingvalue Setting value to be saved. + * @param bool $expectedsaved Whether the setting will be saved or not. + */ + public function test_save_value(int $settingvalue, bool $expectedsaved): void { + global $DB; + + $this->resetAfterTest(); + + // Login as admin, to access all the settings. + $this->setAdminUser(); + + // Set the config values (to confirm they change after applying the preset). + set_config('bloglevel', BLOG_SITE_LEVEL); // All site users can see all blog entries. + + // Get the setting and save the value. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $setting = $generator->get_admin_preset_setting('blog', 'bloglevel'); + $result = $setting->save_value(false, $settingvalue); + + // Check the result is the expected (saved when it has a different value and ignored when the value is the same). + if ($expectedsaved) { + $this->assertCount(1, $DB->get_records('config_log', ['id' => $result])); + // Specific from the save_value in adminpresets_admin_setting_bloglevel. + if ($settingvalue != 0) { + $this->assertTrue((bool) $DB->get_field('block', 'visible', ['name' => 'blog_menu'])); + } else { + $this->assertFalse((bool) $DB->get_field('block', 'visible', ['name' => 'blog_menu'])); + } + } else { + $this->assertFalse($result); + } + $this->assertEquals($settingvalue, get_config('core', 'bloglevel')); + } + + /** + * Data provider for test_save_value(). + * + * @return array + */ + public function save_value_provider(): array { + return [ + 'Save the bloglevel and set blog_menu block visibility to true' => [ + 'setttingvalue' => BLOG_USER_LEVEL, + 'expectedsaved' => true, + ], + 'Same value to bloglevel, so it will not be saved' => [ + 'setttingvalue' => BLOG_SITE_LEVEL, + 'expectedsaved' => false, + ], + 'Save the bloglevel and set blog_menu block visibility to false' => [ + 'setttingvalue' => 0, + 'expectedsaved' => true, + ], + ]; + } + +} diff --git a/adminpresets/tests/local/setting/adminpresets_admin_setting_sitesettext_test.php b/adminpresets/tests/local/setting/adminpresets_admin_setting_sitesettext_test.php new file mode 100644 index 00000000000..3ae332154fa --- /dev/null +++ b/adminpresets/tests/local/setting/adminpresets_admin_setting_sitesettext_test.php @@ -0,0 +1,94 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Tests for the adminpresets_admin_setting_sitesettext class. + * + * @package core_adminpresets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \core_adminpresets\local\setting\adminpresets_admin_setting_sitesettext + */ +class adminpresets_admin_setting_sitesettext_test extends \advanced_testcase { + + /** + * Test the behaviour of save_value() method. + * + * @covers ::save_value + * @dataProvider save_value_provider + * + * @param string $settingname Setting name to save. + * @param string $settingvalue Setting value to be saved. + * @param bool $expectedsaved Whether the setting will be saved or not. + */ + public function test_save_value(string $settingname, string $settingvalue, bool $expectedsaved): void { + global $DB; + + $this->resetAfterTest(); + + // Login as admin, to access all the settings. + $this->setAdminUser(); + + // Get the setting and save the value. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $setting = $generator->get_admin_preset_setting('frontpagesettings', $settingname); + $result = $setting->save_value(false, $settingvalue); + + // Check the result is the expected (saved when it has a different value and ignored when the value is the same). + if ($expectedsaved) { + $this->assertCount(1, $DB->get_records('config_log', ['id' => $result])); + // Specific from the save_value in adminpresets_admin_setting_sitesettext. + $sitecourse = $DB->get_record('course', ['id' => 1]); + $this->assertEquals($settingvalue, $sitecourse->{$settingname}); + } else { + $this->assertFalse($result); + } + } + + /** + * Data provider for test_save_value(). + * + * @return array + */ + public function save_value_provider(): array { + return [ + 'Fullname: different value' => [ + 'settingname' => 'fullname', + 'setttingvalue' => 'New site fullname', + 'expectedsaved' => true, + ], + 'Fullname: same value' => [ + 'settingname' => 'fullname', + 'setttingvalue' => 'PHPUnit test site', + 'expectedsaved' => false, + ], + 'Summary: different value' => [ + 'settingname' => 'summary', + 'setttingvalue' => 'This is a new site summary.', + 'expectedsaved' => true, + ], + 'Summary: same value' => [ + 'settingname' => 'summary', + 'setttingvalue' => '', + 'expectedsaved' => false, + ], + ]; + } + +} diff --git a/adminpresets/tests/local/setting/adminpresets_setting_test.php b/adminpresets/tests/local/setting/adminpresets_setting_test.php new file mode 100644 index 00000000000..3cd81ed5d54 --- /dev/null +++ b/adminpresets/tests/local/setting/adminpresets_setting_test.php @@ -0,0 +1,198 @@ +. + +namespace core_adminpresets\local\setting; + +/** + * Tests for the adminpresets_setting class. + * + * @package core_adminpresets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \core_adminpresets\local\setting\adminpresets_setting + */ +class adminpresets_setting_test extends \advanced_testcase { + + /** + * Test the behaviour of save_value() method. + * + * @covers ::save_value + * @dataProvider save_value_provider + * + * @param string $category Admin tree where the setting belongs. + * @param string $settingplugin Plugin where the setting belongs. + * @param string $settingname Setting name. + * @param string $settingvalue Setting value to be saved. + * @param bool $expectedsaved Whether the setting will be saved or not. + */ + public function test_save_value(string $category, string $settingplugin, string $settingname, string $settingvalue, + bool $expectedsaved): void { + global $DB; + + $this->resetAfterTest(); + + // Login as admin, to access all the settings. + $this->setAdminUser(); + + // Set the config values (to confirm they change after applying the preset). + set_config('enablebadges', 1); + set_config('mediawidth', '640', 'mod_lesson'); + + // The expected setting name in the admin tree is $plugin.$name when plugin is not core. + if ($settingplugin !== 'core') { + $name = $settingplugin . $settingname; + } else { + $name = $settingname; + } + // Get the setting and save the value. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $setting = $generator->get_admin_preset_setting($category, $name); + $result = $setting->save_value(false, $settingvalue); + + // Check the result is the expected (saved when it has a different value and ignored when the value is the same). + if ($expectedsaved) { + $this->assertCount(1, $DB->get_records('config_log', ['id' => $result])); + } else { + $this->assertFalse($result); + } + $this->assertEquals($settingvalue, get_config($settingplugin, $settingname)); + } + + /** + * Data provider for test_save_value(). + * + * @return array + */ + public function save_value_provider(): array { + return [ + 'Core setting with the same value is not saved' => [ + 'category' => 'optionalsubsystems', + 'settingplugin' => 'core', + 'settingname' => 'enablebadges', + 'setttingvalue' => '1', + 'expectedsaved' => false, + ], + 'Core setting with a different value is saved' => [ + 'category' => 'optionalsubsystems', + 'settingplugin' => 'core', + 'settingname' => 'enablebadges', + 'setttingvalue' => '0', + 'expectedsaved' => true, + ], + 'Plugin setting with the same value is not saved' => [ + 'category' => 'modsettinglesson', + 'settingplugin' => 'mod_lesson', + 'settingname' => 'mediawidth', + 'setttingvalue' => '640', + 'expectedsaved' => false, + ], + 'Plugin setting with different value is saved' => [ + 'category' => 'modsettinglesson', + 'settingplugin' => 'mod_lesson', + 'settingname' => 'mediawidth', + 'setttingvalue' => '900', + 'expectedsaved' => true, + ], + ]; + } + + /** + * Test the behaviour of save_attributes_values() method. + * + * @covers ::save_attributes_values + * @dataProvider save_attributes_values_provider + * + * @param string $category Admin tree where the setting belongs. + * @param string $settingplugin Plugin where the setting belongs. + * @param string $settingname Setting name. + * @param string|null $advsettingname Advanced setting name. + * @param string $advsettingvalue Advanced setting value to be saved. + * @param bool $expectedsaved Whether the setting will be saved or not. + */ + public function test_save_attributes_values(string $category, string $settingplugin, string $settingname, + ?string $advsettingname, string $advsettingvalue, bool $expectedsaved): void { + global $DB; + + $this->resetAfterTest(); + + // Login as admin, to access all the settings. + $this->setAdminUser(); + + // Set the config values (to confirm they change after applying the preset). + set_config('maxanswers_adv', '1', 'mod_lesson'); + + // The expected setting name in the admin tree is $plugin.$name when plugin is not core. + if ($settingplugin !== 'core') { + $name = $settingplugin . $settingname; + } else { + $name = $settingname; + } + // Get the setting and save the value. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $setting = $generator->get_admin_preset_setting($category, $name); + if ($advsettingname) { + $setting->set_attribute_value($advsettingname, $advsettingvalue); + } + $result = $setting->save_attributes_values(); + + // Check the result is the expected (saved when it has a different value and ignored when the value is the same). + if ($expectedsaved) { + $this->assertCount(1, $result); + $configlog = reset($result); + $this->assertCount(1, $DB->get_records('config_log', ['id' => $configlog])); + } else { + $this->assertFalse($result); + } + if ($advsettingname) { + $this->assertEquals($advsettingvalue, get_config($settingplugin, $advsettingname)); + } + } + + /** + * Data provider for test_save_attributes_values(). + * + * @return array + */ + public function save_attributes_values_provider(): array { + return [ + 'Plugin setting with the same value is not saved' => [ + 'category' => 'modsettinglesson', + 'settingplugin' => 'mod_lesson', + 'settingname' => 'maxanswers', + 'advsettingname' => 'maxanswers_adv', + 'advsetttingvalue' => '1', + 'expectedsaved' => false, + ], + 'Plugin setting with different value is saved' => [ + 'category' => 'modsettinglesson', + 'settingplugin' => 'mod_lesson', + 'settingname' => 'maxanswers', + 'advsettingname' => 'maxanswers_adv', + 'advsetttingvalue' => '0', + 'expectedsaved' => true, + ], + 'Plugin setting without advanced attributes are not saved' => [ + 'category' => 'modsettinglesson', + 'settingplugin' => 'mod_lesson', + 'settingname' => 'maxanswers', + 'advsettingname' => null, + 'advsetttingvalue' => '0', + 'expectedsaved' => false, + ], + ]; + } +} diff --git a/adminpresets/tests/manager_test.php b/adminpresets/tests/manager_test.php new file mode 100644 index 00000000000..6fef8543047 --- /dev/null +++ b/adminpresets/tests/manager_test.php @@ -0,0 +1,771 @@ +. + +namespace core_adminpresets; + +use stdClass; + +/** + * Tests for the manager class. + * + * @package core_adminpresets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \core_adminpresets\manager + */ +class manager_test extends \advanced_testcase { + /** + * Test the behaviour of protected get_site_settings method. + * + * @covers ::get_site_settings + * @covers ::get_settings + */ + public function test_manager_get_site_settings(): void { + global $DB; + + $this->resetAfterTest(); + + // Login as admin, to access all the settings. + $this->setAdminUser(); + + $manager = new manager(); + $result = $manager->get_site_settings(); + + // Check fullname is set into the none category. + $this->assertInstanceOf( + '\core_adminpresets\local\setting\adminpresets_admin_setting_sitesettext', + $result['none']['fullname'] + ); + $this->assertEquals('PHPUnit test site', $result['none']['fullname']->get_value()); + + // Check some of the config setting is present (they should be stored in the "none" category). + $this->assertInstanceOf( + '\core_adminpresets\local\setting\adminpresets_admin_setting_configcheckbox', + $result['none']['enablecompletion'] + ); + $this->assertEquals(1, $result['none']['enablecompletion']->get_value()); + + // Check some of the plugin config settings is present. + $this->assertInstanceOf( + '\core_adminpresets\local\setting\adminpresets_admin_setting_configtext', + $result['folder']['maxsizetodownload'] + ); + $this->assertEquals(0, $result['folder']['maxsizetodownload']->get_value()); + + // Set some of these values. + $sitecourse = new stdClass(); + $sitecourse->id = 1; + $sitecourse->fullname = 'New site fullname'; + $DB->update_record('course', $sitecourse); + + set_config('enablecompletion', 0); + set_config('maxsizetodownload', 101, 'folder'); + + // Check the new values are returned properly. + $result = $manager->get_site_settings(); + // Site fullname. + $this->assertInstanceOf( + '\core_adminpresets\local\setting\adminpresets_admin_setting_sitesettext', + $result['none']['fullname'] + ); + $this->assertEquals($sitecourse->fullname, $result['none']['fullname']->get_value()); + // Config setting. + $this->assertInstanceOf( + '\core_adminpresets\local\setting\adminpresets_admin_setting_configcheckbox', + $result['none']['enablecompletion'] + ); + $this->assertEquals(0, $result['none']['enablecompletion']->get_value()); + // Plugin config settting. + $this->assertInstanceOf( + '\core_adminpresets\local\setting\adminpresets_admin_setting_configtext', + $result['folder']['maxsizetodownload'] + ); + $this->assertEquals(101, $result['folder']['maxsizetodownload']->get_value()); + } + + /** + * Test the behaviour of protected get_setting method. + * + * @covers ::get_setting + * @covers ::get_settings_class + */ + public function test_manager_get_setting(): void { + $this->resetAfterTest(); + + // Login as admin, to access all the settings. + $this->setAdminUser(); + + $adminroot = admin_get_root(); + + // Check the adminpresets_xxxxx class is created properly when it exists. + $settingpage = $adminroot->locate('optionalsubsystems'); + $settingdata = $settingpage->settings->enablebadges; + $manager = new manager(); + $result = $manager->get_setting($settingdata, ''); + $this->assertInstanceOf('\core_adminpresets\local\setting\adminpresets_admin_setting_configcheckbox', $result); + $this->assertNotEquals('core_adminpresets\local\setting\adminpresets_setting', get_class($result)); + + // Check the mapped class is returned when no specific class exists and it exists in the mappings array. + $settingpage = $adminroot->locate('h5psettings'); + $settingdata = $settingpage->settings->h5plibraryhandler;; + $result = $manager->get_setting($settingdata, ''); + $this->assertInstanceOf('\core_adminpresets\local\setting\adminpresets_admin_setting_configselect', $result); + $this->assertNotEquals( + 'core_adminpresets\local\setting\adminpresets_admin_settings_h5plib_handler_select', + get_class($result) + ); + + // Check the mapped class is returned when no specific class exists and it exists in the mappings array. + $settingpage = $adminroot->locate('modsettingquiz'); + $settingdata = $settingpage->settings->quizbrowsersecurity;; + $result = $manager->get_setting($settingdata, ''); + $this->assertInstanceOf('\mod_quiz\adminpresets\adminpresets_mod_quiz_admin_setting_browsersecurity', $result); + $this->assertNotEquals('core_adminpresets\local\setting\adminpresets_setting', get_class($result)); + + // Check the adminpresets_setting class is returned when no specific class exists. + $settingpage = $adminroot->locate('managecustomfields'); + $settingdata = $settingpage->settings->customfieldsui;; + $result = $manager->get_setting($settingdata, ''); + $this->assertInstanceOf('\core_adminpresets\local\setting\adminpresets_setting', $result); + $this->assertEquals('core_adminpresets\local\setting\adminpresets_setting', get_class($result)); + } + + /** + * Test the behaviour of apply_preset() method when the given presetid doesn't exist. + * + * @covers ::apply_preset + */ + public function test_apply_preset_unexisting_preset(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create some presets. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $presetid = $generator->create_preset(); + + // Unexisting preset identifier. + $unexistingid = $presetid * 2; + + $manager = new manager(); + $this->expectException(\moodle_exception::class); + $manager->apply_preset($unexistingid); + } + + /** + * Test the behaviour of apply_preset() method. + * + * @covers ::apply_preset + */ + public function test_apply_preset(): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $presetid = $generator->create_preset(); + + $currentpresets = $DB->count_records('adminpresets'); + $currentitems = $DB->count_records('adminpresets_it'); + $currentadvitems = $DB->count_records('adminpresets_it_a'); + $currentplugins = $DB->count_records('adminpresets_plug'); + $currentapppresets = $DB->count_records('adminpresets_app'); + $currentappitems = $DB->count_records('adminpresets_app_it'); + $currentappadvitems = $DB->count_records('adminpresets_app_it_a'); + $currentappplugins = $DB->count_records('adminpresets_app_plug'); + + // Set the config values (to confirm they change after applying the preset). + set_config('enablebadges', 1); + set_config('allowemojipicker', 1); + set_config('mediawidth', '640', 'mod_lesson'); + set_config('maxanswers', '5', 'mod_lesson'); + set_config('maxanswers_adv', '1', 'mod_lesson'); + set_config('enablecompletion', 1); + set_config('usecomments', 0); + + // Call the apply_preset method. + $manager = new manager(); + $manager->apply_preset($presetid); + + // Check the preset applied has been added to database. + $this->assertCount($currentapppresets + 1, $DB->get_records('adminpresets_app')); + // Applied items: enablebadges@none, mediawitdh@mod_lesson and maxanswers@@mod_lesson. + $this->assertCount($currentappitems + 3, $DB->get_records('adminpresets_app_it')); + // Applied advanced items: maxanswers_adv@mod_lesson. + $this->assertCount($currentappadvitems + 1, $DB->get_records('adminpresets_app_it_a')); + // Applied plugins: enrol_guest and mod_glossary. + $this->assertCount($currentappplugins + 2, $DB->get_records('adminpresets_app_plug')); + // Check no new preset has been created. + $this->assertCount($currentpresets, $DB->get_records('adminpresets')); + $this->assertCount($currentitems, $DB->get_records('adminpresets_it')); + $this->assertCount($currentadvitems, $DB->get_records('adminpresets_it_a')); + $this->assertCount($currentplugins, $DB->get_records('adminpresets_plug')); + + // Check the setting values have changed accordingly with the ones defined in the preset. + $this->assertEquals(0, get_config('core', 'enablebadges')); + $this->assertEquals(900, get_config('mod_lesson', 'mediawidth')); + $this->assertEquals(2, get_config('mod_lesson', 'maxanswers')); + $this->assertEquals(0, get_config('mod_lesson', 'maxanswers_adv')); + + // These settings will never change. + $this->assertEquals(1, get_config('core', 'allowemojipicker')); + $this->assertEquals(1, get_config('core', 'enablecompletion')); + $this->assertEquals(0, get_config('core', 'usecomments')); + + // Check the plugins visibility have changed accordingly with the ones defined in the preset. + $enabledplugins = \core\plugininfo\enrol::get_enabled_plugins(); + $this->assertArrayNotHasKey('guest', $enabledplugins); + $this->assertArrayHasKey('manual', $enabledplugins); + $enabledplugins = \core\plugininfo\mod::get_enabled_plugins(); + $this->assertArrayNotHasKey('glossary', $enabledplugins); + $this->assertArrayHasKey('assign', $enabledplugins); + $enabledplugins = \core\plugininfo\qtype::get_enabled_plugins(); + $this->assertArrayHasKey('truefalse', $enabledplugins); + + // Check the presetid has been also stored in the lastpresetapplied config setting. + $this->assertEquals($presetid, get_config('tool_admin_presets', 'lastpresetapplied')); + + // Call apply_preset as a simulation, so it shouldn't be applied and lastpresetapplied should still be $presetid. + $presetid2 = $generator->create_preset(); + $manager->apply_preset($presetid2, true); + $this->assertEquals($presetid, get_config('tool_admin_presets', 'lastpresetapplied')); + } + + + /** + * Test the behaviour of export_preset() method. + * + * @covers ::export_preset + * @dataProvider export_preset_provider + * + * @param bool $includesensible Whether the sensible settings should be exported too or not. + * @param string $presetname Preset name. + */ + public function test_export_preset(bool $includesensible = false, string $presetname = 'Export 1'): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Get current presets and items. + $currentpresets = $DB->count_records('adminpresets'); + $currentadvitems = $DB->count_records('adminpresets_it_a'); + + // Initialise some settings (to compare their values have been exported as expected). + set_config('recaptchapublickey', 'abcde'); + set_config('enablebadges', '0'); + set_config('mediawidth', '900', 'mod_lesson'); + set_config('maxanswers', '2', 'mod_lesson'); + set_config('maxanswers_adv', '0', 'mod_lesson'); + set_config('defaultfeedback', '0', 'mod_lesson'); + set_config('defaultfeedback_adv', '1', 'mod_lesson'); + + // Prepare the data to export preset. + $data = [ + 'name' => $presetname, + 'comments' => ['text' => 'This is a presets for testing export'], + 'author' => 'Super-Girl', + 'includesensiblesettings' => $includesensible, + ]; + + // Call the method to be tested. + $manager = new manager(); + list($presetid, $settingsfound, $pluginsfound) = $manager->export_preset((object) $data); + + // Check the preset record has been created. + $presets = $DB->get_records('adminpresets'); + $this->assertCount($currentpresets + 1, $presets); + $this->assertArrayHasKey($presetid, $presets); + $preset = $presets[$presetid]; + $this->assertEquals($presetname, $preset->name); + $this->assertEquals(0, $preset->iscore); + + // Check the preset includes settings and plugins. + $this->assertTrue($settingsfound); + $this->assertTrue($pluginsfound); + + // Check the items, advanced attributes and plugins have been created. + $this->assertGreaterThan(0, $DB->count_records('adminpresets_it', ['adminpresetid' => $presetid])); + $this->assertGreaterThan($currentadvitems, $DB->count_records('adminpresets_it_a')); + $this->assertGreaterThan(0, $DB->count_records('adminpresets_plug', ['adminpresetid' => $presetid])); + + // Check settings have been created with the expected values. + $params = ['adminpresetid' => $presetid, 'plugin' => 'none', 'name' => 'enablebadges']; + $setting = $DB->get_record('adminpresets_it', $params); + $this->assertEquals('0', $setting->value); + + $params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'mediawidth']; + $setting = $DB->get_record('adminpresets_it', $params); + $this->assertEquals('900', $setting->value); + + $params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'maxanswers']; + $setting = $DB->get_record('adminpresets_it', $params); + $this->assertEquals('2', $setting->value); + $params = ['itemid' => $setting->id, 'name' => 'maxanswers_adv']; + $setting = $DB->get_record('adminpresets_it_a', $params); + $this->assertEquals('0', $setting->value); + + $params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'defaultfeedback']; + $setting = $DB->get_record('adminpresets_it', $params); + $this->assertEquals('0', $setting->value); + $params = ['itemid' => $setting->id, 'name' => 'defaultfeedback_adv']; + $setting = $DB->get_record('adminpresets_it_a', $params); + $this->assertEquals('1', $setting->value); + + // Check plugins have been created with the expected values. + $manager = \core_plugin_manager::instance(); + $plugintype = 'enrol'; + $plugins = $manager->get_present_plugins($plugintype); + $enabledplugins = $manager->get_enabled_plugins($plugintype); + foreach ($plugins as $pluginname => $unused) { + $params = ['adminpresetid' => $presetid, 'plugin' => $plugintype, 'name' => $pluginname]; + $plugin = $DB->get_record('adminpresets_plug', $params); + $enabled = (!empty($enabledplugins) && array_key_exists($pluginname, $enabledplugins)); + $this->assertEquals($enabled, (bool) $plugin->enabled); + } + + // Check whether sensible settings have been exported or not. + $params = ['adminpresetid' => $presetid, 'plugin' => 'none', 'name' => 'recaptchapublickey']; + $recaptchasetting = $DB->get_record('adminpresets_it', $params); + $params = ['adminpresetid' => $presetid, 'plugin' => 'none', 'name' => 'cronremotepassword']; + $cronsetting = $DB->get_record('adminpresets_it', $params); + if ($includesensible) { + $this->assertEquals('abcde', $recaptchasetting->value); + $this->assertNotFalse($cronsetting); + } else { + $this->assertFalse($recaptchasetting); + $this->assertFalse($cronsetting); + } + } + + /** + * Data provider for test_export_preset(). + * + * @return array + */ + public function export_preset_provider(): array { + return [ + 'Export settings and plugins, excluding sensible' => [ + 'includesensible' => false, + ], + 'Export settings and plugins, including sensible' => [ + 'includesensible' => true, + ], + 'Export settings and plugins, with Starter name (it should not be marked as core)' => [ + 'includesensible' => false, + 'presetname' => 'Starter', + ], + 'Export settings and plugins, with Full name (it should not be marked as core)' => [ + 'includesensible' => false, + 'presetname' => 'Full', + ], + ]; + } + + /** + * Test the behaviour of download_preset() method, when the given presetid doesn't exist. + * + * @covers ::download_preset + */ + public function test_download_unexisting_preset(): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create some presets. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $presetid = $generator->create_preset(); + + // Unexisting preset identifier. + $unexistingid = $presetid * 2; + + $manager = new manager(); + $this->expectException(\moodle_exception::class); + $manager->download_preset($unexistingid); + } + + + /** + * Test the behaviour of import_preset() method. + * + * @dataProvider import_preset_provider + * @covers ::import_preset + * + * @param string $filecontents File content to import. + * @param bool $expectedpreset Whether the preset should be created or not. + * @param bool $expectedsettings Whether settings will be created or not. + * @param bool $expectedplugins Whether plugins will be created or not. + * @param bool $expecteddebugging Whether debugging message will be thrown or not. + * @param string|null $expectedexception Expected exception class (if that's the case). + * @param string|null $expectedpresetname Expected preset name. + */ + public function test_import_preset(string $filecontents, bool $expectedpreset, bool $expectedsettings = false, + bool $expectedplugins = false, bool $expecteddebugging = false, string $expectedexception = null, + string $expectedpresetname = 'Imported preset'): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + $currentpresets = $DB->count_records('adminpresets'); + $currentitems = $DB->count_records('adminpresets_it'); + $currentadvitems = $DB->count_records('adminpresets_it_a'); + + // Call the method to be tested. + $manager = new manager(); + try { + list($xml, $preset, $settingsfound, $pluginsfound) = $manager->import_preset($filecontents); + } catch (\exception $e) { + if ($expectedexception) { + $this->assertInstanceOf($expectedexception, $e); + } + } finally { + if ($expecteddebugging) { + $this->assertDebuggingCalled(); + } + + if ($expectedpreset) { + // Check the preset record has been created. + $presets = $DB->get_records('adminpresets'); + $this->assertCount($currentpresets + 1, $presets); + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $this->assertArrayHasKey($preset->id, $presets); + $preset = $presets[$preset->id]; + $this->assertEquals($expectedpresetname, $preset->name); + $this->assertEquals('http://demo.moodle', $preset->site); + $this->assertEquals('Ada Lovelace', $preset->author); + $this->assertEquals(0, $preset->iscore); + + if ($expectedsettings) { + // Check the items have been created. + $items = $DB->get_records('adminpresets_it', ['adminpresetid' => $preset->id]); + $this->assertCount(4, $items); + $presetitems = [ + 'none' => [ + 'enablebadges' => 0, + 'enableportfolios' => 1, + 'allowemojipicker' => 1, + ], + 'mod_lesson' => [ + 'mediawidth' => 900, + 'maxanswers' => 2, + ], + ]; + foreach ($items as $item) { + $this->assertArrayHasKey($item->name, $presetitems[$item->plugin]); + $this->assertEquals($presetitems[$item->plugin][$item->name], $item->value); + } + + // Check the advanced attributes have been created. + $advitems = $DB->get_records('adminpresets_it_a'); + $this->assertCount($currentadvitems + 1, $advitems); + $advitemfound = false; + foreach ($advitems as $advitem) { + if ($advitem->name == 'maxanswers_adv') { + $this->assertEmpty($advitem->value); + $advitemfound = true; + } + } + $this->assertTrue($advitemfound); + } + + if ($expectedplugins) { + // Check the plugins have been created. + $plugins = $DB->get_records('adminpresets_plug', ['adminpresetid' => $preset->id]); + $this->assertCount(6, $plugins); + $presetplugins = [ + 'atto' => [ + 'html' => 1, + ], + 'block' => [ + 'html' => 0, + 'activity_modules' => 1, + ], + 'mod' => [ + 'chat' => 0, + 'data' => 0, + 'lesson' => 1, + ], + ]; + foreach ($plugins as $plugin) { + $this->assertArrayHasKey($plugin->name, $presetplugins[$plugin->plugin]); + $this->assertEquals($presetplugins[$plugin->plugin][$plugin->name], $plugin->enabled); + } + + } + } else { + // Check the preset nor the items are not created. + $this->assertCount($currentpresets, $DB->get_records('adminpresets')); + $this->assertCount($currentitems, $DB->get_records('adminpresets_it')); + $this->assertCount($currentadvitems, $DB->get_records('adminpresets_it_a')); + } + } + } + + /** + * Data provider for test_import_preset(). + * + * @return array + */ + public function import_preset_provider(): array { + return [ + 'Import settings from an empty file' => [ + 'filecontents' => '', + 'expectedpreset' => false, + ], + 'Import settings and plugins from a valid XML file' => [ + 'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_settings_plugins.xml'), + 'expectedpreset' => true, + 'expectedsettings' => true, + 'expectedplugins' => true, + ], + 'Import only settings from a valid XML file' => [ + 'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_settings.xml'), + 'expectedpreset' => true, + 'expectedsettings' => true, + 'expectedplugins' => false, + ], + 'Import settings and plugins from a valid XML file with Starter name, which will be marked as non-core' => [ + 'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_starter_name.xml'), + 'expectedpreset' => true, + 'expectedsettings' => true, + 'expectedplugins' => true, + 'expecteddebugging' => false, + 'expectedexception' => null, + 'expectedpresetname' => 'Starter', + ], + 'Import settings from an invalid XML file' => [ + 'filecontents' => file_get_contents(__DIR__ . '/fixtures/invalid_xml_file.xml'), + 'expectedpreset' => false, + 'expectedsettings' => false, + 'expectedplugins' => false, + 'expecteddebugging' => false, + 'expectedexception' => \Exception::class, + ], + 'Import unexisting settings category' => [ + 'filecontents' => file_get_contents(__DIR__ . '/fixtures/unexisting_category.xml'), + 'expectedpreset' => false, + 'expectedsettings' => false, + 'expectedplugins' => false, + ], + 'Import unexisting setting' => [ + 'filecontents' => file_get_contents(__DIR__ . '/fixtures/unexisting_setting.xml'), + 'expectedpreset' => false, + 'expectedsettings' => false, + 'expectedplugins' => false, + 'expecteddebugging' => true, + ], + 'Import valid settings with one unexisting setting too' => [ + 'filecontents' => file_get_contents(__DIR__ . '/fixtures/import_settings_with_unexisting_setting.xml'), + 'expectedpreset' => true, + 'expectedsettings' => false, + 'expectedplugins' => false, + 'expecteddebugging' => true, + ], + ]; + } + + + /** + * Test the behaviour of delete_preset() method when the preset id doesn't exist. + * + * @covers ::delete_preset + */ + public function test_delete_preset_unexisting_preset(): void { + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create some presets. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $presetid = $generator->create_preset(['name' => 'Preset 1']); + + // Unexisting preset identifier. + $unexistingid = $presetid * 2; + + $manager = new manager(); + + $this->expectException(\moodle_exception::class); + $manager->delete_preset($unexistingid); + } + + /** + * Test the behaviour of delete_preset() method. + * + * @covers ::delete_preset + */ + public function test_delete_preset(): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create some presets. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $presetid1 = $generator->create_preset(['name' => 'Preset 1', 'applypreset' => true]); + $presetid2 = $generator->create_preset(['name' => 'Preset 2']); + + $currentpresets = $DB->count_records('adminpresets'); + $currentitems = $DB->count_records('adminpresets_it'); + $currentadvitems = $DB->count_records('adminpresets_it_a'); + $currentplugins = $DB->count_records('adminpresets_plug'); + + // Only preset1 has been applied. + $this->assertCount(1, $DB->get_records('adminpresets_app')); + // Only the preset1 settings that have changed: enablebadges, mediawidth and maxanswers. + $this->assertCount(3, $DB->get_records('adminpresets_app_it')); + // Only the preset1 advanced settings that have changed: maxanswers_adv. + $this->assertCount(1, $DB->get_records('adminpresets_app_it_a')); + // Only the preset1 plugins that have changed: enrol_guest and mod_glossary. + $this->assertCount(2, $DB->get_records('adminpresets_app_plug')); + + // Call the method to be tested. + $manager = new manager(); + $manager->delete_preset($presetid1); + + // Check the preset data has been removed. + $presets = $DB->get_records('adminpresets'); + $this->assertCount($currentpresets - 1, $presets); + $preset = reset($presets); + $this->assertArrayHasKey($presetid2, $presets); + // Check preset items. + $this->assertCount($currentitems - 4, $DB->get_records('adminpresets_it')); + $this->assertCount(0, $DB->get_records('adminpresets_it', ['adminpresetid' => $presetid1])); + // Check preset advanced items. + $this->assertCount($currentadvitems - 1, $DB->get_records('adminpresets_it_a')); + // Check preset plugins. + $this->assertCount($currentplugins - 3, $DB->get_records('adminpresets_plug')); + $this->assertCount(0, $DB->get_records('adminpresets_plug', ['adminpresetid' => $presetid1])); + // Check preset applied tables are empty now. + $this->assertCount(0, $DB->get_records('adminpresets_app')); + $this->assertCount(0, $DB->get_records('adminpresets_app_it')); + $this->assertCount(0, $DB->get_records('adminpresets_app_it_a')); + $this->assertCount(0, $DB->get_records('adminpresets_app_plug')); + } + + /** + * Test the behaviour of revert_preset() method when the preset applied id doesn't exist. + * + * @covers ::revert_preset + */ + public function test_revert_preset_unexisting_presetapp(): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset and apply it. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $presetid = $generator->create_preset(['applypreset' => true]); + $presetappid = $DB->get_field('adminpresets_app', 'id', ['adminpresetid' => $presetid]); + + // Unexisting applied preset identifier. + $unexistingid = $presetappid * 2; + + $manager = new manager(); + $this->expectException(\moodle_exception::class); + $manager->revert_preset($unexistingid); + } + + /** + * Test the behaviour of revert_preset() method. + * + * @covers ::revert_preset + */ + public function test_revert_preset(): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Set the config values (to confirm they change after applying the preset). + set_config('enablebadges', 1); + set_config('allowemojipicker', 1); + set_config('mediawidth', '640', 'mod_lesson'); + set_config('maxanswers', '5', 'mod_lesson'); + set_config('maxanswers_adv', '1', 'mod_lesson'); + set_config('enablecompletion', 1); + set_config('usecomments', 0); + + // Create a preset and apply it. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $presetid = $generator->create_preset(['applypreset' => true]); + $presetappid = $DB->get_field('adminpresets_app', 'id', ['adminpresetid' => $presetid]); + + $currentpresets = $DB->count_records('adminpresets'); + $currentitems = $DB->count_records('adminpresets_it'); + $currentadvitems = $DB->count_records('adminpresets_it_a'); + $currentplugins = $DB->count_records('adminpresets_plug'); + $this->assertCount(1, $DB->get_records('adminpresets_app')); + $this->assertCount(3, $DB->get_records('adminpresets_app_it')); + $this->assertCount(1, $DB->get_records('adminpresets_app_it_a')); + $this->assertCount(2, $DB->get_records('adminpresets_app_plug')); + + // Check the setttings have changed accordingly after applying the preset. + $this->assertEquals(0, get_config('core', 'enablebadges')); + $this->assertEquals(900, get_config('mod_lesson', 'mediawidth')); + $this->assertEquals(2, get_config('mod_lesson', 'maxanswers')); + $this->assertEquals(1, get_config('core', 'allowemojipicker')); + $this->assertEquals(1, get_config('core', 'enablecompletion')); + $this->assertEquals(0, get_config('core', 'usecomments')); + + // Check the plugins visibility have changed accordingly with the ones defined in the preset. + $enabledplugins = \core\plugininfo\enrol::get_enabled_plugins(); + $this->assertArrayNotHasKey('guest', $enabledplugins); + $enabledplugins = \core\plugininfo\mod::get_enabled_plugins(); + $this->assertArrayNotHasKey('glossary', $enabledplugins); + $enabledplugins = \core\plugininfo\qtype::get_enabled_plugins(); + $this->assertArrayHasKey('truefalse', $enabledplugins); + + // Call the method to be tested. + $manager = new manager(); + list($presetapp, $rollback, $failures) = $manager->revert_preset($presetappid); + + // Check the preset applied has been reverted (so the records in _appXX tables have been removed). + $this->assertNotEmpty($presetapp); + $this->assertNotEmpty($rollback); + $this->assertEmpty($failures); + $this->assertCount(0, $DB->get_records('adminpresets_app')); + $this->assertCount(0, $DB->get_records('adminpresets_app_it')); + $this->assertCount(0, $DB->get_records('adminpresets_app_it_a')); + $this->assertCount(0, $DB->get_records('adminpresets_app_plug')); + // Check the preset data hasn't changed. + $this->assertCount($currentpresets, $DB->get_records('adminpresets')); + $this->assertCount($currentitems, $DB->get_records('adminpresets_it')); + $this->assertCount($currentadvitems, $DB->get_records('adminpresets_it_a')); + $this->assertCount($currentplugins, $DB->get_records('adminpresets_plug')); + + // Check the setting values have been reverted accordingly. + $this->assertEquals(1, get_config('core', 'enablebadges')); + $this->assertEquals(640, get_config('mod_lesson', 'mediawidth')); + $this->assertEquals(5, get_config('mod_lesson', 'maxanswers')); + $this->assertEquals(1, get_config('mod_lesson', 'maxanswers_adv')); + // These settings won't change, regardless if they are posted to the form. + $this->assertEquals(1, get_config('core', 'allowemojipicker')); + $this->assertEquals(1, get_config('core', 'enablecompletion')); + $this->assertEquals(0, get_config('core', 'usecomments')); + + // Check the plugins visibility have been reverted accordingly. + $enabledplugins = \core\plugininfo\enrol::get_enabled_plugins(); + $this->assertArrayHasKey('guest', $enabledplugins); + $enabledplugins = \core\plugininfo\mod::get_enabled_plugins(); + $this->assertArrayHasKey('glossary', $enabledplugins); + // This plugin won't change (because it had the same value than before the preset was applied). + $enabledplugins = \core\plugininfo\qtype::get_enabled_plugins(); + $this->assertArrayHasKey('truefalse', $enabledplugins); + } +} diff --git a/adminpresets/tests/privacy/privacy_provider_test.php b/adminpresets/tests/privacy/privacy_provider_test.php new file mode 100644 index 00000000000..fb2bfbdc9c0 --- /dev/null +++ b/adminpresets/tests/privacy/privacy_provider_test.php @@ -0,0 +1,170 @@ +. + +namespace core_adminpresets\privacy; + +use context_system; +use context_user; +use core_privacy\local\metadata\collection; +use core_privacy\tests\provider_testcase; + +/** + * Tests for the privacy provider class. + * + * @package core_adminpresets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \core_adminpresets\privacy\provider + */ +class privacy_provider_test extends provider_testcase { + + /** + * Test for provider::get_metadata(). + * @covers ::get_metadata + */ + public function test_get_metadata() { + $collection = new collection('core_adminpresets'); + $newcollection = provider::get_metadata($collection); + $itemcollection = $newcollection->get_collection(); + $this->assertCount(2, $itemcollection); + + // The expected metadata fields are covered by test_metadata_provider() in privacy/tests/provider_test.php. + } + + /** + * Test for provider::get_contexts_for_userid() doesn't return any context. + * @covers ::get_contexts_for_userid + */ + public function test_get_contexts_for_userid() { + global $USER; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $generator->create_preset(); + + $contextlist = provider::get_contexts_for_userid($USER->id); + $this->assertEmpty($contextlist); + } + + /** + * Test for provider::get_users_in_context() doesn't return any user. + * @covers ::get_users_in_context + */ + public function test_get_users_in_context() { + global $USER; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $generator->create_preset(); + + $usercontext = context_user::instance($USER->id); + $userlist = new \core_privacy\local\request\userlist($usercontext, 'core_adminpresets'); + \core_message\privacy\provider::get_users_in_context($userlist); + $this->assertEmpty($userlist->get_userids()); + } + + + /** + * Test for provider::export_user_data(). + * @covers ::export_user_data + */ + public function test_export_user_data() { + global $USER; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $generator->create_preset(); + + // Check data is not exported in user context. + $usercontext = context_user::instance($USER->id); + $this->export_context_data_for_user($USER->id, $usercontext, 'core_adminpresets'); + $writer = \core_privacy\local\request\writer::with_context($usercontext); + + $this->assertEmpty($writer->get_data([get_string('siteadminpresetspluginname', 'core_adminpresets')])); + $this->assertEmpty($writer->get_all_metadata([])); + $this->assertEmpty($writer->get_files([])); + + // Check data is not exported in system context either. + $systemcontext = context_system::instance(); + $this->export_context_data_for_user($USER->id, $systemcontext, 'core_adminpresets'); + $writer = \core_privacy\local\request\writer::with_context($systemcontext); + + $this->assertEmpty($writer->get_data([get_string('siteadminpresetspluginname', 'core_adminpresets')])); + $this->assertEmpty($writer->get_all_metadata([])); + $this->assertEmpty($writer->get_files([])); + } + + /** + * Test for provider::delete_data_for_all_users_in_context(). + * @covers ::delete_data_for_all_users_in_context + */ + public function test_delete_data_for_all_users_in_context() { + global $DB, $USER; + + $this->resetAfterTest(); + $this->setAdminUser(); + + $currentpresets = $DB->count_records('adminpresets'); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $generator->create_preset(); + $this->assertEquals($currentpresets + 1, $DB->count_records('adminpresets')); + + $usercontext = context_user::instance($USER->id); + + provider::delete_data_for_all_users_in_context($usercontext); + + // Confirm the presets haven't been removed. + $this->assertEquals($currentpresets + 1, $DB->count_records('adminpresets')); + } + + /** + * Test for provider::delete_data_for_user(). + * @covers ::delete_data_for_user + */ + public function test_delete_data_for_user() { + global $DB, $USER; + + $this->resetAfterTest(); + $this->setAdminUser(); + + $currentpresets = $DB->count_records('adminpresets'); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets'); + $generator->create_preset(); + $this->assertEquals($currentpresets + 1, $DB->count_records('adminpresets')); + + $usercontext = context_user::instance($USER->id); + $contextlist = new \core_privacy\local\request\approved_contextlist($USER, 'core_adminpresets', [$usercontext->id]); + provider::delete_data_for_user($contextlist); + + // Confirm the presets haven't been removed. + $this->assertEquals($currentpresets + 1, $DB->count_records('adminpresets')); + } + +} diff --git a/lang/en/adminpresets.php b/lang/en/adminpresets.php new file mode 100644 index 00000000000..1dacabc0b19 --- /dev/null +++ b/lang/en/adminpresets.php @@ -0,0 +1,53 @@ +. + +/** + * Core admin presets component to load some settings/plugins. + * + * @package core_adminpresets + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['disabled'] = 'Disabled'; +$string['disabledwithvalue'] = 'Disabled ({$a})'; +$string['enabled'] = 'Enabled'; +$string['errordeleting'] = 'Error deleting from database.'; +$string['errorinserting'] = 'Error inserting into database.'; +$string['errornopreset'] = 'It doesn\'t exists a preset with that name.'; +$string['fullpreset'] = 'Full'; +$string['fullpresetdescription'] = 'All the Starter features plus External (LTI) tool, SCORM, Workshop, Analytics, Badges, Competencies, Learning plans and lots more.'; +$string['markedasadvanced'] = 'marked as advanced'; +$string['markedasforced'] = 'marked as forced'; +$string['markedaslocked'] = 'marked as locked'; +$string['markedasnonadvanced'] = 'marked as non advanced'; +$string['markedasnonforced'] = 'marked as non forced'; +$string['markedasnonlocked'] = 'marked as non locked'; +$string['privacy:metadata:adminpresets'] = 'The list of configuration presets.'; +$string['privacy:metadata:adminpresets:comments'] = 'A description about the preset.'; +$string['privacy:metadata:adminpresets:moodlerelease'] = 'The Moodle release version where the preset is based on.'; +$string['privacy:metadata:adminpresets:name'] = 'The name of the preset.'; +$string['privacy:metadata:adminpresets:site'] = 'The Moodle site where this preset was created.'; +$string['privacy:metadata:adminpresets:timecreated'] = 'The time that the change was made.'; +$string['privacy:metadata:adminpresets:userid'] = 'The user who create the preset.'; +$string['privacy:metadata:adminpresets_app'] = 'The configuration presets that have been applied.'; +$string['privacy:metadata:adminpresets_app:adminpresetid'] = 'The id of the preset applied.'; +$string['privacy:metadata:adminpresets_app:time'] = 'The time that the preset was applied.'; +$string['privacy:metadata:adminpresets_app:userid'] = 'The user who applied the preset.'; +$string['siteadminpresetspluginname'] = 'Site admin presets'; +$string['starterpreset'] = 'Starter'; +$string['starterpresetdescription'] = 'Moodle with all of the most popular features, including Assignment, Feedback, Forum, H5P, Quiz and Completion tracking.'; +$string['wrongid'] = 'Wrong id'; diff --git a/lib/components.json b/lib/components.json index 73dea36498e..19ca1243fd8 100644 --- a/lib/components.json +++ b/lib/components.json @@ -45,6 +45,7 @@ "subsystems": { "access": null, "admin": "admin", + "adminpresets": "adminpresets", "analytics": "analytics", "antivirus": "lib\/antivirus", "auth": "auth", diff --git a/lib/db/install.php b/lib/db/install.php index db1041e4700..1f45b51cf9b 100644 --- a/lib/db/install.php +++ b/lib/db/install.php @@ -331,4 +331,8 @@ function xmldb_main_install() { require_once($CFG->dirroot . '/badges/upgradelib.php'); // Core install and upgrade related functions only for badges. badges_install_default_backpacks(); + + // Create default core site admin presets. + require_once($CFG->dirroot . '/adminpresets/classes/helper.php'); + \core_adminpresets\helper::create_default_presets(); } diff --git a/lib/db/install.xml b/lib/db/install.xml index 6d4a6d15145..0a62801cf5c 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -1,5 +1,5 @@ - @@ -4497,5 +4497,130 @@ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 013dbac2035..12a80c4dc12 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -3322,5 +3322,199 @@ privatefiles,moodle|/user/files.php'; upgrade_main_savepoint(true, 2021122100.02); } + if ($oldversion < 2021123000.01) { + // The tool_admin_presets tables have been moved to core, because core_adminpresets component has been created, so + // it can interact with the rest of core. + // So the tool_admin_presetsXXX tables will be renamed to adminipresetsXXX if they exists; otherwise, they will be created. + + $tooltable = new xmldb_table('tool_admin_presets'); + $table = new xmldb_table('adminpresets'); + if ($dbman->table_exists($tooltable)) { + $dbman->rename_table($tooltable, 'adminpresets'); + } else if (!$dbman->table_exists($table)) { + // Adding fields to table adminpresets. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); + $table->add_field('comments', XMLDB_TYPE_TEXT, null, null, null, null, null); + $table->add_field('site', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); + $table->add_field('author', XMLDB_TYPE_CHAR, '255', null, null, null, null); + $table->add_field('moodleversion', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null); + $table->add_field('moodlerelease', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); + $table->add_field('iscore', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('timeimported', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + + // Adding keys to table adminpresets. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + + // Launch create table for adminpresets. + $dbman->create_table($table); + } + + $tooltable = new xmldb_table('tool_admin_presets_it'); + $table = new xmldb_table('adminpresets_it'); + if ($dbman->table_exists($tooltable)) { + $dbman->rename_table($tooltable, 'adminpresets_it'); + } else if (!$dbman->table_exists($table)) { + // Adding fields to table adminpresets_it. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('adminpresetid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('plugin', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null); + $table->add_field('value', XMLDB_TYPE_TEXT, null, null, null, null, null); + + // Adding keys to table adminpresets_it. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + + // Adding indexes to table adminpresets_it. + $table->add_index('adminpresetid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetid']); + + // Launch create table for adminpresets_it. + $dbman->create_table($table); + } + + $tooltable = new xmldb_table('tool_admin_presets_it_a'); + $table = new xmldb_table('adminpresets_it_a'); + if ($dbman->table_exists($tooltable)) { + $dbman->rename_table($tooltable, 'adminpresets_it_a'); + } else if (!$dbman->table_exists($table)) { + // Adding fields to table adminpresets_it_a. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null); + $table->add_field('value', XMLDB_TYPE_TEXT, null, null, null, null, null); + + // Adding keys to table adminpresets_it_a. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + + // Adding indexes to table adminpresets_it_a. + $table->add_index('itemid', XMLDB_INDEX_NOTUNIQUE, ['itemid']); + + // Launch create table for adminpresets_it_a. + $dbman->create_table($table); + } + + $tooltable = new xmldb_table('tool_admin_presets_app'); + $table = new xmldb_table('adminpresets_app'); + if ($dbman->table_exists($tooltable)) { + $dbman->rename_table($tooltable, 'adminpresets_app'); + } else if (!$dbman->table_exists($table)) { + // Adding fields to table adminpresets_app. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('adminpresetid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('time', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + + // Adding keys to table adminpresets_app. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + + // Adding indexes to table adminpresets_app. + $table->add_index('adminpresetid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetid']); + + // Launch create table for adminpresets_app. + $dbman->create_table($table); + } + + $tooltable = new xmldb_table('tool_admin_presets_app_it'); + $table = new xmldb_table('adminpresets_app_it'); + if ($dbman->table_exists($tooltable)) { + $dbman->rename_table($tooltable, 'adminpresets_app_it'); + } else if (!$dbman->table_exists($table)) { + // Adding fields to table adminpresets_app_it. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('adminpresetapplyid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('configlogid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + + // Adding keys to table adminpresets_app_it. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + + // Adding indexes to table adminpresets_app_it. + $table->add_index('configlogid', XMLDB_INDEX_NOTUNIQUE, ['configlogid']); + $table->add_index('adminpresetapplyid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetapplyid']); + + // Launch create table for adminpresets_app_it. + $dbman->create_table($table); + } + + $tooltable = new xmldb_table('tool_admin_presets_app_it_a'); + $table = new xmldb_table('adminpresets_app_it_a'); + if ($dbman->table_exists($tooltable)) { + $dbman->rename_table($tooltable, 'adminpresets_app_it_a'); + } else if (!$dbman->table_exists($table)) { + // Adding fields to table adminpresets_app_it_a. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('adminpresetapplyid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('configlogid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('itemname', XMLDB_TYPE_CHAR, '100', null, null, null, null); + + // Adding keys to table adminpresets_app_it_a. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + + // Adding indexes to table adminpresets_app_it_a. + $table->add_index('configlogid', XMLDB_INDEX_NOTUNIQUE, ['configlogid']); + $table->add_index('adminpresetapplyid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetapplyid']); + + // Launch create table for adminpresets_app_it_a. + $dbman->create_table($table); + } + + $tooltable = new xmldb_table('tool_admin_presets_plug'); + $table = new xmldb_table('adminpresets_plug'); + if ($dbman->table_exists($tooltable)) { + $dbman->rename_table($tooltable, 'adminpresets_plug'); + } else if (!$dbman->table_exists($table)) { + // Adding fields to table adminpresets_plug. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('adminpresetid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('plugin', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null); + $table->add_field('enabled', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0'); + + // Adding keys to table adminpresets_plug. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + + // Adding indexes to table adminpresets_plug. + $table->add_index('adminpresetid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetid']); + + // Launch create table for adminpresets_plug. + $dbman->create_table($table); + } + + $tooltable = new xmldb_table('tool_admin_presets_app_plug'); + $table = new xmldb_table('adminpresets_app_plug'); + if ($dbman->table_exists($tooltable)) { + $dbman->rename_table($tooltable, 'adminpresets_app_plug'); + } else if (!$dbman->table_exists($table)) { + // Adding fields to table adminpresets_app_plug. + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('adminpresetapplyid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); + $table->add_field('plugin', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('name', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null); + $table->add_field('value', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('oldvalue', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0'); + + // Adding keys to table adminpresets_app_plug. + $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); + + // Adding indexes to table adminpresets_app_plug. + $table->add_index('adminpresetapplyid', XMLDB_INDEX_NOTUNIQUE, ['adminpresetapplyid']); + + // Launch create table for adminpresets_app_plug. + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + } + + if ($DB->count_records('adminpresets', ['iscore' => 1]) == 0) { + // Create default core site admin presets. + require_once($CFG->dirroot . '/adminpresets/classes/helper.php'); + \core_adminpresets\helper::create_default_presets(); + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2021123000.01); + } + return true; } diff --git a/lib/tests/component_test.php b/lib/tests/component_test.php index f9ec91db5fe..631fd27643b 100644 --- a/lib/tests/component_test.php +++ b/lib/tests/component_test.php @@ -36,7 +36,7 @@ class component_test extends advanced_testcase { * this is defined here to annoy devs that try to add more without any thinking, * always verify that it does not collide with any existing add-on modules and subplugins!!! */ - const SUBSYSTEMCOUNT = 74; + const SUBSYSTEMCOUNT = 75; public function setUp(): void { $psr0namespaces = new ReflectionProperty('core_component', 'psr0namespaces'); diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3a1ccc779e7..7c0661877d9 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -208,6 +208,9 @@ reportbuilder/tests + + adminpresets/tests + diff --git a/version.php b/version.php index 4366b8f45e6..d2742be3e0b 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2021123000.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2021123000.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.0dev+ (Build: 20211230)'; // Human-friendly version name