diff --git a/admin/tool/admin_presets/classes/event/preset_deleted.php b/admin/tool/admin_presets/classes/event/preset_deleted.php new file mode 100755 index 0000000000000..ecc47f1f237a1 --- /dev/null +++ b/admin/tool/admin_presets/classes/event/preset_deleted.php @@ -0,0 +1,48 @@ +. + +namespace tool_admin_presets\event; + +use core\event\base; + +/** + * Admin tool presets event class deleted. + * + * @package tool_admin_presets + * @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 preset_deleted extends base { + + public static function get_name(): string { + return get_string('eventpresetdeleted', 'tool_admin_presets'); + } + + public function get_description(): string { + return "User {$this->userid} has deleted the preset with id {$this->objectid}."; + } + + public function get_legacy_logdata(): array { + return [$this->courseid, 'tool_admin_presets', 'delete', '', $this->objectid, $this->contextinstanceid]; + } + + protected function init(): void { + $this->data['crud'] = 'd'; + $this->data['edulevel'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'tool_admin_presets'; + } +} diff --git a/admin/tool/admin_presets/classes/event/preset_downloaded.php b/admin/tool/admin_presets/classes/event/preset_downloaded.php new file mode 100755 index 0000000000000..48dc39e57a0b6 --- /dev/null +++ b/admin/tool/admin_presets/classes/event/preset_downloaded.php @@ -0,0 +1,49 @@ +. + +namespace tool_admin_presets\event; + +use core\event\base; + +/** + * Admin tool presets event class downloaded. + * + * @package tool_admin_presets + * @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 preset_downloaded extends base { + + public static function get_name(): string { + return get_string('eventpresetdownloaded', 'tool_admin_presets'); + } + + public function get_description(): string { + return "User {$this->userid} has downloaded the preset with id {$this->objectid}."; + } + + public function get_url(): \moodle_url { + return new \moodle_url('/admin/tool/admin_presets/index.php', + ['action' => 'export', 'mode' => 'download_xml', 'id' => $this->objectid, 'sesskey' => sesskey()]); + } + + protected function init(): void { + $this->data['crud'] = 'r'; + $this->data['edulevel'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'tool_admin_presets'; + } +} diff --git a/admin/tool/admin_presets/classes/event/preset_exported.php b/admin/tool/admin_presets/classes/event/preset_exported.php new file mode 100755 index 0000000000000..8e5c926121bea --- /dev/null +++ b/admin/tool/admin_presets/classes/event/preset_exported.php @@ -0,0 +1,53 @@ +. + +namespace tool_admin_presets\event; + +use core\event\base; + +/** + * Admin tool presets event class exported. + * + * @package tool_admin_presets + * @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 preset_exported extends base { + + public static function get_name(): string { + return get_string('eventpresetexported', 'tool_admin_presets'); + } + + public function get_description(): string { + return "User {$this->userid} has exported the preset with id {$this->objectid}."; + } + + public function get_url(): \moodle_url { + return new \moodle_url('/admin/tool/admin_presets/index.php', + ['action' => 'load', 'mode' => 'preview', 'id' => $this->objectid]); + } + + public function get_legacy_logdata(): array { + return [$this->courseid, 'tool_admin_presets', 'export', '', $this->objectid, $this->contextinstanceid]; + } + + protected function init(): void { + $this->data['crud'] = 'c'; + $this->data['edulevel'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'tool_admin_presets'; + } +} diff --git a/admin/tool/admin_presets/classes/event/preset_imported.php b/admin/tool/admin_presets/classes/event/preset_imported.php new file mode 100755 index 0000000000000..7e1b198b5a498 --- /dev/null +++ b/admin/tool/admin_presets/classes/event/preset_imported.php @@ -0,0 +1,53 @@ +. + +namespace tool_admin_presets\event; + +use core\event\base; + +/** + * Admin tool presets event class imported. + * + * @package tool_admin_presets + * @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 preset_imported extends base { + + public static function get_name(): string { + return get_string('eventpresetimported', 'tool_admin_presets'); + } + + public function get_description(): string { + return "User {$this->userid} has imported the preset with id {$this->objectid}."; + } + + public function get_url(): \moodle_url { + return new \moodle_url('/admin/tool/admin_presets/index.php', + ['action' => 'load', 'mode' => 'preview', 'id' => $this->objectid]); + } + + public function get_legacy_logdata(): array { + return [$this->courseid, 'tool_admin_presets', 'import', '', $this->objectid, $this->contextinstanceid]; + } + + protected function init(): void { + $this->data['crud'] = 'c'; + $this->data['edulevel'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'tool_admin_presets'; + } +} diff --git a/admin/tool/admin_presets/classes/event/preset_loaded.php b/admin/tool/admin_presets/classes/event/preset_loaded.php new file mode 100755 index 0000000000000..2e8830a821d05 --- /dev/null +++ b/admin/tool/admin_presets/classes/event/preset_loaded.php @@ -0,0 +1,53 @@ +. + +namespace tool_admin_presets\event; + +use core\event\base; + +/** + * Admin tool presets event class loaded. + * + * @package tool_admin_presets + * @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 preset_loaded extends base { + + public static function get_name(): string { + return get_string('eventpresetloaded', 'tool_admin_presets'); + } + + public function get_description(): string { + return "User {$this->userid} has loaded the preset with id {$this->objectid}."; + } + + public function get_url(): \moodle_url { + return new \moodle_url('/admin/tool/admin_presets/index.php', + ['action' => 'load', 'mode' => 'preview', 'id' => $this->objectid]); + } + + public function get_legacy_logdata(): array { + return [$this->courseid, 'tool_admin_presets', 'load', '', $this->objectid, $this->contextinstanceid]; + } + + protected function init(): void { + $this->data['crud'] = 'u'; + $this->data['edulevel'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'tool_admin_presets'; + } +} diff --git a/admin/tool/admin_presets/classes/event/preset_previewed.php b/admin/tool/admin_presets/classes/event/preset_previewed.php new file mode 100755 index 0000000000000..3c66c59f7d8d5 --- /dev/null +++ b/admin/tool/admin_presets/classes/event/preset_previewed.php @@ -0,0 +1,49 @@ +. + +namespace tool_admin_presets\event; + +use core\event\base; + +/** + * Admin tool presets event class previewed. + * + * @package tool_admin_presets + * @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 preset_previewed extends base { + + public static function get_name(): string { + return get_string('eventpresetpreviewed', 'tool_admin_presets'); + } + + public function get_description(): string { + return "User {$this->userid} has previewed the preset with id {$this->objectid}."; + } + + public function get_url(): \moodle_url { + return new \moodle_url('/admin/tool/admin_presets/index.php', + ['action' => 'load', 'mode' => 'preview', 'id' => $this->objectid]); + } + + protected function init(): void { + $this->data['crud'] = 'r'; + $this->data['edulevel'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'tool_admin_presets'; + } +} diff --git a/admin/tool/admin_presets/classes/event/preset_reverted.php b/admin/tool/admin_presets/classes/event/preset_reverted.php new file mode 100755 index 0000000000000..5f83eaac8b331 --- /dev/null +++ b/admin/tool/admin_presets/classes/event/preset_reverted.php @@ -0,0 +1,48 @@ +. + +namespace tool_admin_presets\event; + +use core\event\base; + +/** + * Admin tool presets event class reverted. + * + * @package tool_admin_presets + * @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 preset_reverted extends base { + + public static function get_name(): string { + return get_string('eventpresetreverted', 'tool_admin_presets'); + } + + public function get_description(): string { + return "User {$this->userid} has reverted the preset with id {$this->objectid}."; + } + + public function get_legacy_logdata(): array { + return [$this->courseid, 'tool_admin_presets', 'rollback', '', $this->objectid, $this->contextinstanceid]; + } + + protected function init(): void { + $this->data['crud'] = 'u'; + $this->data['edulevel'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'tool_admin_presets'; + } +} diff --git a/admin/tool/admin_presets/classes/event/presets_listed.php b/admin/tool/admin_presets/classes/event/presets_listed.php new file mode 100755 index 0000000000000..2b50877490e1b --- /dev/null +++ b/admin/tool/admin_presets/classes/event/presets_listed.php @@ -0,0 +1,52 @@ +. + +namespace tool_admin_presets\event; + +use core\event\base; + +/** + * Admin tool presets event class listed. + * + * @package tool_admin_presets + * @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 presets_listed extends base { + + public static function get_name(): string { + return get_string('eventpresetslisted', 'tool_admin_presets'); + } + + public function get_description(): string { + return "User {$this->userid} listed the system presets."; + } + + public function get_url(): \moodle_url { + return new \moodle_url('/admin/tool/admin_presets/index.php'); + } + + public function get_legacy_logdata(): array { + return [$this->courseid, 'tool_admin_presets', 'base', '', $this->objectid, $this->contextinstanceid]; + } + + protected function init(): void { + $this->data['crud'] = 'r'; + $this->data['edulevel'] = self::LEVEL_OTHER; + $this->data['objecttable'] = 'tool_admin_presets'; + } +} diff --git a/admin/tool/admin_presets/classes/form/continue_form.php b/admin/tool/admin_presets/classes/form/continue_form.php new file mode 100644 index 0000000000000..49697a0bb2a42 --- /dev/null +++ b/admin/tool/admin_presets/classes/form/continue_form.php @@ -0,0 +1,38 @@ +. + +namespace tool_admin_presets\form; + +defined('MOODLE_INTERNAL') || die(); + +use moodleform; + +global $CFG; +require_once($CFG->dirroot . '/lib/formslib.php'); + +/** + * Form for loading continue button. + * + * @package tool_admin_presets + * @copyright 2021 Amaia Anabitarte + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class continue_form extends moodleform { + + public function definition(): void { + $this->add_action_buttons(false, get_string('continue')); + } +} diff --git a/admin/tool/admin_presets/classes/form/export_form.php b/admin/tool/admin_presets/classes/form/export_form.php new file mode 100644 index 0000000000000..8d56346778b9c --- /dev/null +++ b/admin/tool/admin_presets/classes/form/export_form.php @@ -0,0 +1,61 @@ +. + +namespace tool_admin_presets\form; + +use moodleform; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot . '/lib/formslib.php'); + +/** + * Form for exporting settings. + * + * @package tool_admin_presets + * @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 export_form extends moodleform { + + public function definition(): void { + global $USER; + + $mform = &$this->_form; + + // Preset attributes. + $mform->addElement('text', 'name', get_string('name'), 'maxlength="254" size="60"'); + $mform->addRule('name', null, 'required', null, 'client'); + $mform->setType('name', PARAM_TEXT); + + $mform->addElement('editor', 'comments', get_string('description')); + $mform->setType('comments', PARAM_CLEANHTML); + + $mform->addElement('text', 'author', + get_string('author', 'tool_admin_presets'), 'maxlength="254" size="60"'); + $mform->setType('author', PARAM_TEXT); + $mform->setDefault('author', $USER->firstname . ' ' . $USER->lastname); + + $mform->addElement('checkbox', 'includesensiblesettings', + get_string('includesensiblesettings', 'tool_admin_presets')); + $mform->setDefault('includesensiblesettings', 0); + $mform->addHelpButton('includesensiblesettings', 'includesensiblesettings', 'tool_admin_presets'); + + $this->add_action_buttons(true, get_string('actionexportbutton', 'tool_admin_presets')); + } +} diff --git a/admin/tool/admin_presets/classes/form/import_form.php b/admin/tool/admin_presets/classes/form/import_form.php new file mode 100644 index 0000000000000..c7ee781ff206c --- /dev/null +++ b/admin/tool/admin_presets/classes/form/import_form.php @@ -0,0 +1,51 @@ +. + +namespace tool_admin_presets\form; + +defined('MOODLE_INTERNAL') || die(); + +use moodleform; + +global $CFG; +require_once($CFG->dirroot . '/lib/formslib.php'); + +/** + * Form for importting settings. + * + * @package tool_admin_presets + * @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 import_form extends moodleform { + + public function definition(): void { + $mform = &$this->_form; + + // Rename preset input. + $mform->addElement('text', 'name', + get_string('renamepreset', 'tool_admin_presets'), 'maxlength="254" size="40"'); + $mform->setType('name', PARAM_TEXT); + + // File upload. + $mform->addElement('filepicker', 'xmlfile', + get_string('selectfile', 'tool_admin_presets')); + $mform->addRule('xmlfile', null, 'required'); + + $this->add_action_buttons(true, get_string('import', 'tool_admin_presets')); + } +} diff --git a/admin/tool/admin_presets/classes/form/load_form.php b/admin/tool/admin_presets/classes/form/load_form.php new file mode 100644 index 0000000000000..9cd8fb4bb352e --- /dev/null +++ b/admin/tool/admin_presets/classes/form/load_form.php @@ -0,0 +1,47 @@ +. + +namespace tool_admin_presets\form; + +defined('MOODLE_INTERNAL') || die(); + +use moodleform; + +global $CFG; +require_once($CFG->dirroot . '/lib/formslib.php'); + +/** + * Form for loading settings. + * + * @package tool_admin_presets + * @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 load_form extends moodleform { + + public function definition(): void { + $mform = &$this->_form; + + $mform->addElement('hidden', 'id'); + $mform->setType('id', PARAM_INT); + + $html = '
' . get_string('applypresetdescription', 'tool_admin_presets') . '
'; + $mform->addElement('html', $html); + + $this->add_action_buttons(true, get_string('loadselected', 'tool_admin_presets')); + } +} diff --git a/admin/tool/admin_presets/classes/helper.php b/admin/tool/admin_presets/classes/helper.php new file mode 100644 index 0000000000000..67d911be3b9a0 --- /dev/null +++ b/admin/tool/admin_presets/classes/helper.php @@ -0,0 +1,120 @@ +. + +namespace tool_admin_presets; + +/** + * Admin tool presets helper class. + * + * @package tool_admin_presets + * @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('tool_admin_presets', $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('tool_admin_presets_it', $presetitem); + + if (!empty($advname)) { + $presetadv = [ + 'itemid' => $itemid, + 'name' => $advname, + 'value' => $advvalue, + ]; + $DB->insert_record('tool_admin_presets_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('tool_admin_presets_plug', $pluginentry); + + return $pluginid; + } +} diff --git a/admin/tool/admin_presets/classes/local/action/base.php b/admin/tool/admin_presets/classes/local/action/base.php new file mode 100644 index 0000000000000..5b0fe88a4155a --- /dev/null +++ b/admin/tool/admin_presets/classes/local/action/base.php @@ -0,0 +1,206 @@ +. + +namespace tool_admin_presets\local\action; + +use context_system; +use moodle_url; +use tool_admin_presets\manager; +use tool_admin_presets\output\presets_list; +use tool_admin_presets\output\export_import; + +/** + * Admin tool presets main controller class. + * + * @package tool_admin_presets + * @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 base { + + /** @var array Array map for the events. **/ + protected static $eventsactionsmap = [ + 'base' => 'presets_listed', + 'delete' => 'preset_deleted', + 'export' => 'preset_exported', + 'import' => 'preset_imported', + 'preview' => 'preset_previewed', + 'load' => 'preset_loaded', + 'rollback' => 'preset_reverted', + 'download_xml' => 'preset_downloaded' + ]; + + /** @var string The main action (delete, export, import, load...). **/ + protected $action; + + /** @var string The mode (show, execute...). **/ + protected $mode; + + /** @var int Admin preset identifier. **/ + protected $id; + + /** @var int The output content to display in the page. **/ + protected $outputs; + + /** @var int The moodle form to display in the page. **/ + protected $moodleform; + + /** @var manager The manager helper class instance. **/ + protected $manager; + + /** + * Loads common class attributes. + */ + public function __construct() { + $this->manager = new manager(); + $this->action = optional_param('action', 'base', PARAM_ALPHA); + $this->mode = optional_param('mode', 'show', PARAM_ALPHAEXT); + $this->id = optional_param('id', false, PARAM_INT); + } + + /** + * Method to list the presets available on the system + * + * It allows users to access the different preset + * actions (preview, load, download, delete and rollback) + */ + public function show(): void { + global $DB, $OUTPUT; + + $options = new export_import(); + $this->outputs = $OUTPUT->render($options); + + $presets = $DB->get_records('tool_admin_presets'); + $list = new presets_list($presets, true); + $this->outputs .= $OUTPUT->render($list); + } + + /** + * Main display method + * + * Prints the block header and the common block outputs, the + * selected action outputs, his form and the footer + * + * $outputs value depends on $mode and $action selected + */ + public function display(): void { + global $OUTPUT; + + $this->display_header(); + + // Other outputs. + if (!empty($this->outputs)) { + echo $this->outputs; + } + + // Form. + if ($this->moodleform) { + $this->moodleform->display(); + } + + // Footer. + echo $OUTPUT->footer(); + } + + /** + * Displays the header + */ + protected function display_header(): void { + global $PAGE, $OUTPUT, $SITE; + + // Strings. + $titlestr = get_string('pluginname', 'tool_admin_presets'); + + // Header. + $PAGE->set_title($titlestr); + $PAGE->set_heading($SITE->fullname); + + $PAGE->navbar->add(get_string('pluginname', 'tool_admin_presets'), + new moodle_url('/admin/tool/admin_presets/index.php') + ); + + $title = $this->get_title(); + $text = $this->get_explanatory_description(); + + $PAGE->navbar->add($title); + + echo $OUTPUT->header(); + echo $OUTPUT->heading($title); + if ($text) { + echo $OUTPUT->box($text); + } + } + + /** + * Get page title for this action. + * + * @return string The page title to display into the page. + */ + protected function get_title(): string { + if ($this->action == 'base') { + return get_string('pluginname', 'tool_admin_presets'); + } + + return get_string($this->action . $this->mode, 'tool_admin_presets'); + } + + /** + * Get explanatory description to be displayed below the heading. It's optional and might change depending on the + * action and the mode. + * + * @return string|null The explanatory description for the current action and mode. + */ + protected function get_explanatory_description(): ?string { + $text = null; + if ($this->action == 'base') { + $text = get_string('basedescription', 'tool_admin_presets'); + } + + return $text; + } + + /** + * Trigger an event based on the current action. + * + * @return void + */ + public function log(): void { + // The only read action we store is list presets and preview. + $islist = ($this->action == 'base' && $this->mode == 'show'); + $ispreview = ($this->action == 'load' && $this->mode == 'show'); + if ($this->mode != 'show' || $islist || $ispreview) { + $action = $this->action; + if ($ispreview) { + $action = 'preview'; + } + + if ($this->mode != 'execute' && $this->mode != 'show') { + $action = $this->mode; + } + + if (array_key_exists($action, self::$eventsactionsmap)) { + $eventnamespace = '\\tool_admin_presets\\event\\' . self::$eventsactionsmap[$action]; + $eventdata = [ + 'context' => context_system::instance(), + 'objectid' => $this->id + ]; + $event = $eventnamespace::create($eventdata); + $event->trigger(); + } + } + } +} diff --git a/admin/tool/admin_presets/classes/local/action/delete.php b/admin/tool/admin_presets/classes/local/action/delete.php new file mode 100644 index 0000000000000..488508fbd9543 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/action/delete.php @@ -0,0 +1,77 @@ +. + +namespace tool_admin_presets\local\action; + +use moodle_exception; + +/** + * This class extends base class and handles delete function. + * + * @package tool_admin_presets + * @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 delete extends base { + + /** + * Shows a confirm box + */ + public function show(): void { + global $DB, $OUTPUT; + + // Getting the preset name. + $presetdata = $DB->get_record('tool_admin_presets', ['id' => $this->id], 'name'); + + if ($presetdata) { + $deletetext = get_string('deletepreset', 'tool_admin_presets', $presetdata->name); + + $params = ['action' => $this->action, 'mode' => 'execute', 'id' => $this->id, 'sesskey' => sesskey()]; + $confirmurl = new \moodle_url('/admin/tool/admin_presets/index.php', $params); + + $cancelurl = new \moodle_url('/admin/tool/admin_presets/index.php'); + + // If the preset was applied add a warning text. + if ($DB->get_records('tool_admin_presets_app', ['adminpresetid' => $this->id])) { + $deletetext .= '

' . + get_string("deletepreviouslyapplied", "tool_admin_presets") . '

'; + } + $displayoptions = [ + 'confirmtitle' => get_string('deletepresettitle', 'tool_admin_presets', $presetdata->name), + 'continuestr' => get_string('delete') + ]; + $this->outputs = $OUTPUT->confirm($deletetext, $confirmurl, $cancelurl, $displayoptions); + } else { + throw new moodle_exception('errordeleting', 'tool_admin_presets'); + } + } + + /** + * Delete the DB preset + */ + public function execute(): void { + confirm_sesskey(); + + $this->manager->delete_preset($this->id); + + // Trigger the as it is usually triggered after execute finishes. + $this->log(); + + $url = new \moodle_url('/admin/tool/admin_presets/index.php'); + redirect($url); + } +} diff --git a/admin/tool/admin_presets/classes/local/action/export.php b/admin/tool/admin_presets/classes/local/action/export.php new file mode 100644 index 0000000000000..7444cbc621a24 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/action/export.php @@ -0,0 +1,103 @@ +. + +namespace tool_admin_presets\local\action; + +defined('MOODLE_INTERNAL') || die(); + +use tool_admin_presets\form\export_form; +use moodle_exception; + +global $CFG; +require_once($CFG->dirroot . '/lib/filelib.php'); +require_once($CFG->dirroot . '/backup/util/xml/xml_writer.class.php'); +require_once($CFG->dirroot . '/backup/util/xml/output/xml_output.class.php'); +require_once($CFG->dirroot . '/backup/util/xml/output/memory_xml_output.class.php'); + +/** + * This class extends base class and handles export function. + * + * @package tool_admin_presets + * @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 export extends base { + + /** + * Shows the initial form to export/save admin settings. + * + * Loads the database configuration and prints + * the settings in a hierarchical table + */ + public function show(): void { + $url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'export', 'mode' => 'execute']); + $this->moodleform = new export_form($url); + } + + /** + * Stores a preset into the DB. + */ + public function execute(): void { + confirm_sesskey(); + + $url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'export', 'mode' => 'execute']); + $this->moodleform = new export_form($url); + + if ($data = $this->moodleform->get_data()) { + list($presetid, $settingsfound, $pluginsfound) = $this->manager->export_preset($data); + + // Store it here for logging and other future id-oriented stuff. + $this->id = $presetid; + + // If there are no settings nor plugins, an error should be raised. + if (!$settingsfound && !$pluginsfound) { + $url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'export']); + redirect($url, get_string('novalidsettingsselected', 'tool_admin_presets')); + } + } + + // Trigger the as it is usually triggered after execute finishes. + $this->log(); + + $url = new \moodle_url('/admin/tool/admin_presets/index.php'); + redirect($url); + } + + /** + * To download system presets. + * + * @return void preset file + * @throws dml_exception + * @throws moodle_exception + * @throws xml_output_exception + * @throws xml_writer_exception + */ + public function download_xml(): void { + confirm_sesskey(); + + list($xmlstr, $filename) = $this->manager->download_preset($this->id); + + // Trigger the as it is usually triggered after execute finishes. + $this->log(); + + send_file($xmlstr, $filename, 0, 0, true, true); + } + + protected function get_explanatory_description(): ?string { + return get_string('exportdescription', 'tool_admin_presets'); + } +} diff --git a/admin/tool/admin_presets/classes/local/action/import.php b/admin/tool/admin_presets/classes/local/action/import.php new file mode 100644 index 0000000000000..837f5bc63baba --- /dev/null +++ b/admin/tool/admin_presets/classes/local/action/import.php @@ -0,0 +1,90 @@ +. + +namespace tool_admin_presets\local\action; + +use tool_admin_presets\form\import_form; + +/** + * This class extends base class and handles import function. + * + * @package tool_admin_presets + * @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 import extends base { + + /** + * Displays the import moodleform + */ + public function show(): void { + $url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'import', 'mode' => 'execute']); + $this->moodleform = new import_form($url); + } + + /** + * Imports the xmlfile into DB + */ + public function execute(): void { + confirm_sesskey(); + + $url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'import', 'mode' => 'execute']); + $this->moodleform = new import_form($url); + + if ($this->moodleform->is_cancelled()) { + $url = new \moodle_url('/admin/tool/admin_presets/index.php'); + redirect($url); + } + + if ($data = $this->moodleform->get_data()) { + // Getting the file. + $xmlcontent = $this->moodleform->get_file_content('xmlfile'); + list($xml, $preset, $settingsfound, $pluginsfound) = $this->manager->import_preset($xmlcontent, $data->name); + if (!$xml) { + $url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'import']); + redirect($url, get_string('wrongfile', 'tool_admin_presets')); + } + + // Store it here for logging and other future id-oriented stuff. + if (!is_null($preset)) { + $this->id = $preset->id; + } + + // If there are no valid or selected settings, raise an error. + if (!$settingsfound && !$pluginsfound) { + $url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'import']); + redirect($url, get_string('novalidsettings', 'tool_admin_presets')); + } + + // Trigger it after execute finishes. + $this->log(); + + $url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'load', 'id' => $preset->id]); + redirect($url); + } + } + + protected function get_explanatory_description(): ?string { + $text = null; + if ($this->mode == 'show') { + $text = get_string('importdescription', 'tool_admin_presets'); + } + + return $text; + } + +} diff --git a/admin/tool/admin_presets/classes/local/action/load.php b/admin/tool/admin_presets/classes/local/action/load.php new file mode 100644 index 0000000000000..dd1679fc4dd61 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/action/load.php @@ -0,0 +1,154 @@ +. + +namespace tool_admin_presets\local\action; + +use moodle_exception; +use stdClass; +use tool_admin_presets\form\continue_form; +use tool_admin_presets\form\load_form; +use tool_admin_presets\output\presets_list; + +/** + * This class extends base class and handles load function. + * + * @package tool_admin_presets + * @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 load extends base { + + /** + * Executes the settings load into the system + */ + public function execute(): void { + global $OUTPUT; + + confirm_sesskey(); + + $url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'load', 'mode' => 'execute']); + $this->moodleform = new load_form($url); + + if ($this->moodleform->is_cancelled()) { + redirect(new \moodle_url('/admin/tool/admin_presets/index.php?action=base')); + } + + if ($this->moodleform->is_submitted() && $this->moodleform->is_validated() && ($this->moodleform->get_data())) { + // Apply preset settings and set plugins visibility. + [$applied, $skipped] = $this->manager->apply_preset($this->id); + + if (empty($applied)) { + $message = [ + 'message' => get_string('nothingloaded', 'tool_admin_presets'), + 'closebutton' => true, + 'announce' => true, + ]; + } else { + $message = [ + 'message' => get_string('settingsappliednotification', 'tool_admin_presets'), + 'closebutton' => true, + 'announce' => true, + ]; + } + $application = new stdClass(); + $applieddata = new stdClass(); + $applieddata->show = !empty($applied); + $applieddata->message = $message; + $applieddata->heading = get_string('settingsapplied', 'tool_admin_presets'); + $applieddata->caption = get_string('settingsapplied', 'tool_admin_presets'); + $applieddata->settings = $applied; + $application->appliedchanges = $applieddata; + + $skippeddata = new stdClass(); + $skippeddata->show = !empty($skipped); + $skippeddata->heading = get_string('settingsnotapplied', 'tool_admin_presets'); + $skippeddata->caption = get_string('settingsnotapplicable', 'tool_admin_presets'); + $skippeddata->settings = $skipped; + $application->skippedchanges = $skippeddata; + + $this->outputs = $OUTPUT->render_from_template('tool_admin_presets/settings_application', $application); + $url = new \moodle_url('/admin/tool/admin_presets/index.php'); + $this->moodleform = new continue_form($url); + } + } + + /** + * Displays the select preset settings to select what to import. + * Loads the preset data and displays a settings tree. + * + * It checks the Moodle version and it only allows users to import + * the preset available settings. + */ + public function show(): void { + global $DB, $OUTPUT; + + $data = new stdClass(); + $data->id = $this->id; + + // Preset data. + if (!$preset = $DB->get_record('tool_admin_presets', ['id' => $data->id])) { + throw new moodle_exception('errornopreset', 'tool_admin_presets'); + } + + // Print preset basic data. + $list = new presets_list([$preset]); + $this->outputs = $OUTPUT->render($list); + + // Simulate preset application to display settings and plugins that will change. + [$applied] = $this->manager->apply_preset($this->id, true); + + // Order the applied array by the visiblename column. + if (!empty($applied)) { + $visiblenamecolumn = array_column($applied, 'visiblename'); + array_multisort($visiblenamecolumn, SORT_ASC, $applied); + } + + $application = new stdClass(); + $applieddata = new stdClass(); + $applieddata->show = !empty($applied); + $applieddata->heading = get_string('settingstobeapplied', 'tool_admin_presets'); + $applieddata->caption = get_string('settingsapplied', 'tool_admin_presets'); + $applieddata->settings = $applied; + $applieddata->beforeapplying = true; + $application->appliedchanges = $applieddata; + if (empty($applied)) { + // Display a warning when no settings will be applied. + $applieddata->message = get_string('nosettingswillbeapplied', 'tool_admin_presets'); + + // Only display the Continue button. + $url = new \moodle_url('/admin/tool/admin_presets/index.php'); + $this->moodleform = new continue_form($url); + } else { + // Display the form to apply the preset. + $url = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'load', 'mode' => 'execute']); + $this->moodleform = new load_form($url); + $this->moodleform->set_data($data); + } + + $this->outputs .= $OUTPUT->render_from_template('tool_admin_presets/settings_application', $application); + + } + + protected function get_explanatory_description(): ?string { + $text = null; + if ($this->mode == 'show') { + $text = get_string('loaddescription', 'tool_admin_presets'); + } + + return $text; + } +} diff --git a/admin/tool/admin_presets/classes/local/action/rollback.php b/admin/tool/admin_presets/classes/local/action/rollback.php new file mode 100644 index 0000000000000..9a37f05792039 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/action/rollback.php @@ -0,0 +1,124 @@ +. + +namespace tool_admin_presets\local\action; + +use stdClass; +use tool_admin_presets\form\continue_form; + +/** + * This class extends base class and handles rollback function. + * + * @package tool_admin_presets + * @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 rollback extends base { + + /** + * Displays the different previous applications of the preset + */ + public function show(): void { + global $DB, $OUTPUT; + + // Preset data. + $preset = $DB->get_record('tool_admin_presets', ['id' => $this->id]); + + // Applications data. + $context = new stdClass(); + $applications = $DB->get_records('tool_admin_presets_app', ['adminpresetid' => $this->id], 'time DESC'); + $context->noapplications = !empty($applications); + $context->applications = []; + foreach ($applications as $application) { + $format = get_string('strftimedatetime', 'langconfig'); + $user = $DB->get_record('user', ['id' => $application->userid]); + $rollbacklink = new \moodle_url( + '/admin/tool/admin_presets/index.php', + ['action' => 'rollback', 'mode' => 'execute', 'id' => $application->id, 'sesskey' => sesskey()] + ); + + $context->applications[] = [ + 'timeapplied' => strftime($format, $application->time), + 'user' => fullname($user), + 'action' => $rollbacklink->out(false), + ]; + } + + $this->outputs .= '
' . $OUTPUT->heading(get_string('presetname', 'tool_admin_presets') . ': ' . $preset->name, 3); + $this->outputs = $OUTPUT->render_from_template('tool_admin_presets/preset_applications_list', $context); + + $url = new \moodle_url('/admin/tool/admin_presets/index.php'); + $this->moodleform = new continue_form($url); + } + + /** + * Executes the application rollback + * + * Each setting value is checked against the config_log->value + */ + public function execute(): void { + global $OUTPUT; + + confirm_sesskey(); + + list($presetapp, $rollback, $failures) = $this->manager->revert_preset($this->id); + + if (!is_null($presetapp)) { + // Change $this->id to point to the preset. + $this->id = $presetapp->adminpresetid; + } + + $appliedchanges = new stdClass(); + $appliedchanges->show = !empty($rollback); + $appliedchanges->caption = get_string('rollbackresults', 'tool_admin_presets'); + $appliedchanges->settings = $rollback; + + $skippedchanges = new stdClass(); + $skippedchanges->show = !empty($failures); + $skippedchanges->caption = get_string('rollbackfailures', 'tool_admin_presets'); + $skippedchanges->settings = $failures; + + $data = new stdClass(); + $data->appliedchanges = $appliedchanges; + $data->skippedchanges = $skippedchanges; + $data->beforeapplying = true; + $this->outputs = $OUTPUT->render_from_template('tool_admin_presets/settings_rollback', $data); + + $url = new \moodle_url('/admin/tool/admin_presets/index.php'); + $this->moodleform = new continue_form($url); + } + + protected function get_title(): string { + global $DB; + + $title = ''; + if ($preset = $DB->get_record('tool_admin_presets', ['id' => $this->id])) { + $title = get_string($this->action . $this->mode, 'tool_admin_presets', $preset->name); + } + + return $title; + } + + protected function get_explanatory_description(): ?string { + $text = null; + if ($this->mode == 'show') { + $text = get_string('rollbackdescription', 'tool_admin_presets'); + } + + return $text; + } +} diff --git a/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_bloglevel.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_bloglevel.php new file mode 100644 index 0000000000000..d7680df6e0e53 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_bloglevel.php @@ -0,0 +1,57 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Select setting for blog's bloglevel setting. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_bloglevel extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configcheckbox.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configcheckbox.php new file mode 100644 index 0000000000000..139cd0eac4e81 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configcheckbox.php @@ -0,0 +1,43 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Checkbox setting. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configcheckbox extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configcheckbox_with_advanced.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configcheckbox_with_advanced.php new file mode 100644 index 0000000000000..cdf48595d90a8 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configcheckbox_with_advanced.php @@ -0,0 +1,45 @@ +. + +namespace tool_admin_presets\local\setting; + +use admin_setting; + +/** + * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configcheckbox_with_advanced extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configcheckbox_with_lock.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configcheckbox_with_lock.php new file mode 100644 index 0000000000000..6ab7d21d13227 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configcheckbox_with_lock.php @@ -0,0 +1,45 @@ +. + +namespace tool_admin_presets\local\setting; + +use admin_setting; + +/** + * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configcheckbox_with_lock extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configiplist.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configiplist.php new file mode 100644 index 0000000000000..abf845354af62 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configiplist.php @@ -0,0 +1,39 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Used to validate a textarea used for ip addresses. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configiplist extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configmulticheckbox.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configmulticheckbox.php new file mode 100644 index 0000000000000..801e5e02e33b2 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configmulticheckbox.php @@ -0,0 +1,32 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Class to be extended by multicheckbox settings. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configmulticheckbox extends admin_preset_admin_setting_configmultiselect { + + public function set_behaviors() { + $this->behaviors['loadchoices'] = &$this->settingdata; + } +} diff --git a/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configmultiselect.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configmultiselect.php new file mode 100644 index 0000000000000..69cc409e050e6 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configmultiselect.php @@ -0,0 +1,73 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Extends the base class and lists the selected values separated by comma. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configmultiselect extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configmultiselect_with_loader.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configmultiselect_with_loader.php new file mode 100644 index 0000000000000..5860c36ca1df3 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configmultiselect_with_loader.php @@ -0,0 +1,32 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Generalizes a configmultipleselect with load_choices(). + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configmultiselect_with_loader extends admin_preset_admin_setting_configmultiselect { + + public function set_behaviors() { + $this->behaviors['loadchoices'] = &$this->settingdata; + } +} diff --git a/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configselect.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configselect.php new file mode 100644 index 0000000000000..589c892a87ad2 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configselect.php @@ -0,0 +1,64 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Select one value from list. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configselect extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configselect_with_advanced.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configselect_with_advanced.php new file mode 100644 index 0000000000000..9b9e25605e894 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configselect_with_advanced.php @@ -0,0 +1,59 @@ +. + +namespace tool_admin_presets\local\setting; + +use admin_setting; + +/** + * Adds support for the "advanced" attribute. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configselect_with_advanced extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configtext.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configtext.php new file mode 100644 index 0000000000000..7672b400eeb7b --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configtext.php @@ -0,0 +1,57 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Basic text setting, cleans the param using the admin_setting paramtext attribute. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configtext extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configtext_with_advanced.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configtext_with_advanced.php new file mode 100644 index 0000000000000..5417977d42cac --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configtext_with_advanced.php @@ -0,0 +1,45 @@ +. + +namespace tool_admin_presets\local\setting; + +use admin_setting; + +/** + * Adds the advanced attribute. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configtext_with_advanced extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configtime.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configtime.php new file mode 100644 index 0000000000000..a7d12c1fe5656 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_configtime.php @@ -0,0 +1,64 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Time selector. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_configtime extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_devicedetectregex.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_devicedetectregex.php new file mode 100644 index 0000000000000..33c1660fa1acf --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_devicedetectregex.php @@ -0,0 +1,43 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Reimplementation to allow human friendly view of the selected regexps. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_devicedetectregex extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_gradecat_combo.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_gradecat_combo.php new file mode 100644 index 0000000000000..773112004d310 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_gradecat_combo.php @@ -0,0 +1,59 @@ +. + +namespace tool_admin_presets\local\setting; + +use admin_setting; + +/** + * A select with force and advanced options + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_gradecat_combo extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_sitesettext.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_sitesettext.php new file mode 100644 index 0000000000000..4e3979404b187 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_sitesettext.php @@ -0,0 +1,67 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Reimplemented to store values in course table, not in config or config_plugins. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_sitesettext extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_special_backupdays.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_special_backupdays.php new file mode 100644 index 0000000000000..255c8aa3a879a --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_special_backupdays.php @@ -0,0 +1,52 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Special control for selecting days to backup. + * + * It doesn't specify loadchoices behavior because is set_visiblevalue who needs it. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_special_backupdays extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_special_calendar_weekend.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_special_calendar_weekend.php new file mode 100644 index 0000000000000..96a7cb58537d2 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_special_calendar_weekend.php @@ -0,0 +1,44 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Special admin control for calendar weekend. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_special_calendar_weekend extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_users_with_capability.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_users_with_capability.php new file mode 100644 index 0000000000000..3e4516ce7580f --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_admin_setting_users_with_capability.php @@ -0,0 +1,39 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Extends configselect to reuse set_valuevisible. + * + * @package tool_admin_presets + * @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 admin_preset_admin_setting_users_with_capability extends admin_preset_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/admin/tool/admin_presets/classes/local/setting/admin_preset_setting.php b/admin/tool/admin_presets/classes/local/setting/admin_preset_setting.php new file mode 100644 index 0000000000000..21c49303d8622 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/admin_preset_setting.php @@ -0,0 +1,311 @@ +. + +namespace tool_admin_presets\local\setting; + +use admin_setting; +use moodle_exception; +use stdClass; + +/** + * Admin tool presets plugin to load some settings. + * + * @package tool_admin_presets + * @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 admin_preset_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', 'tool_admin_presets'); + } + + return $id; + } +} diff --git a/admin/tool/admin_presets/classes/local/setting/delegation.php b/admin/tool/admin_presets/classes/local/setting/delegation.php new file mode 100644 index 0000000000000..343f3259f8e46 --- /dev/null +++ b/admin/tool/admin_presets/classes/local/setting/delegation.php @@ -0,0 +1,53 @@ +. + +namespace tool_admin_presets\local\setting; + +use admin_setting; + +/** + * Cross-class methods + * + * @package tool_admin_presets + * @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, 'tool_admin_presets'); + } else { + $string = get_string('markedasnon' . $type, 'tool_admin_presets'); + } + + // Adding the advanced state. + return ', ' . $string; + } + + public function extra_loadchoices(admin_setting &$adminsetting) { + $adminsetting->load_choices(); + } +} diff --git a/admin/tool/admin_presets/classes/manager.php b/admin/tool/admin_presets/classes/manager.php new file mode 100644 index 0000000000000..3c1d67c5d2b19 --- /dev/null +++ b/admin/tool/admin_presets/classes/manager.php @@ -0,0 +1,1166 @@ +. + +namespace tool_admin_presets; + +use memory_xml_output; +use moodle_exception; +use SimpleXMLElement; +use stdClass; +use xml_writer; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir . '/adminlib.php'); + +/** + * Admin tool presets manager class. + * + * @package tool_admin_presets + * @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 = [ + 'admin_preset_admin_setting_agedigitalconsentmap' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configcolourpicker' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configdirectory' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configduration_with_advanced' => 'admin_preset_admin_setting_configtext_with_advanced', + 'admin_preset_admin_setting_configduration' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configempty' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configexecutable' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configfile' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_confightmleditor' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configmixedhostiplist' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configmultiselect_modules' => 'admin_preset_admin_setting_configmultiselect_with_loader', + 'admin_preset_admin_setting_configpasswordunmask' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configportlist' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configselect_with_lock' => 'admin_preset_admin_setting_configselect', + 'admin_preset_admin_setting_configtext_trim_lower' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configtext_with_maxlength' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configtextarea' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_configthemepreset' => 'admin_preset_admin_setting_configselect', + 'admin_preset_admin_setting_countrycodes' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_courselist_frontpage' => 'admin_preset_admin_setting_configmultiselect_with_loader', + 'admin_preset_admin_setting_description' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_enablemobileservice' => 'admin_preset_admin_setting_configcheckbox', + 'admin_preset_admin_setting_filetypes' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_forcetimezone' => 'admin_preset_admin_setting_configselect', + 'admin_preset_admin_setting_grade_profilereport' => 'admin_preset_admin_setting_configmultiselect_with_loader', + 'admin_preset_admin_setting_langlist' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_my_grades_report' => 'admin_preset_admin_setting_configselect', + 'admin_preset_admin_setting_pickroles' => 'admin_preset_admin_setting_configmulticheckbox', + 'admin_preset_admin_setting_question_behaviour' => 'admin_preset_admin_setting_configmultiselect_with_loader', + 'admin_preset_admin_setting_regradingcheckbox' => 'admin_preset_admin_setting_configcheckbox', + 'admin_preset_admin_setting_scsscode' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_servertimezone' => 'admin_preset_admin_setting_configselect', + 'admin_preset_admin_setting_sitesetcheckbox' => 'admin_preset_admin_setting_configcheckbox', + 'admin_preset_admin_setting_sitesetselect' => 'admin_preset_admin_setting_configselect', + 'admin_preset_admin_setting_special_adminseesall' => 'admin_preset_admin_setting_configcheckbox', + 'admin_preset_admin_setting_special_backup_auto_destination' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_special_coursecontact' => 'admin_preset_admin_setting_configmulticheckbox', + 'admin_preset_admin_setting_special_coursemanager' => 'admin_preset_admin_setting_configmulticheckbox', + 'admin_preset_admin_setting_special_debug' => 'admin_preset_admin_setting_configmultiselect_with_loader', + 'admin_preset_admin_setting_special_frontpagedesc' => 'admin_preset_admin_setting_sitesettext', + 'admin_preset_admin_setting_special_gradebookroles' => 'admin_preset_admin_setting_configmulticheckbox', + 'admin_preset_admin_setting_special_gradeexport' => 'admin_preset_admin_setting_configmulticheckbox', + 'admin_preset_admin_setting_special_gradelimiting' => 'admin_preset_admin_setting_configcheckbox', + 'admin_preset_admin_setting_special_grademinmaxtouse' => 'admin_preset_admin_setting_configselect', + 'admin_preset_admin_setting_special_gradepointdefault' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_special_gradepointmax' => 'admin_preset_admin_setting_configtext', + 'admin_preset_admin_setting_special_registerauth' => 'admin_preset_admin_setting_configmultiselect_with_loader', + 'admin_preset_admin_setting_special_selectsetup' => 'admin_preset_admin_setting_configselect', + 'admin_preset_admin_settings_country_select' => 'admin_preset_admin_setting_configmultiselect_with_loader', + 'admin_preset_admin_settings_coursecat_select' => 'admin_preset_admin_setting_configmultiselect_with_loader', + 'admin_preset_admin_settings_h5plib_handler_select' => 'admin_preset_admin_setting_configselect', + 'admin_preset_admin_settings_num_course_sections' => 'admin_preset_admin_setting_configmultiselect_with_loader', + 'admin_preset_admin_settings_sitepolicy_handler_select' => 'admin_preset_admin_setting_configselect', + 'admin_preset_antivirus_clamav_pathtounixsocket_setting' => 'admin_preset_admin_setting_configtext', + 'admin_preset_antivirus_clamav_runningmethod_setting' => 'admin_preset_admin_setting_configselect', + 'admin_preset_antivirus_clamav_tcpsockethost_setting' => 'admin_preset_admin_setting_configtext', + 'admin_preset_auth_db_admin_setting_special_auth_configtext' => 'admin_preset_admin_setting_configtext', + 'admin_preset_auth_ldap_admin_setting_special_lowercase_configtext' => 'admin_preset_admin_setting_configtext', + 'admin_preset_auth_ldap_admin_setting_special_ntlm_configtext' => 'admin_preset_admin_setting_configtext', + 'admin_preset_auth_shibboleth_admin_setting_convert_data' => 'admin_preset_admin_setting_configtext', + 'admin_preset_auth_shibboleth_admin_setting_special_idp_configtextarea' => 'admin_preset_admin_setting_configtext', + 'admin_preset_auth_shibboleth_admin_setting_special_wayf_select' => 'admin_preset_admin_setting_configselect', + 'admin_preset_editor_atto_toolbar_setting' => 'admin_preset_admin_setting_configtext', + 'admin_preset_editor_tinymce_json_setting_textarea' => 'admin_preset_admin_setting_configtext', + 'admin_preset_enrol_database_admin_setting_category' => 'admin_preset_admin_setting_configselect', + 'admin_preset_enrol_flatfile_role_setting' => 'admin_preset_admin_setting_configtext', + 'admin_preset_enrol_ldap_admin_setting_category' => 'admin_preset_admin_setting_configselect', + 'admin_preset_format_singleactivity_admin_setting_activitytype' => 'admin_preset_admin_setting_configselect', + 'admin_preset_qtype_multichoice_admin_setting_answernumbering' => 'admin_preset_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('tool_admin_presets_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 = 'admin_preset_' . $settingtype; + $classname = "\\$plugindata[0]_$plugindata[1]\\local\\setting\\$settingname"; + if (!class_exists($classname)) { + $classname = null; + } + } + } else { + $settingname = 'admin_preset_' . $settingtype; + $classname = '\\tool_admin_presets\\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 = '\\tool_admin_presets\\local\\setting\\admin_preset_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 '\\tool_admin_presets\\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('tool_admin_presets', ['id' => $presetid])) { + throw new moodle_exception('errornopreset', 'tool_admin_presets'); + } + + // 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); + + 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', 'tool_admin_presets'); + } + + // 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('tool_admin_presets_it', $setting)) { + throw new moodle_exception('errorinserting', 'tool_admin_presets'); + } + + // 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('tool_admin_presets_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('tool_admin_presets_plug', $entry); + $pluginsfound = true; + } + } + } + + // If there are no settings nor plugins, the admin preset record should be removed. + if (!$settingsfound && !$pluginsfound) { + $DB->delete_records('tool_admin_presets', ['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('tool_admin_presets', ['id' => $presetid])) { + throw new moodle_exception('errornopreset', 'tool_admin_presets'); + } + + // 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('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets', $preset)) { + throw new moodle_exception('errorinserting', 'tool_admin_presets'); + } + + // 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('tool_admin_presets_it', $item)) { + throw new moodle_exception('errorinserting', 'tool_admin_presets'); + } + + // 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('tool_admin_presets_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('tool_admin_presets_plug', $entry)) { + throw new moodle_exception('errorinserting', 'tool_admin_presets'); + } + } + } + } + + // If there are no valid or selected settings we should delete the admin preset record. + if (!$settingsfound && !$pluginsfound) { + $DB->delete_records('tool_admin_presets', ['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('tool_admin_presets', ['id' => $presetid]); + if (!$preset) { + throw new moodle_exception('errordeleting', 'tool_admin_presets'); + } + + // Deleting the preset applications. + if ($previouslyapplied = $DB->get_records('tool_admin_presets_app', ['adminpresetid' => $presetid], 'id')) { + $appids = array_keys($previouslyapplied); + list($insql, $inparams) = $DB->get_in_or_equal($appids); + $DB->delete_records_select('tool_admin_presets_app_it', "adminpresetapplyid $insql", $inparams); + $DB->delete_records_select('tool_admin_presets_app_it_a', "adminpresetapplyid $insql", $inparams); + $DB->delete_records_select('tool_admin_presets_app_plug', "adminpresetapplyid $insql", $inparams); + + if (!$DB->delete_records('tool_admin_presets_app', ['adminpresetid' => $presetid])) { + throw new moodle_exception('errordeleting', 'tool_admin_presets'); + } + } + + // Getting items ids and remove advanced items associated to them. + $items = $DB->get_records('tool_admin_presets_it', ['adminpresetid' => $presetid], 'id'); + if (!empty($items)) { + $itemsid = array_keys($items); + list($insql, $inparams) = $DB->get_in_or_equal($itemsid); + $DB->delete_records_select('tool_admin_presets_it_a', "itemid $insql", $inparams); + } + + if (!$DB->delete_records('tool_admin_presets_it', ['adminpresetid' => $presetid])) { + throw new moodle_exception('errordeleting', 'tool_admin_presets'); + } + + // Delete plugins. + if (!$DB->delete_records('tool_admin_presets_plug', ['adminpresetid' => $presetid])) { + throw new moodle_exception('errordeleting', 'tool_admin_presets'); + } + + // Delete preset. + if (!$DB->delete_records('tool_admin_presets', ['id' => $presetid])) { + throw new moodle_exception('errordeleting', 'tool_admin_presets'); + } + } + + /** + * 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('tool_admin_presets_app', ['id' => $presetappid])) { + throw new moodle_exception('wrongid', 'tool_admin_presets'); + } + + // Items. + $itemsql = "SELECT cl.id, cl.plugin, cl.name, cl.value, cl.oldvalue, ap.adminpresetapplyid + FROM {tool_admin_presets_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 admin_preset_apply_item instance. + $deletewhere = [ + 'adminpresetapplyid' => $change->adminpresetapplyid, + 'configlogid' => $change->id, + ]; + $DB->delete_records('tool_admin_presets_app_it', $deletewhere); + + } else { + $failures[] = $contextdata; + } + } + } + } + + // Attributes. + $attrsql = "SELECT cl.id, cl.plugin, cl.name, cl.value, cl.oldvalue, ap.itemname, ap.adminpresetapplyid + FROM {tool_admin_presets_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 admin_preset_apply_item_attr instance. + $deletewhere = [ + 'adminpresetapplyid' => $change->adminpresetapplyid, + 'configlogid' => $change->id, + ]; + $DB->delete_records('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets_app_plug', ['adminpresetapplyid' => $presetappid]); + } + + // Delete application if no items nor attributes nor plugins of the application remains. + if (!$DB->get_records('tool_admin_presets_app_it', ['adminpresetapplyid' => $presetappid]) && + !$DB->get_records('tool_admin_presets_app_it_a', ['adminpresetapplyid' => $presetappid]) && + !$DB->get_records('tool_admin_presets_app_plug', ['adminpresetapplyid' => $presetappid])) { + + $presetapp = $DB->get_record('tool_admin_presets_app', ['id' => $presetappid]); + $DB->delete_records('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets_app', $presetapplied)) { + throw new moodle_exception('errorinserting', 'tool_admin_presets'); + } + } + + // 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('tool_admin_presets_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('tool_admin_presets_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', 'tool_admin_presets'); + $strdisabled = get_string('disabled', 'tool_admin_presets'); + + $plugins = $DB->get_records('tool_admin_presets_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', 'tool_admin_presets', $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('tool_admin_presets_app', $presetapplied)) { + throw new moodle_exception('errorinserting', 'tool_admin_presets'); + } + } + + // 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('tool_admin_presets_app_plug', $appliedplug); + } + + if ($oldvalue > 0) { + $oldvisiblevalue = $strenabled; + } else if ($oldvalue == 0) { + $oldvisiblevalue = $strdisabled; + } else { + $oldvisiblevalue = get_string('disabledwithvalue', 'tool_admin_presets', $oldvalue); + } + $data['oldvisiblevalue'] = $oldvisiblevalue; + $applied[] = $data; + } catch (\exception $e) { + $skipped[] = $data; + } + } else { + $skipped[] = $data; + } + } + + return [$applied, $skipped, $adminpresetapplyid]; + } + +} diff --git a/admin/tool/admin_presets/classes/output/export_import.php b/admin/tool/admin_presets/classes/output/export_import.php new file mode 100644 index 0000000000000..d19934a8a1296 --- /dev/null +++ b/admin/tool/admin_presets/classes/output/export_import.php @@ -0,0 +1,54 @@ +. + +/** + * tool_admin_presets export and import option renderer + * + * @package tool_admin_presets + * @copyright 2021 Amaia Anabitarte + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_admin_presets\output; + +use renderable; +use templatable; +use renderer_base; +use stdClass; +/** + * Class containing data for export and import template + * + * @copyright 2021 Amaia Anabitarte + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class export_import implements renderable, templatable { + /** + * Export the data. + * + * @param renderer_base $output + * @return stdClass + */ + public function export_for_template(renderer_base $output): stdClass { + $context = new stdClass(); + $exportlink = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'export']); + $exportbutton = new \single_button($exportlink, get_string('actionexport', 'tool_admin_presets'), 'get'); + $context->export = $exportbutton->export_for_template($output); + + $importlink = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'import']); + $importbutton = new \single_button($importlink, get_string('actionimport', 'tool_admin_presets'), 'get'); + $context->import = $importbutton->export_for_template($output); + return $context; + } +} diff --git a/admin/tool/admin_presets/classes/output/presets_list.php b/admin/tool/admin_presets/classes/output/presets_list.php new file mode 100644 index 0000000000000..0691b54791652 --- /dev/null +++ b/admin/tool/admin_presets/classes/output/presets_list.php @@ -0,0 +1,139 @@ +. + +/** + * tool_admin_presets specific renderers + * + * @package tool_admin_presets + * @copyright 2021 Amaia Anabitarte + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_admin_presets\output; + +use renderable; +use templatable; +use renderer_base; +use stdClass; +/** + * Class containing data for admin_presets tool + * + * @copyright 2021 Amaia Anabitarte + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class presets_list implements renderable, templatable { + + /** + * @var stdClass[] Array of admin presets. + */ + private $presets; + + /** + * @var bool Wether the action menu is visible. + */ + private $showactions; + + /** + * Construct this renderable. + * + * @param stdClass[] $presets Array of existing admin presets. + * @param bool $showactions Whether actions should be displayed or not. + */ + public function __construct(array $presets, bool $showactions = false) { + $this->presets = $presets; + $this->showactions = $showactions; + } + + /** + * Export the data. + * + * @param renderer_base $output + * @return stdClass + */ + public function export_for_template(renderer_base $output): stdClass { + global $DB; + + $context = new stdClass(); + $context->presets = []; + foreach ($this->presets as $preset) { + if ($preset->timeimported) { + $timeimportedstring = userdate($preset->timeimported); + } else { + $timeimportedstring = ''; + } + + $data = [ + 'name' => format_text($preset->name, FORMAT_PLAIN), + 'description' => format_text($preset->comments, FORMAT_HTML), + 'release' => format_text($preset->moodlerelease, FORMAT_PLAIN), + 'author' => format_text($preset->author, FORMAT_PLAIN), + 'site' => format_text(clean_text($preset->site, PARAM_URL), FORMAT_PLAIN), + 'timecreated' => userdate($preset->timecreated), + 'timeimported' => $timeimportedstring + ]; + + if ($this->showactions) { + // Preset actions. + $actionsmenu = new \action_menu(); + $actionsmenu->set_menu_trigger(get_string('actions')); + $actionsmenu->set_owner_selector('preset-actions-' . $preset->id); + $actionsmenu->set_alignment(\action_menu::TL, \action_menu::BL); + + $loadlink = new \moodle_url('/admin/tool/admin_presets/index.php', ['action' => 'load', 'id' => $preset->id]); + $actionsmenu->add(new \action_menu_link_secondary( + $loadlink, new \pix_icon('t/play', ''), + get_string('applyaction', 'tool_admin_presets') + )); + $downloadlink = new \moodle_url('/admin/tool/admin_presets/index.php', + ['action' => 'export', 'mode' => 'download_xml', 'sesskey' => sesskey(), 'id' => $preset->id] + ); + $actionsmenu->add(new \action_menu_link_secondary( + $downloadlink, + new \pix_icon('t/download', ''), + get_string('download') + )); + + // Delete button won't be displayed for the pre-installed core "Starter" and "Full" presets. + if (!$preset->iscore) { + $deletelink = new \moodle_url('/admin/tool/admin_presets/index.php', + ['action' => 'delete', 'id' => $preset->id] + ); + $actionsmenu->add(new \action_menu_link_secondary( + $deletelink, + new \pix_icon('i/delete', ''), + get_string('delete') + )); + } + + // Look for preset applications. + if ($DB->get_records('tool_admin_presets_app', ['adminpresetid' => $preset->id])) { + $params = ['action' => 'rollback', 'id' => $preset->id]; + $rollbacklink = new \moodle_url('/admin/tool/admin_presets/index.php', $params); + $actionsmenu->add(new \action_menu_link_secondary( + $rollbacklink, + new \pix_icon('i/reload', ''), + get_string('showhistory', 'tool_admin_presets') + )); + } + $data['actions'] = $actionsmenu->export_for_template($output); + } + $context->presets[] = $data; + } + $context->nopresets = empty($context->presets); + $context->showactions = $this->showactions; + + return $context; + } +} diff --git a/admin/tool/admin_presets/classes/privacy/provider.php b/admin/tool/admin_presets/classes/privacy/provider.php new file mode 100755 index 0000000000000..262660504991b --- /dev/null +++ b/admin/tool/admin_presets/classes/privacy/provider.php @@ -0,0 +1,125 @@ +. + +namespace tool_admin_presets\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 tool presets this file handle privacy provider. + * + * @package tool_admin_presets + * @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 tool_admin_presets 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('tool_admin_presets', [ + 'userid' => 'privacy:metadata:admin_presets:userid', + 'name' => 'privacy:metadata:admin_presets:name', + 'comments' => 'privacy:metadata:admin_presets:comments', + 'site' => 'privacy:metadata:admin_presets:site', + 'moodlerelease' => 'privacy:metadata:admin_presets:moodlerelease', + 'timecreated' => 'privacy:metadata:admin_presets:timecreated', + ], 'privacy:metadata:admin_presets'); + + // The tool_admin_presets_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('tool_admin_presets_app', [ + 'adminpresetid' => 'privacy:metadata:tool_admin_presets_app:adminpresetid', + 'userid' => 'privacy:metadata:tool_admin_presets_app:userid', + 'time' => 'privacy:metadata:tool_admin_presets_app:time', + ], 'privacy:metadata:tool_admin_presets_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/admin/tool/admin_presets/db/install.php b/admin/tool/admin_presets/db/install.php new file mode 100755 index 0000000000000..274602d41ee7c --- /dev/null +++ b/admin/tool/admin_presets/db/install.php @@ -0,0 +1,250 @@ +. + +/** + * Install code for Admin tool presets plugin. + * + * @package tool_admin_presets + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +use tool_admin_presets\helper; + +/** + * Perform the post-install procedures. + */ +function xmldb_tool_admin_presets_install() { + + // Create the "Starter" site admin preset. + $data = [ + 'name' => get_string('starterpreset', 'tool_admin_presets'), + 'comments' => get_string('starterpresetdescription', 'tool_admin_presets'), + 'iscore' => 1, + ]; + $presetid = helper::create_preset($data); + + // Add settings to the "Starter" site admin preset. + helper::add_item($presetid, 'usecomments', '0'); + helper::add_item($presetid, 'usetags', '0'); + helper::add_item($presetid, 'enablenotes', '0'); + helper::add_item($presetid, 'enableblogs', '0'); + helper::add_item($presetid, 'enablebadges', '0'); + helper::add_item($presetid, 'enableanalytics', '0'); + helper::add_item($presetid, 'enabled', '0', 'core_competency'); + helper::add_item($presetid, 'pushcourseratingstouserplans', '0', 'core_competency'); + + helper::add_item($presetid, 'showdataretentionsummary', '0', 'tool_dataprivacy'); + helper::add_item($presetid, 'forum_maxattachments', '3'); + helper::add_item($presetid, 'customusermenuitems', 'grades,grades|/grade/report/mygrades.php|t/grades +preferences,moodle|/user/preferences.php|t/preferences'); + + // Modules: Hide chat, database, external tool (lti), IMS content package (imscp), lesson, SCORM, survey, wiki, workshop. + helper::add_plugin($presetid, 'mod', 'chat', false); + helper::add_plugin($presetid, 'mod', 'data', false); + helper::add_plugin($presetid, 'mod', 'lti', false); + helper::add_plugin($presetid, 'mod', 'imscp', false); + helper::add_plugin($presetid, 'mod', 'lesson', false); + helper::add_plugin($presetid, 'mod', 'scorm', false); + helper::add_plugin($presetid, 'mod', 'survey', false); + helper::add_plugin($presetid, 'mod', 'wiki', false); + helper::add_plugin($presetid, 'mod', 'workshop', false); + + // Availability restrictions: Hide Grouping, User profile. + helper::add_plugin($presetid, 'availability', 'grouping', false); + helper::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. + helper::add_plugin($presetid, 'block', 'activity_modules', false); + helper::add_plugin($presetid, 'block', 'blog_menu', false); + helper::add_plugin($presetid, 'block', 'blog_tags', false); + helper::add_plugin($presetid, 'block', 'comments', false); + helper::add_plugin($presetid, 'block', 'completionstatus', false); + helper::add_plugin($presetid, 'block', 'course_summary', false); + helper::add_plugin($presetid, 'block', 'course_list', false); + helper::add_plugin($presetid, 'block', 'tag_flickr', false); + helper::add_plugin($presetid, 'block', 'globalsearch', false); + helper::add_plugin($presetid, 'block', 'badges', false); + helper::add_plugin($presetid, 'block', 'lp', false); + helper::add_plugin($presetid, 'block', 'myprofile', false); + helper::add_plugin($presetid, 'block', 'login', false); + helper::add_plugin($presetid, 'block', 'site_main_menu', false); + helper::add_plugin($presetid, 'block', 'mentees', false); + helper::add_plugin($presetid, 'block', 'mnet_hosts', false); + helper::add_plugin($presetid, 'block', 'private_files', false); + helper::add_plugin($presetid, 'block', 'blog_recent', false); + helper::add_plugin($presetid, 'block', 'rss_client', false); + helper::add_plugin($presetid, 'block', 'search_forums', false); + helper::add_plugin($presetid, 'block', 'section_links', false); + helper::add_plugin($presetid, 'block', 'selfcompletion', false); + helper::add_plugin($presetid, 'block', 'social_activities', false); + helper::add_plugin($presetid, 'block', 'tags', false); + helper::add_plugin($presetid, 'block', 'tag_youtube', false); + helper::add_plugin($presetid, 'block', 'feedback', false); + + // Course formats: Disable Social format. + helper::add_plugin($presetid, 'format', 'social', false); + + // Data formats: Disable Javascript Object Notation (.json). + helper::add_plugin($presetid, 'dataformat', 'json', false); + + // Enrolments: Disable Cohort sync. + helper::add_plugin($presetid, 'enrol', 'cohort', false); + + // Filter: Disable MathJax, Activity names auto-linking. + helper::add_plugin($presetid, 'filter', 'mathjaxloader', TEXTFILTER_DISABLED); + helper::add_plugin($presetid, 'filter', 'activitynames', TEXTFILTER_DISABLED); + + // Question behaviours: Disable Adaptive mode (no penalties), Deferred feedback with CBM, Immediate feedback with CBM. + helper::add_plugin($presetid, 'qbehaviour', 'adaptivenopenalty', false); + helper::add_plugin($presetid, 'qbehaviour', 'deferredcbm', false); + helper::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. + helper::add_plugin($presetid, 'qtype', 'calculated', false); + helper::add_plugin($presetid, 'qtype', 'calculatedmulti', false); + helper::add_plugin($presetid, 'qtype', 'calculatedsimple', false); + helper::add_plugin($presetid, 'qtype', 'description', false); + helper::add_plugin($presetid, 'qtype', 'ddmarker', false); + helper::add_plugin($presetid, 'qtype', 'ddimageortext', false); + helper::add_plugin($presetid, 'qtype', 'multianswer', false); + helper::add_plugin($presetid, 'qtype', 'essay', false); + helper::add_plugin($presetid, 'qtype', 'numerical', false); + helper::add_plugin($presetid, 'qtype', 'randomsamatch', false); + + // Repositories: Disable Server files, URL downloader, Wikimedia. + helper::add_plugin($presetid, 'repository', 'local', false); + helper::add_plugin($presetid, 'repository', 'url', false); + helper::add_plugin($presetid, 'repository', 'wikimedia', false); + + // Text editors: Disable TinyMCE HTML editor. + helper::add_plugin($presetid, 'editor', 'tinymce', false); + + // Create the "Full" site admin preset. + $data = [ + 'name' => get_string('fullpreset', 'tool_admin_presets'), + 'comments' => get_string('fullpresetdescription', 'tool_admin_presets'), + 'iscore' => 1, + ]; + $presetid = helper::create_preset($data); + + // Add settings to the "Full" site admin preset. + helper::add_item($presetid, 'usecomments', '1'); + helper::add_item($presetid, 'usetags', '1'); + helper::add_item($presetid, 'enablenotes', '1'); + helper::add_item($presetid, 'enableblogs', '1'); + helper::add_item($presetid, 'enablebadges', '1'); + helper::add_item($presetid, 'enableanalytics', '1'); + helper::add_item($presetid, 'enabled', '1', 'core_competency'); + helper::add_item($presetid, 'pushcourseratingstouserplans', '1', 'core_competency'); + + helper::add_item($presetid, 'showdataretentionsummary', '1', 'tool_dataprivacy'); + helper::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. + helper::add_item($presetid, 'customusermenuitems', 'grades,grades|/grade/report/mygrades.php|t/grades +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. + helper::add_plugin($presetid, 'mod', 'chat', true); + helper::add_plugin($presetid, 'mod', 'data', true); + helper::add_plugin($presetid, 'mod', 'lti', true); + helper::add_plugin($presetid, 'mod', 'imscp', true); + helper::add_plugin($presetid, 'mod', 'lesson', true); + helper::add_plugin($presetid, 'mod', 'scorm', true); + helper::add_plugin($presetid, 'mod', 'survey', true); + helper::add_plugin($presetid, 'mod', 'wiki', true); + helper::add_plugin($presetid, 'mod', 'workshop', true); + + // Availability restrictions: Enable Grouping, User profile. + helper::add_plugin($presetid, 'availability', 'grouping', true); + helper::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. + helper::add_plugin($presetid, 'block', 'activity_modules', true); + helper::add_plugin($presetid, 'block', 'blog_menu', true); + helper::add_plugin($presetid, 'block', 'blog_tags', true); + helper::add_plugin($presetid, 'block', 'comments', true); + helper::add_plugin($presetid, 'block', 'completionstatus', true); + helper::add_plugin($presetid, 'block', 'course_summary', true); + helper::add_plugin($presetid, 'block', 'course_list', true); + helper::add_plugin($presetid, 'block', 'tag_flickr', true); + helper::add_plugin($presetid, 'block', 'globalsearch', true); + helper::add_plugin($presetid, 'block', 'badges', true); + helper::add_plugin($presetid, 'block', 'lp', true); + helper::add_plugin($presetid, 'block', 'myprofile', true); + helper::add_plugin($presetid, 'block', 'login', true); + helper::add_plugin($presetid, 'block', 'site_main_menu', true); + helper::add_plugin($presetid, 'block', 'mentees', true); + helper::add_plugin($presetid, 'block', 'mnet_hosts', true); + helper::add_plugin($presetid, 'block', 'private_files', true); + helper::add_plugin($presetid, 'block', 'blog_recent', true); + helper::add_plugin($presetid, 'block', 'rss_client', true); + helper::add_plugin($presetid, 'block', 'search_forums', true); + helper::add_plugin($presetid, 'block', 'section_links', true); + helper::add_plugin($presetid, 'block', 'selfcompletion', true); + helper::add_plugin($presetid, 'block', 'social_activities', true); + helper::add_plugin($presetid, 'block', 'tags', true); + helper::add_plugin($presetid, 'block', 'feedback', true); + + // Course formats: Enable Social format. + helper::add_plugin($presetid, 'format', 'social', true); + + // Data formats: Enable Javascript Object Notation (.json). + helper::add_plugin($presetid, 'dataformat', 'json', true); + + // Enrolments: Enable Cohort sync. + helper::add_plugin($presetid, 'enrol', 'cohort', true); + + // Filter: Enable MathJax, Activity names auto-linking. + helper::add_plugin($presetid, 'filter', 'mathjaxloader', TEXTFILTER_ON); + helper::add_plugin($presetid, 'filter', 'activitynames', TEXTFILTER_ON); + + // Question behaviours: Enable Adaptive mode (no penalties), Deferred feedback with CBM, Immediate feedback with CBM. + helper::add_plugin($presetid, 'qbehaviour', 'adaptivenopenalty', true); + helper::add_plugin($presetid, 'qbehaviour', 'deferredcbm', true); + helper::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. + helper::add_plugin($presetid, 'qtype', 'calculated', true); + helper::add_plugin($presetid, 'qtype', 'calculatedmulti', true); + helper::add_plugin($presetid, 'qtype', 'calculatedsimple', true); + helper::add_plugin($presetid, 'qtype', 'description', true); + helper::add_plugin($presetid, 'qtype', 'ddmarker', true); + helper::add_plugin($presetid, 'qtype', 'ddimageortext', true); + helper::add_plugin($presetid, 'qtype', 'multianswer', true); + helper::add_plugin($presetid, 'qtype', 'essay', true); + helper::add_plugin($presetid, 'qtype', 'numerical', true); + helper::add_plugin($presetid, 'qtype', 'randomsamatch', true); + + // Repositories: Enable Server files, URL downloader, Wikimedia. + helper::add_plugin($presetid, 'repository', 'local', true); + helper::add_plugin($presetid, 'repository', 'url', true); + helper::add_plugin($presetid, 'repository', 'wikimedia', true); + + // Text editors: Enable TinyMCE HTML editor. + helper::add_plugin($presetid, 'editor', 'tinymce', true); + +} diff --git a/admin/tool/admin_presets/db/install.xml b/admin/tool/admin_presets/db/install.xml new file mode 100755 index 0000000000000..de81e87d88c21 --- /dev/null +++ b/admin/tool/admin_presets/db/install.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+
+
diff --git a/admin/tool/admin_presets/db/upgrade.php b/admin/tool/admin_presets/db/upgrade.php new file mode 100755 index 0000000000000..0c8774ef20473 --- /dev/null +++ b/admin/tool/admin_presets/db/upgrade.php @@ -0,0 +1,38 @@ +. + +/** + * Admin tool presets plugin to load some settings. + * + * @package tool_admin_presets + * @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 + */ + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Function to upgrade tool_admin_presets. + * + * @param int $oldversion the version we are upgrading from + * @return bool result + */ +function xmldb_tool_admin_presets_upgrade($oldversion): bool { + + return true; +} diff --git a/admin/tool/admin_presets/index.php b/admin/tool/admin_presets/index.php new file mode 100755 index 0000000000000..ddce329ae07b4 --- /dev/null +++ b/admin/tool/admin_presets/index.php @@ -0,0 +1,65 @@ +. + +/** + * Admin tool presets plugin to load some settings. + * + * @package tool_admin_presets + * @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 + */ + +require_once(__DIR__ . '/../../../config.php'); + +global $CFG, $PAGE; + +$action = optional_param('action', 'base', PARAM_ALPHA); +$mode = optional_param('mode', 'show', PARAM_ALPHAEXT); + +require_login(); + +if (!$context = context_system::instance()) { + throw new moodle_exception('wrongcontext', 'error'); +} + +require_capability('moodle/site:config', $context); + +// Loads the required action class and form. +$fileclassname = $action; +$classname = 'tool_admin_presets\\local\\action\\'.$action; + +if (!class_exists($classname)) { + throw new moodle_exception('falseaction', 'tool_admin_presets', $action); +} + +$url = new moodle_url('/admin/tool/admin_presets/index.php'); +$url->param('action', $action); +$url->param('mode', $mode); +$PAGE->set_url($url); +$PAGE->set_pagelayout('admin'); +$PAGE->set_context($context); + +// Executes the required action. +$instance = new $classname(); +if (!method_exists($instance, $mode)) { + throw new moodle_exception('falsemode', 'tool_admin_presets', $mode); +} + +// Executes the required method and displays output. +$instance->$mode(); +$instance->log(); +$instance->display(); diff --git a/admin/tool/admin_presets/lang/en/tool_admin_presets.php b/admin/tool/admin_presets/lang/en/tool_admin_presets.php new file mode 100755 index 0000000000000..9367b64b94742 --- /dev/null +++ b/admin/tool/admin_presets/lang/en/tool_admin_presets.php @@ -0,0 +1,127 @@ +. + +/** + * Admin tool presets plugin to load some settings. + * + * @package tool_admin_presets + * @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 + */ + +$string['actionexport'] = 'Create preset'; +$string['actionexportbutton'] = 'Create preset'; +$string['actionimport'] = 'Import preset'; +$string['actualvalue'] = 'Actual value'; +$string['applyaction'] = 'Review settings and apply'; +$string['applypresetdescription'] = 'If you change your mind after applying this preset, you can undo the setting changes via \'Show version history\' in the preset actions menu.'; +$string['author'] = 'Author'; +$string['basedescription'] = 'Presets allow you to easily switch between different site admin configurations. After selecting a preset, you can turn on more features any time as required.'; +$string['created'] = 'Created'; +$string['currentvalue'] = 'Current value'; +$string['deletepreset'] = 'Are you sure you want to delete "{$a}" site admin preset?'; +$string['deletepreviouslyapplied'] = 'This preset has been previously applied. Deleting a preset removes it from your site completely. You will not be able to revert your settings to how they were before applying this preset'; +$string['deletepresettitle'] = 'Delete {$a} preset?'; +$string['deleteshow'] = 'Delete site admin preset'; +$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['eventpresetdeleted'] = 'Preset deleted'; +$string['eventpresetdownloaded'] = 'Preset downloaded'; +$string['eventpresetexported'] = 'Preset created'; +$string['eventpresetimported'] = 'Preset imported'; +$string['eventpresetloaded'] = 'Preset applied'; +$string['eventpresetpreviewed'] = 'Preset previewed'; +$string['eventpresetreverted'] = 'Preset restored'; +$string['eventpresetslisted'] = 'Presets have been listed'; +$string['exportdescription'] = 'Save all your current site admin settings as a preset to share or reuse.'; +$string['exportshow'] = 'Create site admin preset'; +$string['falseaction'] = 'Action not supported in this version.'; +$string['falsemode'] = 'Mode not supported in this version.'; +$string['fullpreset'] = 'Full'; +$string['fullpresetdescription'] = 'All the Starter features plus External (LTI) tool, SCORM, Workshop, Analytics, Badges, Competencies, Learning plans and lots more.'; +$string['import'] = 'Import'; +$string['imported'] = 'Imported'; +$string['importdescription'] = 'Import site admin settings as a preset to apply to your site.'; +$string['importexecute'] = 'Import site admin preset'; +$string['importshow'] = 'Import site admin preset'; +$string['includesensiblesettings'] = 'Include settings with passwords'; +$string['includesensiblesettings_help'] = 'Settings with passwords contain sensitive information specific to your site. Only include these settings if you are creating a preset to reuse on your site. You can find the list of settings with passwords in Site admin preset settings in the Site administration'; +$string['starterpreset'] = 'Starter'; +$string['starterpresetdescription'] = 'Moodle with all of the most popular features, including Assignment, Feedback, Forum, H5P, Quiz and Completion tracking.'; +$string['loaddescription'] = 'Review the setting changes before applying this preset.'; +$string['loadexecute'] = 'Site admin preset applied'; +$string['loadpreview'] = 'Preview site admin preset'; +$string['loadselected'] = 'Apply'; +$string['loadshow'] = 'Apply site admin preset'; +$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['newvalue'] = 'New value'; +$string['nopresets'] = 'You don\'t have any site admin preset.'; +$string['nosettingswillbeapplied'] = 'These settings are the same as the current settings; there are no changes to apply.'; +$string['nothingloaded'] = 'No setting changes have been made because the settings in the preset are the same as on your site.'; +$string['novalidsettings'] = 'No valid settings'; +$string['novalidsettingsselected'] = 'No valid settings selected'; +$string['oldvalue'] = 'Old value'; +$string['pluginname'] = 'Site admin presets'; +$string['presetapplicationslisttable'] = 'Site admin preset applications table'; +$string['presetslisttable'] = 'Site admin presets table'; +$string['presetmoodlerelease'] = 'Moodle release'; +$string['presetname'] = 'Preset name'; +$string['presetsettings'] = 'Preset settings'; +$string['previewpreset'] = 'Preview preset'; +$string['privacy:metadata:admin_presets'] = 'The list of configuration presets.'; +$string['privacy:metadata:admin_presets:comments'] = 'A description about the preset.'; +$string['privacy:metadata:admin_presets:moodlerelease'] = 'The Moodle release version where the preset is based on.'; +$string['privacy:metadata:admin_presets:name'] = 'The name of the preset.'; +$string['privacy:metadata:admin_presets:site'] = 'The Moodle site where this preset was created.'; +$string['privacy:metadata:admin_presets:timecreated'] = 'The time that the change was made.'; +$string['privacy:metadata:admin_presets:userid'] = 'The user who create the preset.'; +$string['privacy:metadata:tool_admin_presets_app'] = 'The configuration presets that have been applied.'; +$string['privacy:metadata:tool_admin_presets_app:adminpresetid'] = 'The id of the preset applied.'; +$string['privacy:metadata:tool_admin_presets_app:time'] = 'The time that the preset was applied.'; +$string['privacy:metadata:tool_admin_presets_app:userid'] = 'The user who applied the preset.'; +$string['renamepreset'] = 'Name (optional)'; +$string['rollback'] = 'Restore this version'; +$string['rollbackdescription'] = 'Use the \'Restore this version\' link to revert to the settings just before the preset was applied.'; +$string['rollbackexecute'] = 'Restored version from "{$a}" site admin preset'; +$string['rollbackfailures'] = 'The following settings can not be restored, the actual values differs from the values applied by the preset'; +$string['rollbackresults'] = 'Settings successfully restored'; +$string['rollbackshow'] = '{$a} preset version history'; +$string['selectfile'] = 'Select file'; +$string['sensiblesettings'] = 'Settings with passwords'; +$string['sensiblesettingstext'] = 'Settings with passwords or other sensitive information can be excluded when creating a site admin preset. Enter additional settings with format SETTINGNAME@@PLUGINNAME separated by commas.'; +$string['settingname'] = 'Setting name'; +$string['settingsapplied'] = 'Setting changes'; +$string['settingsappliednotification'] = 'Review the following setting changes which have been applied. +
If you change your mind, you can undo the setting changes via \'Show version history\' in the preset actions menu.'; +$string['settingsnotapplicable'] = 'Settings not applicable to this Moodle version'; +$string['settingsnotapplied'] = 'Unchanged settings'; +$string['settingstobeapplied'] = 'Setting changes'; +$string['showhistory'] = 'Show version history'; +$string['site'] = 'Site'; +$string['skippedchanges'] = 'Skipped settings table'; +$string['timeapplied'] = 'Date'; +$string['wrongfile'] = 'Wrong file'; +$string['wrongid'] = 'Wrong id'; diff --git a/admin/tool/admin_presets/settings.php b/admin/tool/admin_presets/settings.php new file mode 100755 index 0000000000000..7dc63082f0fcc --- /dev/null +++ b/admin/tool/admin_presets/settings.php @@ -0,0 +1,52 @@ +. + +/** + * Admin tool presets plugin to load some settings. + * + * @package tool_admin_presets + * @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 + */ + +defined('MOODLE_INTERNAL') || die(); + +if ($hassiteconfig) { + + $ADMIN->add('root', new admin_externalpage('tool_admin_presets', + get_string('pluginname', 'tool_admin_presets'), + new moodle_url('/admin/tool/admin_presets/index.php'))); + + $settings = new admin_settingpage('tool_admin_presets_settings', get_string('pluginname', 'tool_admin_presets')); + $ADMIN->add('tools', $settings); + + $sensiblesettingsdefault = 'recaptchapublickey@@none, recaptchaprivatekey@@none, googlemapkey3@@none, '; + $sensiblesettingsdefault .= 'secretphrase@@url, cronremotepassword@@none, smtpuser@@none, '; + $sensiblesettingsdefault .= 'smtppass@none, proxypassword@@none, quizpassword@@quiz, allowedip@@none, blockedip@@none, '; + $sensiblesettingsdefault .= 'dbpass@@logstore_database, messageinbound_hostpass@@none, '; + $sensiblesettingsdefault .= 'bind_pw@@auth_cas, pass@@auth_db, bind_pw@@auth_ldap, '; + $sensiblesettingsdefault .= 'dbpass@@enrol_database, bind_pw@@enrol_ldap, '; + $sensiblesettingsdefault .= 'server_password@@search_solr, ssl_keypassword@@search_solr, '; + $sensiblesettingsdefault .= 'alternateserver_password@@search_solr, alternatessl_keypassword@@search_solr, '; + $sensiblesettingsdefault .= 'test_password@@cachestore_redis, password@@mlbackend_python'; + + $settings->add(new admin_setting_configtextarea('tool_admin_presets/sensiblesettings', + get_string('sensiblesettings', 'tool_admin_presets'), + get_string('sensiblesettingstext', 'tool_admin_presets'), + $sensiblesettingsdefault, PARAM_TEXT)); + +} diff --git a/admin/tool/admin_presets/styles.css b/admin/tool/admin_presets/styles.css new file mode 100755 index 0000000000000..9cdf9207edf45 --- /dev/null +++ b/admin/tool/admin_presets/styles.css @@ -0,0 +1,24 @@ +.admin_presets_tree_name { + padding: 0 0 4px 2px; +} + +.admin_presets_tree_value { + border: 1px solid #ccc; + padding: 0 0 4px 2px; +} + +.admin_presets_error { + color: red; + text-align: center; +} + +.admin_presets_success { + color: green; + text-align: center; +} + +.adminpreset_appdescription { + font-size: small; + margin-top: 40px; + margin-bottom: 20px; +} diff --git a/admin/tool/admin_presets/templates/export_import.mustache b/admin/tool/admin_presets/templates/export_import.mustache new file mode 100644 index 0000000000000..78b773fb8cf4c --- /dev/null +++ b/admin/tool/admin_presets/templates/export_import.mustache @@ -0,0 +1,46 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template tool_admin_presets/export_import + + Template for export and import options. + + Example context (json): + { + "export": + { + "id": "button1", + "method": "get", + "url": "http://example.org/export", + "label": "Create preset", + "actions": [] + }, + "import": + { + "id": "button2", + "method": "get", + "url": "http://example.org/import", + "label": "Import preset", + "actions": [] + } + } +}} + +
+ {{#export}}{{> core/single_button}}{{/export}} + {{#import}}{{> core/single_button}}{{/import}} +
diff --git a/admin/tool/admin_presets/templates/local/tables/actual_value_settings_table.mustache b/admin/tool/admin_presets/templates/local/tables/actual_value_settings_table.mustache new file mode 100644 index 0000000000000..40c5bb57a5a4e --- /dev/null +++ b/admin/tool/admin_presets/templates/local/tables/actual_value_settings_table.mustache @@ -0,0 +1,53 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template tool_admin_presets/local/tables/actual_value_settings_table + + Template for actual setting value table. + + Example context (json): + { + "caption": "Applied settings", + "settings": [ + { + "plugin": "quiz", + "visiblename": "Decimal places in grades", + "visiblevalue": "2" + } + ] + } +}} + + + + + + + + + + + + {{#settings}} + + + + + + {{/settings}} + +
{{{caption}}}
{{#str}}settingname, tool_admin_presets{{/str}}{{#str}}plugin{{/str}}{{#str}}actualvalue, tool_admin_presets{{/str}}
{{{visiblename}}}{{{plugin}}}{{{visiblevalue}}}
diff --git a/admin/tool/admin_presets/templates/local/tables/old_and_new_value_settings_table.mustache b/admin/tool/admin_presets/templates/local/tables/old_and_new_value_settings_table.mustache new file mode 100644 index 0000000000000..1709cbfafd3b1 --- /dev/null +++ b/admin/tool/admin_presets/templates/local/tables/old_and_new_value_settings_table.mustache @@ -0,0 +1,66 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template tool_admin_presets/local/tables/old_and_new_value_settings_table + + Template for setting comparison table, showing old and new values. + + Context variables required for this template: + * beforeapplying - Whether the preset has been already applied or not. + + Example context (json): + { + "caption": "Applied settings", + "settings": [ + { + "plugin": "quiz", + "visiblename": "Decimal places in grades", + "oldvisiblevalue": "0", + "visiblevalue": "2" + } + ] + } +}} + + + + + + + + + + + + + {{#settings}} + + + + + + + {{/settings}} + +
{{{caption}}}
{{#str}}settingname, tool_admin_presets{{/str}}{{#str}}plugin{{/str}} + {{^beforeapplying}} + {{#str}}oldvalue, tool_admin_presets{{/str}} + {{/beforeapplying}} + {{#beforeapplying}} + {{#str}}currentvalue, tool_admin_presets{{/str}} + {{/beforeapplying}} + {{#str}}newvalue, tool_admin_presets{{/str}}
{{{visiblename}}}{{{plugin}}}{{{oldvisiblevalue}}}{{{visiblevalue}}}
diff --git a/admin/tool/admin_presets/templates/preset_applications_list.mustache b/admin/tool/admin_presets/templates/preset_applications_list.mustache new file mode 100644 index 0000000000000..31dc9fea93adb --- /dev/null +++ b/admin/tool/admin_presets/templates/preset_applications_list.mustache @@ -0,0 +1,64 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template tool_admin_presets/preset_applications_list + + Template for preset applications list. + + Example context (json): + { + "noapplications": 0, + "applications": { + "show": 1, + "caption": "Applied settings", + "settings": [ + { + "timeapplied": "Thursday, 9 September 2021, 3:18 PM", + "user": "Joseba Cilarte", + "action": "http://example.com/admin/tool/admin_presets/index.php?action=rollback&mode=execute&id=43" + } + ] + } + } +}} + +{{^noapplications}} +
+

{{#str}}nopresets, tool_admin_presets{{/str}}

+
+{{/noapplications}} +{{#noapplications}} + + + + + + + + + + + {{#applications}} + + + + + + {{/applications}} + +
{{#str}}presetapplicationslisttable, tool_admin_presets{{/str}}
{{#str}}timeapplied, tool_admin_presets{{/str}}{{#str}}user{{/str}}{{#str}}actions{{/str}}
{{{timeapplied}}}{{{user}}}{{#str}}rollback, tool_admin_presets{{/str}}
+{{/noapplications}} diff --git a/admin/tool/admin_presets/templates/presets_list.mustache b/admin/tool/admin_presets/templates/presets_list.mustache new file mode 100644 index 0000000000000..c582e06d8f2da --- /dev/null +++ b/admin/tool/admin_presets/templates/presets_list.mustache @@ -0,0 +1,79 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template tool_admin_presets/presets_list + + Template for presets list. + + Context variables required for this template: + * nopresets - Whether presets list is empty or not. + * showactions - Whether the actions should be displayed or not. + * presets - Presets to display in the list, with information such as name, description or author. + + Example context (json): + { + "nopresets": 0, + "showactions": 1 + } +}} + +{{#nopresets}} +
+

{{#str}}nopresets, tool_admin_presets{{/str}}

+
+{{/nopresets}} +{{^nopresets}} + + + + + + + {{^showactions}} + + + + + + {{/showactions}} + {{#showactions}} + + {{/showactions}} + + + + {{#presets}} + + + + {{^showactions}} + + + + + + {{/showactions}} + {{#showactions}} + + {{/showactions}} + + {{/presets}} + +
{{#str}}presetslisttable, tool_admin_presets{{/str}}
{{#str}}name{{/str}}{{#str}}description{{/str}}{{#str}}presetmoodlerelease, tool_admin_presets{{/str}}{{#str}}author, tool_admin_presets{{/str}}{{#str}}site, tool_admin_presets{{/str}}{{#str}}created, tool_admin_presets{{/str}}{{#str}}imported, tool_admin_presets{{/str}}
{{{name}}}{{{description}}}{{{release}}}{{{author}}}{{{site}}}{{{timecreated}}}{{{timeimported}}} + {{#actions}}{{> core/action_menu}}{{/actions}} +
+{{/nopresets}} diff --git a/admin/tool/admin_presets/templates/settings_application.mustache b/admin/tool/admin_presets/templates/settings_application.mustache new file mode 100644 index 0000000000000..dccd0ad065a3a --- /dev/null +++ b/admin/tool/admin_presets/templates/settings_application.mustache @@ -0,0 +1,59 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template tool_admin_presets/settings_application + + Template for preset loading result. + + Example context (json): + { + "nopresets": 0, + "showactions": 1 + } +}} + +{{#appliedchanges.show}} + {{#appliedchanges.message}} + {{> core/notification_success}} + {{/appliedchanges.message}} + + {{#appliedchanges.heading}} +

{{{.}}}

+ {{/appliedchanges.heading}} + + {{#appliedchanges}}{{> tool_admin_presets/local/tables/old_and_new_value_settings_table}}{{/appliedchanges}} +{{/appliedchanges.show}} +{{^appliedchanges.show}} + {{#appliedchanges}} + {{> core/notification_warning}} + {{/appliedchanges}} +{{/appliedchanges.show}} +{{#skippedchanges.show}} + {{#skippedchanges.heading}} +

+ +

+ {{/skippedchanges.heading}} + + {{#skippedchanges}} +
+ {{> tool_admin_presets/local/tables/actual_value_settings_table}} +
+ {{/skippedchanges}} +{{/skippedchanges.show}} diff --git a/admin/tool/admin_presets/templates/settings_rollback.mustache b/admin/tool/admin_presets/templates/settings_rollback.mustache new file mode 100644 index 0000000000000..9ae7b0ddbfbcf --- /dev/null +++ b/admin/tool/admin_presets/templates/settings_rollback.mustache @@ -0,0 +1,36 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template tool_admin_presets/settings_rollback + + Template for rolling preset back result. + + Example context (json): + { + "nopresets": 0, + "showactions": 1 + } +}} + +{{#appliedchanges.show}} +
{{#str}}rollbackresults, tool_admin_presets{{/str}}
+ {{#appliedchanges}}{{> tool_admin_presets/local/tables/old_and_new_value_settings_table}}{{/appliedchanges}} +{{/appliedchanges.show}} +{{#skippedchanges.show}} +
{{#str}}rollbackfailures, tool_admin_presets{{/str}}
+ {{#skippedchanges}}{{> tool_admin_presets/local/tables/old_and_new_value_settings_table}}{{/skippedchanges}} +{{/skippedchanges.show}} diff --git a/admin/tool/admin_presets/tests/behat/apply_presets.feature b/admin/tool/admin_presets/tests/behat/apply_presets.feature new file mode 100644 index 0000000000000..5f61422585bbd --- /dev/null +++ b/admin/tool/admin_presets/tests/behat/apply_presets.feature @@ -0,0 +1,77 @@ +@tool @tool_admin_presets @javascript +Feature: I can apply presets + + Background: + Given I log in as "admin" + + Scenario: Default settings are equal to Full preset + Given I navigate to "Site admin presets" in site administration + And I should see "Full" + And I open the action menu in "Full" "table_row" + When I choose "Apply" in the open action menu + Then I should not see "Setting changes" + + Scenario: Applying Starter Moodle preset changes status and settings +# Checking the settings before applying Full Moodle preset. + Given I navigate to "Plugins > Activity modules > Manage activities" in site administration + And "Hide" "icon" should exist in the "Chat" "table_row" + And I navigate to "Plugins > Availability restrictions > Manage restrictions" in site administration + And "Hide" "icon" should exist in the "Restriction by grouping" "table_row" + And I navigate to "Plugins > Blocks > Manage blocks" in site administration + And "Hide" "icon" should exist in the "Logged in user" "table_row" + And I navigate to "Plugins > Course formats > Manage course formats" in site administration + And "Disable" "icon" should exist in the "Social format" "table_row" + And I navigate to "Plugins > Question behaviours > Manage question behaviours" in site administration + And "Enabled" "icon" should exist in the "Immediate feedback with CBM" "table_row" + And I navigate to "Plugins > Question types > Manage question types" in site administration + And "Enabled" "icon" should exist in the "Calculated multichoice" "table_row" + When I navigate to "Site admin presets" in site administration + And I should see "Starter" + And I open the action menu in "Starter" "table_row" + And I choose "Apply" in the open action menu + And I should see "Setting changes" +# We are not testing all the settings, just one of each type. + And I should see "Enable badges" in the "Setting changes" "table" + And I should see "Enable competencies" in the "core_competency" "table_row" + And I should see "Chat" in the "Setting changes" "table" + And I should see "Restriction by grouping" in the "Setting changes" "table" + And I should see "Logged in user" in the "Setting changes" "table" + And I should see "Social format" in the "format" "table_row" + And I should see "Immediate feedback with CBM" in the "Setting changes" "table" + And I should see "Calculated multichoice" in the "Setting changes" "table" + And I click on "Apply" "button" + And I navigate to "Advanced features" in site administration + And the field "Enable badges" matches value "0" + And the field "Enable competencies" matches value "0" + And I navigate to "Plugins > Activity modules > Manage activities" in site administration + And "Hide" "icon" should not exist in the "Chat" "table_row" + And I navigate to "Plugins > Availability restrictions > Manage restrictions" in site administration + And "Hide" "icon" should not exist in the "Restriction by grouping" "table_row" + And I navigate to "Plugins > Blocks > Manage blocks" in site administration + And "Hide" "icon" should not exist in the "Logged in user" "table_row" + And I navigate to "Plugins > Course formats > Manage course formats" in site administration + And "Disable" "icon" should not exist in the "Social format" "table_row" + And I navigate to "Plugins > Question behaviours > Manage question behaviours" in site administration + And "Enabled" "icon" should not exist in the "Immediate feedback with CBM" "table_row" + And I navigate to "Plugins > Question types > Manage question types" in site administration + And "Enabled" "icon" should not exist in the "Calculated multichoice" "table_row" + + Scenario: Applied exported settings + Given I navigate to "Site admin presets" in site administration + And I click on "Create preset" "button" + And I set the field "Name" to "Current" + And I click on "Create preset" "button" + And I should see "Current" + And I open the action menu in "Current" "table_row" + When I choose "Apply" in the open action menu + And I should not see "Setting changes" + And I click on "Continue" "button" + And the following config values are set as admin: + | enabled | 0 | core_competency | + And I open the action menu in "Current" "table_row" + And I choose "Apply" in the open action menu + Then I should see "Setting changes" + And I should see "Enable competencies" in the "core_competency" "table_row" + And I click on "Apply" "button" + And I navigate to "Advanced features" in site administration + And the field "Enable competencies" matches value "1" diff --git a/admin/tool/admin_presets/tests/behat/behat_admin_presets.php b/admin/tool/admin_presets/tests/behat/behat_admin_presets.php new file mode 100644 index 0000000000000..23b8f9ff31a0c --- /dev/null +++ b/admin/tool/admin_presets/tests/behat/behat_admin_presets.php @@ -0,0 +1,130 @@ +. + +/** + * Steps definitions related with admin presets. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Pimenko + * @author Sylvain Revenu | Pimenko + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. + +require_once(__DIR__ . '/../../../../../lib/behat/behat_base.php'); +require_once(__DIR__ . '/../../../../../lib/behat/behat_field_manager.php'); + +use Behat\Mink\Exception\ExpectationException as ExpectationException; + +/** + * Steps definitions related with admin presets. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Pimenko + * @author Sylvain Revenu | Pimenko + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_admin_presets extends behat_base { + + /** + * Downloads the file from a specific link on the page and checks the size is in a given range. + * + * Only works if the link has an href attribute. Javascript downloads are + * not supported. Currently, the href must be an absolute URL. + * + * The range includes the endpoints. That is, a 10 byte file in considered to + * be between "5" and "10" bytes, and between "10" and "20" bytes. + * + * @Then /^following "(?P[^"]*)" "(?P[^"]*)" in the "(?P(?:[^"]|\\")*)" "(?P[^"]*)" should download between "(?P\d+)" and "(?P\d+)" bytes$/ + * @param string $link the text of the link. + * @param string $selectortype The type of what we look for + * @param string $nodeelement Element we look in + * @param string $nodeselectortype The type of selector where we look in + * @param int $minexpectedsize the minimum expected file size in bytes. + * @param int $maxexpectedsize the maximum expected file size in bytes. + * @return void + * @throws ExpectationException + */ + final public function following_in_the_should_download_between_and_bytes(string $link, string $selectortype, + string $nodeelement, string $nodeselectortype, int $minexpectedsize, int $maxexpectedsize): void { + // If the minimum is greater than the maximum then swap the values. + if ((int) $minexpectedsize > (int) $maxexpectedsize) { + list($minexpectedsize, $maxexpectedsize) = [$maxexpectedsize, $minexpectedsize]; + } + + $exception = new ExpectationException('Error while downloading data from ' . $link, $this->getSession()); + + // It will stop spinning once file is downloaded or time out. + $result = $this->spin( + function($context, $args) { + return $this->download_file_from_link_within_node($args['selectortype'], $args['link'], + $args['nodeselectortype'], $args['nodeelement']); + }, + [ + 'selectortype' => $selectortype, + 'link' => $link, + 'nodeselectortype' => $nodeselectortype, + 'nodeelement' => $nodeelement + ], + behat_base::get_extended_timeout(), + $exception + ); + + // Check download size. + $actualsize = (int) strlen($result); + if ($actualsize < $minexpectedsize || $actualsize > $maxexpectedsize) { + throw new ExpectationException('Downloaded data was ' . $actualsize . + ' bytes, expecting between ' . $minexpectedsize . ' and ' . + $maxexpectedsize, $this->getSession()); + } + } + + /** + * Given the text of a link, download the linked file and return the contents. + * + * This is a helper method used by {@see following_in_the_should_download_between_and_bytes()} + * + * @param string $selectortype The type of what we look for + * @param string $link the text of the link. + * @param string $nodeselectortype The type of selector where we look in + * @param string $nodeelement Element we look in + * @return string the content of the downloaded file. + */ + final public function download_file_from_link_within_node(string $selectortype, string $link, + string $nodeselectortype, string $nodeelement): string { + // Find the link from ur specific node. + $linknode = $this->get_node_in_container($selectortype, $link, $nodeselectortype, $nodeelement); + $this->ensure_node_is_visible($linknode); + + // Get the href and check it. + $url = $linknode->getAttribute('href'); + if (!$url) { + throw new ExpectationException('Download link does not have href attribute', + $this->getSession()); + } + if (!preg_match('~^https?://~', $url)) { + throw new ExpectationException('Download link not an absolute URL: ' . $url, + $this->getSession()); + } + + // Download the URL and check the size. + $session = $this->getSession()->getCookie('MoodleSession'); + return download_file_content($url, ['Cookie' => 'MoodleSession=' . $session]); + } +} diff --git a/admin/tool/admin_presets/tests/behat/delete.feature b/admin/tool/admin_presets/tests/behat/delete.feature new file mode 100644 index 0000000000000..db079717bca59 --- /dev/null +++ b/admin/tool/admin_presets/tests/behat/delete.feature @@ -0,0 +1,49 @@ +@tool @tool_admin_presets @javascript +Feature: Admin preset deletion + + Background: Create a preset to delete + Given I log in as "admin" + And the following "tool_admin_presets > preset" exist: + | name | + | Custom preset | + And I navigate to "Site admin presets" in site administration + + Scenario: Core preset settings can't be deleted + Given I should see "Starter" + And I should see "Full" + And I should see "Custom preset" + When I open the action menu in "Starter" "table_row" + Then I should not see "Delete" + And I open the action menu in "Starter" "table_row" + And I should not see "Delete" + And I open the action menu in "Custom preset" "table_row" + And I should see "Delete" + + Scenario: Custom preset settings can be deleted + Given I should see "Custom preset" + And I open the action menu in "Custom preset" "table_row" + When I choose "Delete" in the open action menu + And I should see "Are you sure you want to delete \"Custom preset\" site admin preset?" + And I should not see "This preset has been previously applied" + And I click on "Cancel" "button" + And I should see "Presets allow you to easily switch between different site admin configurations." + And "Custom preset" "table_row" should exist + And I open the action menu in "Custom preset" "table_row" + And I choose "Delete" in the open action menu + And I should not see "This preset has been previously applied" + And I click on "Delete" "button" + And I should see "Presets allow you to easily switch between different site admin configurations." + Then "Custom preset" "table_row" should not exist + + Scenario: Delete preset that has been applied + Given I open the action menu in "Custom preset" "table_row" + And I choose "Apply" in the open action menu + And I click on "Apply" "button" + And I navigate to "Site admin presets" in site administration + When I open the action menu in "Custom preset" "table_row" + And I choose "Delete" in the open action menu + And I should see "Are you sure you want to delete \"Custom preset\" site admin preset?" + Then I should see "This preset has been previously applied" + And I click on "Delete" "button" + And I should see "Presets allow you to easily switch between different site admin configurations" + And "Custom preset" "table_row" should not exist diff --git a/admin/tool/admin_presets/tests/behat/download.feature b/admin/tool/admin_presets/tests/behat/download.feature new file mode 100644 index 0000000000000..d17dc65655e8f --- /dev/null +++ b/admin/tool/admin_presets/tests/behat/download.feature @@ -0,0 +1,18 @@ +@tool @tool_admin_presets +Feature: I can download a preset + + Background: + Given I log in as "admin" + And the following "tool_admin_presets > preset" exist: + | name | + | Custom preset | + And I navigate to "Site admin presets" in site administration + + @javascript + Scenario: Core and custom preset settings can be downloaded + Given I log in as "admin" + And I navigate to "Site admin presets" in site administration + When I open the action menu in "Starter" "table_row" + Then following "Download" "link" in the "Starter" "table_row" should download between "0" and "5000" bytes + And I open the action menu in "Custom preset" "table_row" + And following "Download" "link" in the "Custom preset" "table_row" should download between "0" and "5000" bytes diff --git a/admin/tool/admin_presets/tests/behat/export_settings.feature b/admin/tool/admin_presets/tests/behat/export_settings.feature new file mode 100644 index 0000000000000..2781c5ce63730 --- /dev/null +++ b/admin/tool/admin_presets/tests/behat/export_settings.feature @@ -0,0 +1,30 @@ +@tool @tool_admin_presets @javascript +Feature: I can add a new preset with current settings + + Background: + Given I log in as "admin" + And I navigate to "Site admin presets" in site administration + + Scenario: Export settings with an existing name + Given I should see "Starter" + And I click on "Create preset" "button" + And I set the field "Name" to "Starter" + And I set the field "Description" to "Non-core starter preset" + When I click on "Create preset" "button" + Then I should see "Moodle with all of the most popular features" in the "Starter" "table_row" + And I should see "Starter" in the "Non-core starter preset" "table_row" + + Scenario: Export current settings + Given I click on "Create preset" "button" + And I set the field "Name" to "Current" + And I click on "Create preset" "button" + And I should see "Current" + And I open the action menu in "Current" "table_row" + When I choose "Apply" in the open action menu + And I should not see "Setting changes" + And I click on "Continue" "button" + And the following config values are set as admin: + | enableportfolios | 1 | + And I open the action menu in "Current" "table_row" + And I choose "Apply" in the open action menu + Then I should see "Setting changes" diff --git a/admin/tool/admin_presets/tests/behat/import_file.feature b/admin/tool/admin_presets/tests/behat/import_file.feature new file mode 100644 index 0000000000000..3ba22921ad597 --- /dev/null +++ b/admin/tool/admin_presets/tests/behat/import_file.feature @@ -0,0 +1,77 @@ +@tool @tool_admin_presets @_file_upload @javascript +Feature: I can upload a preset file + + Background: Go to the Import settings page + Given I log in as "admin" + And I navigate to "Site admin presets" in site administration + And I click on "Import preset" "link_or_button" + + Scenario: Import settings and plugins from a valid XML file + Given I should see "Import site admin preset" + And I click on "Import" "button" + And I should see "You must supply a value here" + And I upload "admin/tool/admin_presets/tests/fixtures/import_settings_plugins.xml" file to "Select file" filemanager + And I click on "Import" "button" + And I should see "Setting changes" + And I should see "Imported preset" + And I should see "Enable portfolios" in the "core" "table_row" + And I should see "900" in the "mod_lesson" "table_row" + When I click on "Apply" "button" + And I should see "Setting changes" + And I should see "Unchanged settings" + And I click on "Continue" "button" + Then I should see "Imported preset" in the "Site admin presets table" "table" + And I navigate to "Advanced features" in site administration + And the following fields match these values: + | Enable portfolios | 1 | + And I navigate to "Plugins > Activity modules > Lesson" in site administration + And the following fields match these values: + | Popup window width | 900 | + + Scenario: Rename imported settings + Given I should see "Import site admin preset" + And I set the field "Name" to "Renamed preset" + And I upload "admin/tool/admin_presets/tests/fixtures/import_settings_plugins.xml" file to "Select file" filemanager + And I click on "Import" "button" + And I should not see "Imported preset" + And I should see "Renamed preset" + When I click on "Apply" "button" + And I click on "Continue" "button" + Then I should not see "Imported preset" in the "Site admin presets table" "table" + And I should see "Renamed preset" in the "Site admin presets table" "table" + + Scenario: Import settings from an invalid XML file + Given I set the field "Name" to "Renamed preset" + And I upload "admin/tool/admin_presets/tests/fixtures/invalid_xml_file.xml" file to "Select file" filemanager + When I click on "Import" "button" + Then I should see "Wrong file" + And I should not see "Setting changes" + And I navigate to "Site admin presets" in site administration + And I should not see "Renamed preset" + + Scenario: Import unexisting settings category + Given I set the field "Name" to "Renamed preset" + And I upload "admin/tool/admin_presets/tests/fixtures/unexisting_category.xml" file to "Select file" filemanager + When I click on "Import" "button" + Then I should see "No valid settings" + And I should not see "Setting changes" + And I navigate to "Site admin presets" in site administration + And I should not see "Renamed preset" + + Scenario: Import one unexisting setting + Given the following config values are set as admin: + | debug | 0 | + | debugdisplay | 0 | + And I set the field "Name" to "Renamed preset" + And I upload "admin/tool/admin_presets/tests/fixtures/import_settings_with_unexisting_setting.xml" file to "Select file" filemanager + When I click on "Import" "button" + And I should see "Setting changes" + And I should see "Enable portfolios" in the "core" "table_row" + And I should not see "No valid settings" + And I click on "Apply" "button" + And I should see "Setting changes" + And I click on "Continue" "button" + Then I should see "Renamed preset" in the "Site admin presets table" "table" + And I navigate to "Advanced features" in site administration + And the following fields match these values: + | Enable portfolios | 1 | diff --git a/admin/tool/admin_presets/tests/behat/revert_changes.feature b/admin/tool/admin_presets/tests/behat/revert_changes.feature new file mode 100755 index 0000000000000..4f28570649a9a --- /dev/null +++ b/admin/tool/admin_presets/tests/behat/revert_changes.feature @@ -0,0 +1,52 @@ +@tool @tool_admin_presets @javascript +Feature: I can revert changes after a load + + Background: Apply Starter Moodle to revert it + Given I log in as "admin" + And I navigate to "Site admin presets" in site administration + And I open the action menu in "Starter" "table_row" + And I choose "Apply" in the open action menu + And I should see "Setting changes" + And I click on "Apply" "button" + And I click on "Continue" "button" + + Scenario: Presets that haven't been applied can't be reverted + Given I open the action menu in "Full" "table_row" + Then I should not see "Show version history" + + Scenario: Presets that have been applied can be reverted + # Checking applied settings before reverting them. + Given I navigate to "Advanced features" in site administration + And the field "Enable badges" matches value "0" + And the field "Enable competencies" matches value "0" + And I navigate to "Plugins > Activity modules > Manage activities" in site administration + And "Hide" "icon" should not exist in the "Chat" "table_row" + And I navigate to "Plugins > Availability restrictions > Manage restrictions" in site administration + And "Hide" "icon" should not exist in the "Restriction by grouping" "table_row" + And I navigate to "Plugins > Blocks > Manage blocks" in site administration + And "Hide" "icon" should not exist in the "Logged in user" "table_row" + And I navigate to "Plugins > Course formats > Manage course formats" in site administration + And "Disable" "icon" should not exist in the "Social format" "table_row" + And I navigate to "Plugins > Question behaviours > Manage question behaviours" in site administration + And "Enabled" "icon" should not exist in the "Immediate feedback with CBM" "table_row" + And I navigate to "Plugins > Question types > Manage question types" in site administration + And "Enabled" "icon" should not exist in the "Calculated multichoice" "table_row" + And I navigate to "Site admin presets" in site administration + And I open the action menu in "Starter" "table_row" + And I choose "Show version history" in the open action menu + When I click on "Restore this version" "link" + And I navigate to "Advanced features" in site administration + Then the field "Enable badges" matches value "1" + And the field "Enable competencies" matches value "1" + And I navigate to "Plugins > Activity modules > Manage activities" in site administration + And "Hide" "icon" should exist in the "Chat" "table_row" + And I navigate to "Plugins > Availability restrictions > Manage restrictions" in site administration + And "Hide" "icon" should exist in the "Restriction by grouping" "table_row" + And I navigate to "Plugins > Blocks > Manage blocks" in site administration + And "Hide" "icon" should exist in the "Logged in user" "table_row" + And I navigate to "Plugins > Course formats > Manage course formats" in site administration + And "Disable" "icon" should exist in the "Social format" "table_row" + And I navigate to "Plugins > Question behaviours > Manage question behaviours" in site administration + And "Enabled" "icon" should exist in the "Immediate feedback with CBM" "table_row" + And I navigate to "Plugins > Question types > Manage question types" in site administration + And "Enabled" "icon" should exist in the "Calculated multichoice" "table_row" diff --git a/admin/tool/admin_presets/tests/event/preset_deleted_test.php b/admin/tool/admin_presets/tests/event/preset_deleted_test.php new file mode 100644 index 0000000000000..251c201bf3ab0 --- /dev/null +++ b/admin/tool/admin_presets/tests/event/preset_deleted_test.php @@ -0,0 +1,61 @@ +. + +namespace tool_admin_presets\event; + +/** + * Tests for the preset_deleted event class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\event\preset_deleted + */ +class preset_deleted_test extends \advanced_testcase { + + /** + * Test preset_deleted event. + */ + public function test_preset_deleted_event() { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(); + + $params = [ + 'context' => \context_system::instance(), + 'objectid' => $presetid, + ]; + $event = preset_deleted::create($params); + + // Triggering and capturing the event. + $sink = $this->redirectEvents(); + $event->trigger(); + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + // Checking that the event contains the expected values. + $this->assertInstanceOf('\tool_admin_presets\event\preset_deleted', $event); + $this->assertEquals(\context_system::instance(), $event->get_context()); + $this->assertEquals($presetid, $event->objectid); + $expected = [0, 'tool_admin_presets', 'delete', '', $presetid, 0]; + $this->assertEventLegacyLogData($expected, $event); + } +} diff --git a/admin/tool/admin_presets/tests/event/preset_downloaded_test.php b/admin/tool/admin_presets/tests/event/preset_downloaded_test.php new file mode 100644 index 0000000000000..0162a5f2635d7 --- /dev/null +++ b/admin/tool/admin_presets/tests/event/preset_downloaded_test.php @@ -0,0 +1,59 @@ +. + +namespace tool_admin_presets\event; + +/** + * Tests for the preset_downloaded event class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\event\preset_downloaded + */ +class preset_downloaded_test extends \advanced_testcase { + + /** + * Test preset_downloaded event. + */ + public function test_preset_downloaded_event() { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(); + + $params = [ + 'context' => \context_system::instance(), + 'objectid' => $presetid, + ]; + $event = preset_downloaded::create($params); + + // Triggering and capturing the event. + $sink = $this->redirectEvents(); + $event->trigger(); + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + // Checking that the event contains the expected values. + $this->assertInstanceOf('\tool_admin_presets\event\preset_downloaded', $event); + $this->assertEquals(\context_system::instance(), $event->get_context()); + $this->assertEquals($presetid, $event->objectid); + } +} diff --git a/admin/tool/admin_presets/tests/event/preset_exported_test.php b/admin/tool/admin_presets/tests/event/preset_exported_test.php new file mode 100644 index 0000000000000..4f05e40814e71 --- /dev/null +++ b/admin/tool/admin_presets/tests/event/preset_exported_test.php @@ -0,0 +1,61 @@ +. + +namespace tool_admin_presets\event; + +/** + * Tests for the preset_exported event class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\event\preset_exported + */ +class preset_exported_test extends \advanced_testcase { + + /** + * Test preset_exported event. + */ + public function test_preset_exported_event() { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(); + + $params = [ + 'context' => \context_system::instance(), + 'objectid' => $presetid, + ]; + $event = preset_exported::create($params); + + // Triggering and capturing the event. + $sink = $this->redirectEvents(); + $event->trigger(); + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + // Checking that the event contains the expected values. + $this->assertInstanceOf('\tool_admin_presets\event\preset_exported', $event); + $this->assertEquals(\context_system::instance(), $event->get_context()); + $this->assertEquals($presetid, $event->objectid); + $expected = [0, 'tool_admin_presets', 'export', '', $presetid, 0]; + $this->assertEventLegacyLogData($expected, $event); + } +} diff --git a/admin/tool/admin_presets/tests/event/preset_imported_test.php b/admin/tool/admin_presets/tests/event/preset_imported_test.php new file mode 100644 index 0000000000000..005522fec65c5 --- /dev/null +++ b/admin/tool/admin_presets/tests/event/preset_imported_test.php @@ -0,0 +1,61 @@ +. + +namespace tool_admin_presets\event; + +/** + * Tests for the preset_imported event class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\event\preset_imported + */ +class preset_imported_test extends \advanced_testcase { + + /** + * Test preset_imported event. + */ + public function test_preset_imported_event() { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(); + + $params = [ + 'context' => \context_system::instance(), + 'objectid' => $presetid, + ]; + $event = preset_imported::create($params); + + // Triggering and capturing the event. + $sink = $this->redirectEvents(); + $event->trigger(); + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + // Checking that the event contains the expected values. + $this->assertInstanceOf('\tool_admin_presets\event\preset_imported', $event); + $this->assertEquals(\context_system::instance(), $event->get_context()); + $this->assertEquals($presetid, $event->objectid); + $expected = [0, 'tool_admin_presets', 'import', '', $presetid, 0]; + $this->assertEventLegacyLogData($expected, $event); + } +} diff --git a/admin/tool/admin_presets/tests/event/preset_loaded_test.php b/admin/tool/admin_presets/tests/event/preset_loaded_test.php new file mode 100644 index 0000000000000..82be1dda44962 --- /dev/null +++ b/admin/tool/admin_presets/tests/event/preset_loaded_test.php @@ -0,0 +1,61 @@ +. + +namespace tool_admin_presets\event; + +/** + * Tests for the preset_loaded event class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\event\preset_loaded + */ +class preset_loaded_test extends \advanced_testcase { + + /** + * Test preset_loaded event. + */ + public function test_preset_loaded_event() { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(); + + $params = [ + 'context' => \context_system::instance(), + 'objectid' => $presetid, + ]; + $event = preset_loaded::create($params); + + // Triggering and capturing the event. + $sink = $this->redirectEvents(); + $event->trigger(); + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + // Checking that the event contains the expected values. + $this->assertInstanceOf('\tool_admin_presets\event\preset_loaded', $event); + $this->assertEquals(\context_system::instance(), $event->get_context()); + $this->assertEquals($presetid, $event->objectid); + $expected = [0, 'tool_admin_presets', 'load', '', $presetid, 0]; + $this->assertEventLegacyLogData($expected, $event); + } +} diff --git a/admin/tool/admin_presets/tests/event/preset_previewed_test.php b/admin/tool/admin_presets/tests/event/preset_previewed_test.php new file mode 100644 index 0000000000000..1e275a091bfd2 --- /dev/null +++ b/admin/tool/admin_presets/tests/event/preset_previewed_test.php @@ -0,0 +1,59 @@ +. + +namespace tool_admin_presets\event; + +/** + * Tests for the preset_previewed event class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\event\preset_previewed + */ +class preset_previewed_test extends \advanced_testcase { + + /** + * Test preset_previewed event. + */ + public function test_preset_previewed_event() { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(); + + $params = [ + 'context' => \context_system::instance(), + 'objectid' => $presetid, + ]; + $event = preset_previewed::create($params); + + // Triggering and capturing the event. + $sink = $this->redirectEvents(); + $event->trigger(); + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + // Checking that the event contains the expected values. + $this->assertInstanceOf('\tool_admin_presets\event\preset_previewed', $event); + $this->assertEquals(\context_system::instance(), $event->get_context()); + $this->assertEquals($presetid, $event->objectid); + } +} diff --git a/admin/tool/admin_presets/tests/event/preset_reverted_test.php b/admin/tool/admin_presets/tests/event/preset_reverted_test.php new file mode 100644 index 0000000000000..86bfba97b47f5 --- /dev/null +++ b/admin/tool/admin_presets/tests/event/preset_reverted_test.php @@ -0,0 +1,61 @@ +. + +namespace tool_admin_presets\event; + +/** + * Tests for the preset_reverted event class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\event\preset_reverted + */ +class preset_reverted_test extends \advanced_testcase { + + /** + * Test preset_reverted event. + */ + public function test_preset_reverted_event() { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(); + + $params = [ + 'context' => \context_system::instance(), + 'objectid' => $presetid, + ]; + $event = preset_reverted::create($params); + + // Triggering and capturing the event. + $sink = $this->redirectEvents(); + $event->trigger(); + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + // Checking that the event contains the expected values. + $this->assertInstanceOf('\tool_admin_presets\event\preset_reverted', $event); + $this->assertEquals(\context_system::instance(), $event->get_context()); + $this->assertEquals($presetid, $event->objectid); + $expected = [0, 'tool_admin_presets', 'rollback', '', $presetid, 0]; + $this->assertEventLegacyLogData($expected, $event); + } +} diff --git a/admin/tool/admin_presets/tests/event/presets_listed_test.php b/admin/tool/admin_presets/tests/event/presets_listed_test.php new file mode 100644 index 0000000000000..a306ad205c98b --- /dev/null +++ b/admin/tool/admin_presets/tests/event/presets_listed_test.php @@ -0,0 +1,61 @@ +. + +namespace tool_admin_presets\event; + +/** + * Tests for the presets_listed event class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\event\presets_listed + */ +class presets_listed_test extends \advanced_testcase { + + /** + * Test presets_listed event. + */ + public function test_presets_listed_event() { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(); + + $params = [ + 'context' => \context_system::instance(), + 'objectid' => $presetid, + ]; + $event = presets_listed::create($params); + + // Triggering and capturing the event. + $sink = $this->redirectEvents(); + $event->trigger(); + $events = $sink->get_events(); + $this->assertCount(1, $events); + $event = reset($events); + + // Checking that the event contains the expected values. + $this->assertInstanceOf('\tool_admin_presets\event\presets_listed', $event); + $this->assertEquals(\context_system::instance(), $event->get_context()); + $this->assertEquals($presetid, $event->objectid); + $expected = [0, 'tool_admin_presets', 'base', '', $presetid, 0]; + $this->assertEventLegacyLogData($expected, $event); + } +} diff --git a/admin/tool/admin_presets/tests/fixtures/import_settings.xml b/admin/tool/admin_presets/tests/fixtures/import_settings.xml new file mode 100644 index 0000000000000..42d7dcabc88ee --- /dev/null +++ b/admin/tool/admin_presets/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/admin/tool/admin_presets/tests/fixtures/import_settings_plugins.xml b/admin/tool/admin_presets/tests/fixtures/import_settings_plugins.xml new file mode 100644 index 0000000000000..c09ac84cfc9d2 --- /dev/null +++ b/admin/tool/admin_presets/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/admin/tool/admin_presets/tests/fixtures/import_settings_with_unexisting_setting.xml b/admin/tool/admin_presets/tests/fixtures/import_settings_with_unexisting_setting.xml new file mode 100644 index 0000000000000..f0cecc581cce9 --- /dev/null +++ b/admin/tool/admin_presets/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/admin/tool/admin_presets/tests/fixtures/import_starter_name.xml b/admin/tool/admin_presets/tests/fixtures/import_starter_name.xml new file mode 100644 index 0000000000000..a3265077d1eb3 --- /dev/null +++ b/admin/tool/admin_presets/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/admin/tool/admin_presets/tests/fixtures/invalid_xml_file.xml b/admin/tool/admin_presets/tests/fixtures/invalid_xml_file.xml new file mode 100644 index 0000000000000..6e262822d63a2 --- /dev/null +++ b/admin/tool/admin_presets/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/admin/tool/admin_presets/tests/fixtures/unexisting_category.xml b/admin/tool/admin_presets/tests/fixtures/unexisting_category.xml new file mode 100644 index 0000000000000..47762f460eb78 --- /dev/null +++ b/admin/tool/admin_presets/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/admin/tool/admin_presets/tests/fixtures/unexisting_setting.xml b/admin/tool/admin_presets/tests/fixtures/unexisting_setting.xml new file mode 100644 index 0000000000000..984d6f01c77b9 --- /dev/null +++ b/admin/tool/admin_presets/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/admin/tool/admin_presets/tests/generator/behat_tool_admin_presets_generator.php b/admin/tool/admin_presets/tests/generator/behat_tool_admin_presets_generator.php new file mode 100644 index 0000000000000..f1bd96f9df846 --- /dev/null +++ b/admin/tool/admin_presets/tests/generator/behat_tool_admin_presets_generator.php @@ -0,0 +1,41 @@ +. + +/** + * Admin presets entity generators. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Amaia Anabitarte (amaia@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_tool_admin_presets_generator extends behat_generator_base { + + /** + * Get a list of the entities that can be created. + + * @return array entity name => information about how to generate. + */ + protected function get_creatable_entities(): array { + return [ + 'presets' => [ + 'singular' => 'preset', + 'datagenerator' => 'preset', + 'required' => [], + ], + ]; + } +} diff --git a/admin/tool/admin_presets/tests/generator/lib.php b/admin/tool/admin_presets/tests/generator/lib.php new file mode 100644 index 0000000000000..79667e4a9264a --- /dev/null +++ b/admin/tool/admin_presets/tests/generator/lib.php @@ -0,0 +1,245 @@ +. + +defined('MOODLE_INTERNAL') || die(); + +use tool_admin_presets\local\setting\admin_preset_setting; +use tool_admin_presets\manager; +use tool_admin_presets\helper; + +global $CFG; +require_once($CFG->libdir . '/adminlib.php'); + +/** + * Data generator the admin_presets tool. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tool_admin_presets_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('tool_admin_presets', $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('tool_admin_presets_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 = 'tool_admin_presets_app_it'; + if (!is_null($itemname)) { + $table = 'tool_admin_presets_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('tool_admin_presets_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 admin_preset_setting class. + * + * @param string $category Tree category name where the setting is located. + * @param string $settingname Setting name to get the class. + * @return admin_preset_setting + */ + public function get_admin_preset_setting(string $category, string $settingname): admin_preset_setting { + $adminroot = admin_get_root(); + + // Set method accessibility. + $method = new ReflectionMethod(manager::class, 'get_setting'); + $method->setAccessible(true); + + // Get the proper admin_preset_setting instance. + $settingpage = $adminroot->locate($category); + $settingdata = $settingpage->settings->$settingname; + return $method->invokeArgs(new manager(), [$settingdata, '']); + } +} diff --git a/admin/tool/admin_presets/tests/generator_test.php b/admin/tool/admin_presets/tests/generator_test.php new file mode 100644 index 0000000000000..870cf14deea27 --- /dev/null +++ b/admin/tool/admin_presets/tests/generator_test.php @@ -0,0 +1,203 @@ +. + +namespace tool_admin_presets; + +/** + * Tests for the data generator. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass tool_admin_presets_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('tool_admin_presets')->create_preset($data); + + // Check the preset data. + $preset = $DB->get_record('tool_admin_presets', ['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('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets_app', ['adminpresetid' => $presetid]); + $this->assertCount(1, $apps); + $app = reset($apps); + + // Check the applied settings. + $appsettings = $DB->get_records('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets_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/admin/tool/admin_presets/tests/helper_test.php b/admin/tool/admin_presets/tests/helper_test.php new file mode 100644 index 0000000000000..19dafa3f7c1c6 --- /dev/null +++ b/admin/tool/admin_presets/tests/helper_test.php @@ -0,0 +1,243 @@ +. + +namespace tool_admin_presets; + +/** + * Tests for the helper class. + * + * @package tool_admin_presets + * @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('tool_admin_presets', ['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('tool_admin_presets_it', ['adminpresetid' => $presetid]); + $this->assertCount(0, $settings); + $plugins = $DB->get_records('tool_admin_presets_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('tool_admin_presets', ['id' => $presetid])); + + // Add items. + $itemid = helper::add_item($presetid, $name, $value, $plugin, $advname, $advvalue); + + // Check settings have been created. + $settings = $DB->get_records('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets_it_a', ['itemid' => $itemid])); + } + + // Check no plugins have been created. + $this->assertEquals(0, $DB->count_records('tool_admin_presets_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('tool_admin_presets', ['id' => $presetid])); + + // Add plugin. + $pluginid = helper::add_plugin($presetid, $type, $name, $enabled); + + // Check plugin has been created. + $pluggins = $DB->get_records('tool_admin_presets_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('tool_admin_presets_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, + ], + ]; + } +} diff --git a/admin/tool/admin_presets/tests/local/action/base_test.php b/admin/tool/admin_presets/tests/local/action/base_test.php new file mode 100644 index 0000000000000..84125847526fb --- /dev/null +++ b/admin/tool/admin_presets/tests/local/action/base_test.php @@ -0,0 +1,149 @@ +. + +namespace tool_admin_presets\local\action; + +use ReflectionMethod; +use stdClass; + +/** + * Tests for the base class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\local\action\base + */ +class base_test extends \advanced_testcase { + + /** + * Test the behaviour of log() method. + * + * @covers ::log + * @dataProvider log_provider + * + * @param string $action Action to log. + * @param string $mode Mode to log. + * @param string|null $expectedclassname The expected classname or null if no event is expected. + */ + public function test_base_log(string $action, string $mode, ?string $expectedclassname): void { + $this->resetAfterTest(); + $this->setAdminUser(); + + // Initialise the parameters and create the class. + if (!empty($mode)) { + $_POST['mode'] = $mode; + } + if (!empty($action)) { + $_POST['action'] = $action; + } + $base = new base(); + + // Redirect events (to capture them) and call to the log method. + $sink = $this->redirectEvents(); + $base->log(); + $events = $sink->get_events(); + $sink->close(); + $event = reset($events); + + // Validate event data. + if (is_null($expectedclassname)) { + $this->assertFalse($event); + } else { + $this->assertInstanceOf('\\tool_admin_presets\\event\\' . $expectedclassname, $event); + } + } + + /** + * Data provider for test_base_log(). + * + * @return array + */ + public function log_provider(): array { + return [ + // Action = base. + 'action=base and mode = show' => [ + 'action' => 'base', + 'mode' => 'show', + 'expectedclassname' => 'presets_listed', + ], + 'action=base and mode = execute' => [ + 'action' => 'base', + 'mode' => 'execute', + 'expectedclassname' => 'presets_listed', + ], + + // Action = delete. + 'action=delete and mode = show' => [ + 'action' => 'delete', + 'mode' => 'show', + 'expectedclassname' => null, + ], + 'action=delete and mode = execute' => [ + 'action' => 'delete', + 'mode' => 'execute', + 'expectedclassname' => 'preset_deleted', + ], + 'mode = delete and action = base' => [ + 'action' => 'base', + 'mode' => 'delete', + 'expectedclassname' => 'preset_deleted', + ], + + // Action = export. + 'action=export and mode = show' => [ + 'action' => 'export', + 'mode' => 'show', + 'expectedclassname' => null, + ], + 'action=export and mode = execute' => [ + 'action' => 'export', + 'mode' => 'execute', + 'expectedclassname' => 'preset_exported', + ], + 'mode = export and action = download_xml' => [ + 'action' => 'export', + 'mode' => 'download_xml', + 'expectedclassname' => 'preset_downloaded', + ], + + // Action = load. + 'action=load and mode = show' => [ + 'action' => 'load', + 'mode' => 'show', + 'expectedclassname' => 'preset_previewed', + ], + 'action=load and mode = execute' => [ + 'action' => 'load', + 'mode' => 'execute', + 'expectedclassname' => 'preset_loaded', + ], + + // Unexisting action/method. + 'Unexisting action' => [ + 'action' => 'unexisting', + 'mode' => 'show', + 'expectedclassname' => null, + ], + 'Unexisting mode' => [ + 'action' => 'delete', + 'mode' => 'unexisting', + 'expectedclassname' => null, + ], + ]; + } +} diff --git a/admin/tool/admin_presets/tests/local/action/delete_test.php b/admin/tool/admin_presets/tests/local/action/delete_test.php new file mode 100644 index 0000000000000..a3b269a831667 --- /dev/null +++ b/admin/tool/admin_presets/tests/local/action/delete_test.php @@ -0,0 +1,150 @@ +. + +namespace tool_admin_presets\local\action; + +/** + * Tests for the delete class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\local\action\delete + */ +class delete_test extends \advanced_testcase { + + /** + * Test the behaviour of execute() method. + * + * @covers ::execute + */ + public function test_delete_execute(): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create some presets. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid1 = $generator->create_preset(['name' => 'Preset 1', 'applypreset' => true]); + $presetid2 = $generator->create_preset(['name' => 'Preset 2']); + + $currentpresets = $DB->count_records('tool_admin_presets'); + $currentitems = $DB->count_records('tool_admin_presets_it'); + $currentadvitems = $DB->count_records('tool_admin_presets_it_a'); + $currentplugins = $DB->count_records('tool_admin_presets_plug'); + + // Only preset1 has been applied. + $this->assertCount(1, $DB->get_records('tool_admin_presets_app')); + // Only the preset1 settings that have changed: enablebadges, mediawidth and maxanswers. + $this->assertCount(3, $DB->get_records('tool_admin_presets_app_it')); + // Only the preset1 advanced settings that have changed: maxanswers_adv. + $this->assertCount(1, $DB->get_records('tool_admin_presets_app_it_a')); + // Only the preset1 plugins that have changed: enrol_guest and mod_glossary. + $this->assertCount(2, $DB->get_records('tool_admin_presets_app_plug')); + + // Initialise the parameters and create the delete class. + $_POST['action'] = 'delete'; + $_POST['mode'] = 'execute'; + $_POST['id'] = $presetid1; + $_POST['sesskey'] = sesskey(); + + $action = new delete(); + $sink = $this->redirectEvents(); + try { + $action->execute(); + } catch (\exception $e) { + // If delete action was successfull, redirect should be called so we will encounter an + // 'unsupported redirect error' moodle_exception. + $this->assertInstanceOf(\moodle_exception::class, $e); + } finally { + // Check the preset data has been removed. + $presets = $DB->get_records('tool_admin_presets'); + $this->assertCount($currentpresets - 1, $presets); + $preset = reset($presets); + $this->assertArrayHasKey($presetid2, $presets); + // Check preset items. + $this->assertCount($currentitems - 4, $DB->get_records('tool_admin_presets_it')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_it', ['adminpresetid' => $presetid1])); + // Check preset advanced items. + $this->assertCount($currentadvitems - 1, $DB->get_records('tool_admin_presets_it_a')); + // Check preset plugins. + $this->assertCount($currentplugins - 3, $DB->get_records('tool_admin_presets_plug')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_plug', ['adminpresetid' => $presetid1])); + // Check preset applied tables are empty now. + $this->assertCount(0, $DB->get_records('tool_admin_presets_app')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_app_it')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_app_it_a')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_app_plug')); + + // Check the delete event has been raised. + $events = $sink->get_events(); + $sink->close(); + $event = reset($events); + $this->assertInstanceOf('\\tool_admin_presets\\event\\preset_deleted', $event); + } + } + + /** + * Test the behaviour of execute() method when the preset id doesn't exist. + * + * @covers ::execute + */ + public function test_delete_execute_unexisting_preset(): void { + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create some presets. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(['name' => 'Preset 1']); + + // Initialise the parameters and create the delete class. + $_POST['action'] = 'delete'; + $_POST['mode'] = 'execute'; + $_POST['id'] = $presetid * 2; // Unexisting preset identifier. + $_POST['sesskey'] = sesskey(); + + $action = new delete(); + $this->expectException(\moodle_exception::class); + $action->execute(); + } + + /** + * Test the behaviour of show() method when the preset id doesn't exist. + * + * @covers ::show + */ + public function test_delete_show_unexisting_preset(): void { + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create some presets. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(['name' => 'Preset 1']); + + // Initialise the parameters and create the delete class. + $_POST['action'] = 'delete'; + $_POST['mode'] = 'show'; + $_POST['id'] = $presetid * 2; // Unexisting preset identifier. + + $action = new delete(); + $this->expectException(\moodle_exception::class); + $action->show(); + } +} diff --git a/admin/tool/admin_presets/tests/local/action/export_test.php b/admin/tool/admin_presets/tests/local/action/export_test.php new file mode 100644 index 0000000000000..4b385d54fb3ba --- /dev/null +++ b/admin/tool/admin_presets/tests/local/action/export_test.php @@ -0,0 +1,177 @@ +. + +namespace tool_admin_presets\local\action; + +use tool_admin_presets\helper; + +/** + * Tests for the export class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\local\action\export + */ +class export_test extends \advanced_testcase { + + /** + * Test the behaviour of execute() method. + * @covers ::execute + * @dataProvider export_execute_provider + * + * @param bool $includesensible Whether the sensible settings should be exported too or not. + * @param string $presetname Preset name. + */ + public function test_export_execute(bool $includesensible = false, string $presetname = 'Export 1'): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Get current presets and items. + $currentpresets = $DB->count_records('tool_admin_presets'); + $currentadvitems = $DB->count_records('tool_admin_presets_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'); + + // Get the data we are submitting for the form and mock submitting it. + $formdata = [ + 'name' => $presetname, + 'comments' => ['text' => 'This is a presets for testing export'], + 'author' => 'Super-Girl', + 'includesensiblesettings' => $includesensible, + 'admin_presets_submit' => 'Save changes', + ]; + \tool_admin_presets\form\export_form::mock_submit($formdata); + + // Initialise the parameters and create the export class. + $_POST['action'] = 'export'; + $_POST['mode'] = 'execute'; + $_POST['sesskey'] = sesskey(); + + $action = new export(); + $sink = $this->redirectEvents(); + try { + $action->execute(); + } catch (\exception $e) { + // If export action was successfull, redirect should be called so we will encounter an + // 'unsupported redirect error' moodle_exception. + $this->assertInstanceOf(\moodle_exception::class, $e); + } finally { + // Check the preset record has been created. + $presets = $DB->get_records('tool_admin_presets'); + $this->assertCount($currentpresets + 1, $presets); + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->access_protected($action, 'id'); + $this->assertArrayHasKey($presetid, $presets); + $preset = $presets[$presetid]; + $this->assertEquals($presetname, $preset->name); + $this->assertEquals(0, $preset->iscore); + + // Check the items, advanced attributes and plugins have been created. + $this->assertGreaterThan(0, $DB->count_records('tool_admin_presets_it', ['adminpresetid' => $presetid])); + $this->assertGreaterThan($currentadvitems, $DB->count_records('tool_admin_presets_it_a')); + $this->assertGreaterThan(0, $DB->count_records('tool_admin_presets_plug', ['adminpresetid' => $presetid])); + + // Check settings have been created with the expected values. + $params = ['adminpresetid' => $presetid, 'plugin' => 'none', 'name' => 'enablebadges']; + $setting = $DB->get_record('tool_admin_presets_it', $params); + $this->assertEquals('0', $setting->value); + + $params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'mediawidth']; + $setting = $DB->get_record('tool_admin_presets_it', $params); + $this->assertEquals('900', $setting->value); + + $params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'maxanswers']; + $setting = $DB->get_record('tool_admin_presets_it', $params); + $this->assertEquals('2', $setting->value); + $params = ['itemid' => $setting->id, 'name' => 'maxanswers_adv']; + $setting = $DB->get_record('tool_admin_presets_it_a', $params); + $this->assertEquals('0', $setting->value); + + $params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'defaultfeedback']; + $setting = $DB->get_record('tool_admin_presets_it', $params); + $this->assertEquals('0', $setting->value); + $params = ['itemid' => $setting->id, 'name' => 'defaultfeedback_adv']; + $setting = $DB->get_record('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets_it', $params); + $params = ['adminpresetid' => $presetid, 'plugin' => 'none', 'name' => 'cronremotepassword']; + $cronsetting = $DB->get_record('tool_admin_presets_it', $params); + if ($includesensible) { + $this->assertEquals('abcde', $recaptchasetting->value); + $this->assertNotFalse($cronsetting); + } else { + $this->assertFalse($recaptchasetting); + $this->assertFalse($cronsetting); + } + + // Check the export event has been raised. + $events = $sink->get_events(); + $sink->close(); + $event = reset($events); + $this->assertInstanceOf('\\tool_admin_presets\\event\\preset_exported', $event); + } + } + + /** + * Data provider for test_export_execute(). + * + * @return array + */ + public function export_execute_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', + ], + ]; + } +} diff --git a/admin/tool/admin_presets/tests/local/action/import_test.php b/admin/tool/admin_presets/tests/local/action/import_test.php new file mode 100644 index 0000000000000..e1c7cf7bb2e43 --- /dev/null +++ b/admin/tool/admin_presets/tests/local/action/import_test.php @@ -0,0 +1,248 @@ +. + +namespace tool_admin_presets\local\action; + +/** + * Tests for the import class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\local\action\import + */ +class import_test extends \advanced_testcase { + + /** + * Test the behaviour of execute() method. + * + * @dataProvider import_execute_provider + * @covers ::execute + * + * @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_execute(string $filecontents, bool $expectedpreset, bool $expectedsettings = false, + bool $expectedplugins = false, bool $expecteddebugging = false, string $expectedexception = null, + string $expectedpresetname = 'Imported preset'): void { + global $DB, $USER; + + $this->resetAfterTest(); + $this->setAdminUser(); + + $currentpresets = $DB->count_records('tool_admin_presets'); + $currentitems = $DB->count_records('tool_admin_presets_it'); + $currentadvitems = $DB->count_records('tool_admin_presets_it_a'); + + // Create draft file to import. + $draftid = file_get_unused_draft_itemid(); + $filerecord = [ + 'component' => 'user', + 'filearea' => 'draft', + 'contextid' => \context_user::instance($USER->id)->id, 'itemid' => $draftid, + 'filename' => 'export.xml', 'filepath' => '/' + ]; + $fs = get_file_storage(); + $fs->create_file_from_string($filerecord, $filecontents); + // Get the data we are submitting for the form and mock submitting it. + $formdata = [ + 'xmlfile' => $draftid, + 'name' => '', + 'admin_presets_submit' => 'Save changes', + 'sesskey' => sesskey(), + ]; + \tool_admin_presets\form\import_form::mock_submit($formdata); + + // Initialise the parameters and create the import class. + $_POST['action'] = 'import'; + $_POST['mode'] = 'execute'; + + $action = new import(); + $sink = $this->redirectEvents(); + try { + $action->execute(); + } catch (\exception $e) { + // If import action was successfull, redirect should be called so we will encounter an + // 'unsupported redirect error' moodle_exception. + if ($expectedexception) { + $this->assertInstanceOf($expectedexception, $e); + } else { + $this->assertInstanceOf(\moodle_exception::class, $e); + } + } finally { + if ($expecteddebugging) { + $this->assertDebuggingCalled(); + } + + if ($expectedpreset) { + // Check the preset record has been created. + $presets = $DB->get_records('tool_admin_presets'); + $this->assertCount($currentpresets + 1, $presets); + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->access_protected($action, 'id'); + $this->assertArrayHasKey($presetid, $presets); + $preset = $presets[$presetid]; + $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('tool_admin_presets_it', ['adminpresetid' => $presetid]); + $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('tool_admin_presets_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('tool_admin_presets_plug', ['adminpresetid' => $presetid]); + $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('tool_admin_presets')); + $this->assertCount($currentitems, $DB->get_records('tool_admin_presets_it')); + $this->assertCount($currentadvitems, $DB->get_records('tool_admin_presets_it_a')); + } + + // Check the export event has been raised. + $events = $sink->get_events(); + $sink->close(); + $event = reset($events); + if ($expectedpreset) { + // If preset has been created, an event should be raised. + $this->assertInstanceOf('\\tool_admin_presets\\event\\preset_imported', $event); + } else { + $this->assertFalse($event); + } + } + } + + /** + * Data provider for test_import_execute(). + * + * @return array + */ + public function import_execute_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, + ], + ]; + } +} diff --git a/admin/tool/admin_presets/tests/local/action/load_test.php b/admin/tool/admin_presets/tests/local/action/load_test.php new file mode 100644 index 0000000000000..2eb191d3bd3ec --- /dev/null +++ b/admin/tool/admin_presets/tests/local/action/load_test.php @@ -0,0 +1,139 @@ +. + +namespace tool_admin_presets\local\action; + +/** + * Tests for the load class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\local\action\load + */ +class load_test extends \advanced_testcase { + + /** + * Test the behaviour of show() method when the preset id doesn't exist. + * + * @covers ::show + */ + public function test_load_show_unexisting_preset(): void { + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create some presets. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(); + + // Initialise the parameters and create the load class. + $_POST['action'] = 'load'; + $_POST['mode'] = 'view'; + $_POST['id'] = $presetid * 2; // Unexisting preset identifier. + + $action = new load(); + $this->expectException(\moodle_exception::class); + $action->show(); + } + + /** + * Test the behaviour of execute() method. + * + * @covers ::execute + */ + public function test_load_execute(): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(); + + $currentpresets = $DB->count_records('tool_admin_presets'); + $currentitems = $DB->count_records('tool_admin_presets_it'); + $currentadvitems = $DB->count_records('tool_admin_presets_it_a'); + $currentplugins = $DB->count_records('tool_admin_presets_plug'); + $currentapppresets = $DB->count_records('tool_admin_presets_app'); + $currentappitems = $DB->count_records('tool_admin_presets_app_it'); + $currentappadvitems = $DB->count_records('tool_admin_presets_app_it_a'); + $currentappplugins = $DB->count_records('tool_admin_presets_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); + + // Get the data we are submitting for the form and mock submitting it. + $formdata = [ + 'id' => $presetid, + 'admin_presets_submit' => 'Load selected settings', + ]; + \tool_admin_presets\form\load_form::mock_submit($formdata); + + // Initialise the parameters. + $_POST['action'] = 'load'; + $_POST['mode'] = 'execute'; + $_POST['id'] = $presetid; + $_POST['sesskey'] = sesskey(); + + // Create the load class and execute it. + $action = new load(); + $action->execute(); + + // Check the preset applied has been added to database. + $this->assertCount($currentapppresets + 1, $DB->get_records('tool_admin_presets_app')); + // Applied items: enablebadges@none, mediawitdh@mod_lesson and maxanswers@@mod_lesson. + $this->assertCount($currentappitems + 3, $DB->get_records('tool_admin_presets_app_it')); + // Applied advanced items: maxanswers_adv@mod_lesson. + $this->assertCount($currentappadvitems + 1, $DB->get_records('tool_admin_presets_app_it_a')); + // Applied plugins: enrol_guest and mod_glossary. + $this->assertCount($currentappplugins + 2, $DB->get_records('tool_admin_presets_app_plug')); + // Check no new preset has been created. + $this->assertCount($currentpresets, $DB->get_records('tool_admin_presets')); + $this->assertCount($currentitems, $DB->get_records('tool_admin_presets_it')); + $this->assertCount($currentadvitems, $DB->get_records('tool_admin_presets_it_a')); + $this->assertCount($currentplugins, $DB->get_records('tool_admin_presets_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); + } +} diff --git a/admin/tool/admin_presets/tests/local/action/rollback_test.php b/admin/tool/admin_presets/tests/local/action/rollback_test.php new file mode 100644 index 0000000000000..60ca9cc9cb635 --- /dev/null +++ b/admin/tool/admin_presets/tests/local/action/rollback_test.php @@ -0,0 +1,148 @@ +. + +namespace tool_admin_presets\local\action; + +/** + * Tests for the rollback class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\local\action\rollback + */ +class rollback_test extends \advanced_testcase { + + /** + * Test the behaviour of execute() method. + * + * @covers ::execute + */ + public function test_rollback_execute(): 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('tool_admin_presets'); + $presetid = $generator->create_preset(['applypreset' => true]); + $presetappid = $DB->get_field('tool_admin_presets_app', 'id', ['adminpresetid' => $presetid]); + + $currentpresets = $DB->count_records('tool_admin_presets'); + $currentitems = $DB->count_records('tool_admin_presets_it'); + $currentadvitems = $DB->count_records('tool_admin_presets_it_a'); + $currentplugins = $DB->count_records('tool_admin_presets_plug'); + $this->assertCount(1, $DB->get_records('tool_admin_presets_app')); + $this->assertCount(3, $DB->get_records('tool_admin_presets_app_it')); + $this->assertCount(1, $DB->get_records('tool_admin_presets_app_it_a')); + $this->assertCount(2, $DB->get_records('tool_admin_presets_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); + + // Initialise the parameters. + $_POST['action'] = 'rollback'; + $_POST['mode'] = 'execute'; + $_POST['id'] = $presetappid; + $_POST['sesskey'] = sesskey(); + + // Create the rollback class and execute it. + $action = new rollback(); + $action->execute(); + + // Check the preset applied has been reverted (so the records in _appXX tables have been removed). + $this->assertCount(0, $DB->get_records('tool_admin_presets_app')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_app_it')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_app_it_a')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_app_plug')); + // Check the preset data hasn't changed. + $this->assertCount($currentpresets, $DB->get_records('tool_admin_presets')); + $this->assertCount($currentitems, $DB->get_records('tool_admin_presets_it')); + $this->assertCount($currentadvitems, $DB->get_records('tool_admin_presets_it_a')); + $this->assertCount($currentplugins, $DB->get_records('tool_admin_presets_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); + } + + /** + * Test the behaviour of execute() method when the preset applied id doesn't exist. + * + * @covers ::execute + */ + public function test_rollback_execute_unexisting_presetapp(): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a preset and apply it. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $presetid = $generator->create_preset(['applypreset' => true]); + $presetappid = $DB->get_field('tool_admin_presets_app', 'id', ['adminpresetid' => $presetid]); + + // Initialise the parameters. + $_POST['action'] = 'rollback'; + $_POST['mode'] = 'execute'; + $_POST['id'] = $presetappid * 2; // Unexisting presetapp identifier. + $_POST['sesskey'] = sesskey(); + + // Create the rollback class and execute it. + $action = new rollback(); + $this->expectException(\moodle_exception::class); + $action->execute(); + } +} diff --git a/admin/tool/admin_presets/tests/local/setting/admin_preset_admin_setting_bloglevel_test.php b/admin/tool/admin_presets/tests/local/setting/admin_preset_admin_setting_bloglevel_test.php new file mode 100644 index 0000000000000..08f6ee65fff02 --- /dev/null +++ b/admin/tool/admin_presets/tests/local/setting/admin_preset_admin_setting_bloglevel_test.php @@ -0,0 +1,92 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Tests for the admin_preset_admin_setting_bloglevel class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\local\setting\admin_preset_admin_setting_bloglevel + */ +class admin_preset_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('tool_admin_presets'); + $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 admin_preset_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/admin/tool/admin_presets/tests/local/setting/admin_preset_admin_setting_sitesettext_test.php b/admin/tool/admin_presets/tests/local/setting/admin_preset_admin_setting_sitesettext_test.php new file mode 100644 index 0000000000000..ad9213f8d33c3 --- /dev/null +++ b/admin/tool/admin_presets/tests/local/setting/admin_preset_admin_setting_sitesettext_test.php @@ -0,0 +1,94 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Tests for the admin_preset_admin_setting_sitesettext class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\local\setting\admin_preset_admin_setting_sitesettext + */ +class admin_preset_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('tool_admin_presets'); + $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 admin_preset_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/admin/tool/admin_presets/tests/local/setting/admin_preset_setting_test.php b/admin/tool/admin_presets/tests/local/setting/admin_preset_setting_test.php new file mode 100644 index 0000000000000..c46592e546385 --- /dev/null +++ b/admin/tool/admin_presets/tests/local/setting/admin_preset_setting_test.php @@ -0,0 +1,198 @@ +. + +namespace tool_admin_presets\local\setting; + +/** + * Tests for the admin_preset_setting class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\local\setting\admin_preset_setting + */ +class admin_preset_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('tool_admin_presets'); + $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('tool_admin_presets'); + $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/admin/tool/admin_presets/tests/manager_test.php b/admin/tool/admin_presets/tests/manager_test.php new file mode 100644 index 0000000000000..884642b323a01 --- /dev/null +++ b/admin/tool/admin_presets/tests/manager_test.php @@ -0,0 +1,763 @@ +. + +namespace tool_admin_presets; + +use stdClass; + +/** + * Tests for the manager class. + * + * @package tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\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( + '\tool_admin_presets\local\setting\admin_preset_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( + '\tool_admin_presets\local\setting\admin_preset_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( + '\tool_admin_presets\local\setting\admin_preset_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( + '\tool_admin_presets\local\setting\admin_preset_admin_setting_sitesettext', + $result['none']['fullname'] + ); + $this->assertEquals($sitecourse->fullname, $result['none']['fullname']->get_value()); + // Config setting. + $this->assertInstanceOf( + '\tool_admin_presets\local\setting\admin_preset_admin_setting_configcheckbox', + $result['none']['enablecompletion'] + ); + $this->assertEquals(0, $result['none']['enablecompletion']->get_value()); + // Plugin config settting. + $this->assertInstanceOf( + '\tool_admin_presets\local\setting\admin_preset_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 admin_preset_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('\tool_admin_presets\local\setting\admin_preset_admin_setting_configcheckbox', $result); + $this->assertNotEquals('tool_admin_presets\local\setting\admin_preset_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('\tool_admin_presets\local\setting\admin_preset_admin_setting_configselect', $result); + $this->assertNotEquals( + 'tool_admin_presets\local\setting\admin_preset_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\local\setting\admin_preset_mod_quiz_admin_setting_browsersecurity', $result); + $this->assertNotEquals('tool_admin_presets\local\setting\admin_preset_setting', get_class($result)); + + // Check the admin_preset_setting class is returned when no specific class exists. + $settingpage = $adminroot->locate('managecustomfields'); + $settingdata = $settingpage->settings->customfieldsui;; + $result = $manager->get_setting($settingdata, ''); + $this->assertInstanceOf('\tool_admin_presets\local\setting\admin_preset_setting', $result); + $this->assertEquals('tool_admin_presets\local\setting\admin_preset_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('tool_admin_presets'); + $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('tool_admin_presets'); + $presetid = $generator->create_preset(); + + $currentpresets = $DB->count_records('tool_admin_presets'); + $currentitems = $DB->count_records('tool_admin_presets_it'); + $currentadvitems = $DB->count_records('tool_admin_presets_it_a'); + $currentplugins = $DB->count_records('tool_admin_presets_plug'); + $currentapppresets = $DB->count_records('tool_admin_presets_app'); + $currentappitems = $DB->count_records('tool_admin_presets_app_it'); + $currentappadvitems = $DB->count_records('tool_admin_presets_app_it_a'); + $currentappplugins = $DB->count_records('tool_admin_presets_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('tool_admin_presets_app')); + // Applied items: enablebadges@none, mediawitdh@mod_lesson and maxanswers@@mod_lesson. + $this->assertCount($currentappitems + 3, $DB->get_records('tool_admin_presets_app_it')); + // Applied advanced items: maxanswers_adv@mod_lesson. + $this->assertCount($currentappadvitems + 1, $DB->get_records('tool_admin_presets_app_it_a')); + // Applied plugins: enrol_guest and mod_glossary. + $this->assertCount($currentappplugins + 2, $DB->get_records('tool_admin_presets_app_plug')); + // Check no new preset has been created. + $this->assertCount($currentpresets, $DB->get_records('tool_admin_presets')); + $this->assertCount($currentitems, $DB->get_records('tool_admin_presets_it')); + $this->assertCount($currentadvitems, $DB->get_records('tool_admin_presets_it_a')); + $this->assertCount($currentplugins, $DB->get_records('tool_admin_presets_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); + } + + + /** + * 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('tool_admin_presets'); + $currentadvitems = $DB->count_records('tool_admin_presets_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('tool_admin_presets'); + $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('tool_admin_presets_it', ['adminpresetid' => $presetid])); + $this->assertGreaterThan($currentadvitems, $DB->count_records('tool_admin_presets_it_a')); + $this->assertGreaterThan(0, $DB->count_records('tool_admin_presets_plug', ['adminpresetid' => $presetid])); + + // Check settings have been created with the expected values. + $params = ['adminpresetid' => $presetid, 'plugin' => 'none', 'name' => 'enablebadges']; + $setting = $DB->get_record('tool_admin_presets_it', $params); + $this->assertEquals('0', $setting->value); + + $params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'mediawidth']; + $setting = $DB->get_record('tool_admin_presets_it', $params); + $this->assertEquals('900', $setting->value); + + $params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'maxanswers']; + $setting = $DB->get_record('tool_admin_presets_it', $params); + $this->assertEquals('2', $setting->value); + $params = ['itemid' => $setting->id, 'name' => 'maxanswers_adv']; + $setting = $DB->get_record('tool_admin_presets_it_a', $params); + $this->assertEquals('0', $setting->value); + + $params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'defaultfeedback']; + $setting = $DB->get_record('tool_admin_presets_it', $params); + $this->assertEquals('0', $setting->value); + $params = ['itemid' => $setting->id, 'name' => 'defaultfeedback_adv']; + $setting = $DB->get_record('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets_it', $params); + $params = ['adminpresetid' => $presetid, 'plugin' => 'none', 'name' => 'cronremotepassword']; + $cronsetting = $DB->get_record('tool_admin_presets_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('tool_admin_presets'); + $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('tool_admin_presets'); + $currentitems = $DB->count_records('tool_admin_presets_it'); + $currentadvitems = $DB->count_records('tool_admin_presets_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('tool_admin_presets'); + $this->assertCount($currentpresets + 1, $presets); + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $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('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets_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('tool_admin_presets')); + $this->assertCount($currentitems, $DB->get_records('tool_admin_presets_it')); + $this->assertCount($currentadvitems, $DB->get_records('tool_admin_presets_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('tool_admin_presets'); + $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('tool_admin_presets'); + $presetid1 = $generator->create_preset(['name' => 'Preset 1', 'applypreset' => true]); + $presetid2 = $generator->create_preset(['name' => 'Preset 2']); + + $currentpresets = $DB->count_records('tool_admin_presets'); + $currentitems = $DB->count_records('tool_admin_presets_it'); + $currentadvitems = $DB->count_records('tool_admin_presets_it_a'); + $currentplugins = $DB->count_records('tool_admin_presets_plug'); + + // Only preset1 has been applied. + $this->assertCount(1, $DB->get_records('tool_admin_presets_app')); + // Only the preset1 settings that have changed: enablebadges, mediawidth and maxanswers. + $this->assertCount(3, $DB->get_records('tool_admin_presets_app_it')); + // Only the preset1 advanced settings that have changed: maxanswers_adv. + $this->assertCount(1, $DB->get_records('tool_admin_presets_app_it_a')); + // Only the preset1 plugins that have changed: enrol_guest and mod_glossary. + $this->assertCount(2, $DB->get_records('tool_admin_presets_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('tool_admin_presets'); + $this->assertCount($currentpresets - 1, $presets); + $preset = reset($presets); + $this->assertArrayHasKey($presetid2, $presets); + // Check preset items. + $this->assertCount($currentitems - 4, $DB->get_records('tool_admin_presets_it')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_it', ['adminpresetid' => $presetid1])); + // Check preset advanced items. + $this->assertCount($currentadvitems - 1, $DB->get_records('tool_admin_presets_it_a')); + // Check preset plugins. + $this->assertCount($currentplugins - 3, $DB->get_records('tool_admin_presets_plug')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_plug', ['adminpresetid' => $presetid1])); + // Check preset applied tables are empty now. + $this->assertCount(0, $DB->get_records('tool_admin_presets_app')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_app_it')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_app_it_a')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_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('tool_admin_presets'); + $presetid = $generator->create_preset(['applypreset' => true]); + $presetappid = $DB->get_field('tool_admin_presets_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('tool_admin_presets'); + $presetid = $generator->create_preset(['applypreset' => true]); + $presetappid = $DB->get_field('tool_admin_presets_app', 'id', ['adminpresetid' => $presetid]); + + $currentpresets = $DB->count_records('tool_admin_presets'); + $currentitems = $DB->count_records('tool_admin_presets_it'); + $currentadvitems = $DB->count_records('tool_admin_presets_it_a'); + $currentplugins = $DB->count_records('tool_admin_presets_plug'); + $this->assertCount(1, $DB->get_records('tool_admin_presets_app')); + $this->assertCount(3, $DB->get_records('tool_admin_presets_app_it')); + $this->assertCount(1, $DB->get_records('tool_admin_presets_app_it_a')); + $this->assertCount(2, $DB->get_records('tool_admin_presets_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('tool_admin_presets_app')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_app_it')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_app_it_a')); + $this->assertCount(0, $DB->get_records('tool_admin_presets_app_plug')); + // Check the preset data hasn't changed. + $this->assertCount($currentpresets, $DB->get_records('tool_admin_presets')); + $this->assertCount($currentitems, $DB->get_records('tool_admin_presets_it')); + $this->assertCount($currentadvitems, $DB->get_records('tool_admin_presets_it_a')); + $this->assertCount($currentplugins, $DB->get_records('tool_admin_presets_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/admin/tool/admin_presets/tests/privacy_provider_test.php b/admin/tool/admin_presets/tests/privacy_provider_test.php new file mode 100644 index 0000000000000..695b60b244dd8 --- /dev/null +++ b/admin/tool/admin_presets/tests/privacy_provider_test.php @@ -0,0 +1,170 @@ +. + +namespace tool_admin_presets\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 tool_admin_presets + * @category test + * @copyright 2021 Sara Arjona (sara@moodle.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \tool_admin_presets\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('tool_admin_presets'); + $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('tool_admin_presets'); + $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('tool_admin_presets'); + $generator->create_preset(); + + $usercontext = context_user::instance($USER->id); + $userlist = new \core_privacy\local\request\userlist($usercontext, 'tool_admin_presets'); + \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('tool_admin_presets'); + $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, 'tool_admin_presets'); + $writer = \core_privacy\local\request\writer::with_context($usercontext); + + $this->assertEmpty($writer->get_data([get_string('pluginname', 'tool_admin_presets')])); + $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, 'tool_admin_presets'); + $writer = \core_privacy\local\request\writer::with_context($systemcontext); + + $this->assertEmpty($writer->get_data([get_string('pluginname', 'tool_admin_presets')])); + $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('tool_admin_presets'); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $generator->create_preset(); + $this->assertEquals($currentpresets + 1, $DB->count_records('tool_admin_presets')); + + $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('tool_admin_presets')); + } + + /** + * 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('tool_admin_presets'); + + // Create a preset. + $generator = $this->getDataGenerator()->get_plugin_generator('tool_admin_presets'); + $generator->create_preset(); + $this->assertEquals($currentpresets + 1, $DB->count_records('tool_admin_presets')); + + $usercontext = context_user::instance($USER->id); + $contextlist = new \core_privacy\local\request\approved_contextlist($USER, 'tool_admin_presets', [$usercontext->id]); + provider::delete_data_for_user($contextlist); + + // Confirm the presets haven't been removed. + $this->assertEquals($currentpresets + 1, $DB->count_records('tool_admin_presets')); + } + +} diff --git a/admin/tool/admin_presets/version.php b/admin/tool/admin_presets/version.php new file mode 100755 index 0000000000000..4036507a5034d --- /dev/null +++ b/admin/tool/admin_presets/version.php @@ -0,0 +1,30 @@ +. + +/** + * Admin tool presets plugin to load some settings. + * + * @package tool_admin_presets + * @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 + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->component = 'tool_admin_presets'; +$plugin->version = 2021111100; +$plugin->requires = 2021080500; diff --git a/auth/ldap/classes/local/setting/admin_preset_auth_ldap_admin_setting_special_contexts_configtext.php b/auth/ldap/classes/local/setting/admin_preset_auth_ldap_admin_setting_special_contexts_configtext.php new file mode 100644 index 0000000000000..435d08cd10928 --- /dev/null +++ b/auth/ldap/classes/local/setting/admin_preset_auth_ldap_admin_setting_special_contexts_configtext.php @@ -0,0 +1,60 @@ +. + +namespace auth_ldap\local\setting; + +use tool_admin_presets\local\setting\admin_preset_setting; + +/** + * Basic text setting, cleans the param using the admin_setting paramtext attribute. + * + * @package auth_ldap + * @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 admin_preset_auth_ldap_admin_setting_special_contexts_configtext extends admin_preset_setting { + + /** + * Validates the value using paramtype attribute + * + * @param string $value + * @return boolean Returned value is always true, whenever the value has been successfully cleaned or not. + */ + protected function set_value($value): bool { + + $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/lib/classes/output/icon_system_fontawesome.php b/lib/classes/output/icon_system_fontawesome.php index 19b428dcba387..8790a881a986d 100644 --- a/lib/classes/output/icon_system_fontawesome.php +++ b/lib/classes/output/icon_system_fontawesome.php @@ -412,6 +412,7 @@ public function get_core_icon_map() { 'core:t/online' => 'fa-circle', 'core:t/passwordunmask-edit' => 'fa-pencil', 'core:t/passwordunmask-reveal' => 'fa-eye', + 'core:t/play' => 'fa-play', 'core:t/portfolioadd' => 'fa-plus', 'core:t/preferences' => 'fa-wrench', 'core:t/preview' => 'fa-search-plus', diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php index f6ebe21afe8f4..8e829a720a2fd 100644 --- a/lib/classes/plugin_manager.php +++ b/lib/classes/plugin_manager.php @@ -2032,8 +2032,8 @@ public static function standard_plugins_list($type) { ), 'tool' => array( - 'analytics', 'availabilityconditions', 'behat', 'brickfield', 'capability', 'cohortroles', 'componentlibrary', - 'customlang', 'dataprivacy', 'dbtransfer', 'filetypes', 'generator', 'httpsreplace', 'innodb', + 'admin_presets', 'analytics', 'availabilityconditions', 'behat', 'brickfield', 'capability', 'cohortroles', + 'componentlibrary', 'customlang', 'dataprivacy', 'dbtransfer', 'filetypes', 'generator', 'httpsreplace', 'innodb', 'installaddon', 'langimport', 'licensemanager', 'log', 'lp', 'lpimportcsv', 'lpmigrate', 'messageinbound', 'mobile', 'moodlenet', 'multilangupgrade', 'monitor', 'oauth2', 'phpunit', 'policy', 'profiling', 'recyclebin', 'replace', 'spamcleaner', 'task', 'templatelibrary', 'uploadcourse', 'uploaduser', 'unsuproles', diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index 52b8fe4866e81..d25f206e4a67e 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -5395,9 +5395,10 @@ public function htmllize_file_tree($dir) { * @param string $message The question to ask the user * @param single_button|moodle_url|string $continue The single_button component representing the Continue answer. * @param single_button|moodle_url|string $cancel The single_button component representing the Cancel answer. + * @param array $displayoptions optional extra display options * @return string HTML fragment */ - public function confirm($message, $continue, $cancel) { + public function confirm($message, $continue, $cancel, array $displayoptions = []) { // We need plain styling of confirm boxes on upgrade because we don't know which stylesheet we have (it could be // from any previous version of Moodle). if ($continue instanceof single_button) { diff --git a/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_review_setting.php b/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_review_setting.php new file mode 100644 index 0000000000000..ec44c4b7ea89f --- /dev/null +++ b/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_review_setting.php @@ -0,0 +1,69 @@ +. + +namespace mod_quiz\local\setting; + +use ReflectionMethod; +use tool_admin_presets\local\setting\admin_preset_setting; + +/** + * Admin settings class for the quiz review options. + * + * @package mod_quiz + * @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 admin_preset_mod_quiz_admin_review_setting extends admin_preset_setting { + + /** + * Overwrite to add the reviewoptions text + */ + public function set_text() { + + $this->set_visiblevalue(); + + $name = get_string('reviewoptionsheading', 'quiz') . + ': ' . $this->settingdata->visiblename; + $namediv = '
' . $name . '
'; + $valuediv = '
' . $this->visiblevalue . '
'; + + $this->text = $namediv . $valuediv . '
'; + } + + /** + * The setting value is a sum of 'mod_quiz_admin_review_setting::times' + */ + protected function set_visiblevalue() { + + // Getting the masks descriptions (mod_quiz_admin_review_setting protected method). + $reflectiontimes = new ReflectionMethod('mod_quiz_admin_review_setting', 'times'); + $reflectiontimes->setAccessible(true); + $times = $reflectiontimes->invoke(null); + + $visiblevalue = ''; + foreach ($times as $timemask => $namestring) { + + // If the value is checked. + if ($this->value & $timemask) { + $visiblevalue .= $namestring . ', '; + } + } + $visiblevalue = rtrim($visiblevalue, ', '); + + $this->visiblevalue = $visiblevalue; + } +} diff --git a/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_browsersecurity.php b/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_browsersecurity.php new file mode 100644 index 0000000000000..c16641ee727ad --- /dev/null +++ b/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_browsersecurity.php @@ -0,0 +1,34 @@ +. + +namespace mod_quiz\local\setting; + +use tool_admin_presets\local\setting\admin_preset_admin_setting_configselect_with_advanced; + +/** + * Admin settings class for the quiz browser security option. + * + * @package mod_quiz + * @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 admin_preset_mod_quiz_admin_setting_browsersecurity extends admin_preset_admin_setting_configselect_with_advanced { + + public function set_behaviors() { + $this->behaviors['loadchoices'] = &$this->settingdata; + } +} diff --git a/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_grademethod.php b/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_grademethod.php new file mode 100644 index 0000000000000..3f64e525186e8 --- /dev/null +++ b/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_grademethod.php @@ -0,0 +1,34 @@ +. + +namespace mod_quiz\local\setting; + +use tool_admin_presets\local\setting\admin_preset_admin_setting_configselect_with_advanced; + +/** + * Admin settings class for the quiz grading method. + * + * @package mod_quiz + * @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 admin_preset_mod_quiz_admin_setting_grademethod extends admin_preset_admin_setting_configselect_with_advanced { + + public function set_behaviors() { + $this->behaviors['loadchoices'] = &$this->settingdata; + } +} diff --git a/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_overduehandling.php b/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_overduehandling.php new file mode 100644 index 0000000000000..e284ff711ab4c --- /dev/null +++ b/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_overduehandling.php @@ -0,0 +1,34 @@ +. + +namespace mod_quiz\local\setting; + +use tool_admin_presets\local\setting\admin_preset_admin_setting_configselect_with_advanced; + +/** + * Admin settings class for the quiz overdue attempt handling method. + * + * @package mod_quiz + * @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 admin_preset_mod_quiz_admin_setting_overduehandling extends admin_preset_admin_setting_configselect_with_advanced { + + public function set_behaviors() { + $this->behaviors['loadchoices'] = &$this->settingdata; + } +} diff --git a/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_user_image.php b/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_user_image.php new file mode 100644 index 0000000000000..0dc8268373a3d --- /dev/null +++ b/mod/quiz/classes/local/setting/admin_preset_mod_quiz_admin_setting_user_image.php @@ -0,0 +1,34 @@ +. + +namespace mod_quiz\local\setting; + +use tool_admin_presets\local\setting\admin_preset_admin_setting_configselect_with_advanced; + +/** + * Admin settings class for the choices for how to display the user's image. + * + * @package mod_quiz + * @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 admin_preset_mod_quiz_admin_setting_user_image extends admin_preset_admin_setting_configselect_with_advanced { + + public function set_behaviors() { + $this->behaviors['loadchoices'] = &$this->settingdata; + } +} diff --git a/pix/t/play.png b/pix/t/play.png new file mode 100644 index 0000000000000..756464af05af9 Binary files /dev/null and b/pix/t/play.png differ diff --git a/pix/t/play.svg b/pix/t/play.svg new file mode 100644 index 0000000000000..967a5a2d0b385 --- /dev/null +++ b/pix/t/play.svg @@ -0,0 +1,6 @@ + + + + + +