first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,44 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\event;
use core\event\base;
/**
* Admin tool presets event class deleted.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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}.";
}
protected function init(): void {
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'adminpresets';
}
}
@@ -0,0 +1,49 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\event;
use core\event\base;
/**
* Admin tool presets event class downloaded.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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'] = 'adminpresets';
}
}
@@ -0,0 +1,49 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\event;
use core\event\base;
/**
* Admin tool presets event class exported.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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]);
}
protected function init(): void {
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'adminpresets';
}
}
@@ -0,0 +1,49 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\event;
use core\event\base;
/**
* Admin tool presets event class imported.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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]);
}
protected function init(): void {
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'adminpresets';
}
}
@@ -0,0 +1,49 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\event;
use core\event\base;
/**
* Admin tool presets event class loaded.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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]);
}
protected function init(): void {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'adminpresets';
}
}
@@ -0,0 +1,49 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\event;
use core\event\base;
/**
* Admin tool presets event class previewed.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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'] = 'adminpresets';
}
}
@@ -0,0 +1,44 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\event;
use core\event\base;
/**
* Admin tool presets event class reverted.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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}.";
}
protected function init(): void {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'adminpresets';
}
}
@@ -0,0 +1,48 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\event;
use core\event\base;
/**
* Admin tool presets event class listed.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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');
}
protected function init(): void {
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'adminpresets';
}
}
@@ -0,0 +1,38 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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 <amaia@moodle.com>
* @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'));
}
}
@@ -0,0 +1,61 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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 <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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'));
}
}
@@ -0,0 +1,51 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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 <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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'), null,
['accepted_types' => ['.xml']]);
$mform->addRule('xmlfile', null, 'required');
$this->add_action_buttons(true, get_string('import', 'tool_admin_presets'));
}
}
@@ -0,0 +1,47 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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 <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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 = '<div class="alert alert-info">' . get_string('applypresetdescription', 'tool_admin_presets') . '</div>';
$mform->addElement('html', $html);
$this->add_action_buttons(true, get_string('loadselected', 'tool_admin_presets'));
}
}
@@ -0,0 +1,209 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\local\action;
use context_system;
use moodle_url;
use core_adminpresets\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 <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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 \moodleform 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('adminpresets');
$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);
$title = $this->get_title();
$text = $this->get_explanatory_description();
// Only add it to the navbar if it's different to the plugin name (to avoid duplicates in the navbar).
if ($title != get_string('pluginname', 'tool_admin_presets')) {
$PAGE->navbar->add($title);
}
if ($node = $PAGE->settingsnav->find('tool_admin_presets', \navigation_node::TYPE_SETTING)) {
$node->make_active();
}
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();
}
}
}
}
@@ -0,0 +1,78 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\local\action;
use core_adminpresets\manager;
use moodle_exception;
/**
* This class extends base class and handles delete function.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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;
// Check the preset exists (cannot delete the pre-installed core "Starter" and "Full" presets).
$presetdata = $DB->get_record('adminpresets', ['id' => $this->id, 'iscore' => manager::NONCORE_PRESET], '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('adminpresets_app', ['adminpresetid' => $this->id])) {
$deletetext .= '<p><strong>' .
get_string("deletepreviouslyapplied", "tool_admin_presets") . '</strong></p>';
}
$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', 'core_adminpresets');
}
}
/**
* Delete the DB preset
*/
public function execute(): void {
require_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);
}
}
@@ -0,0 +1,101 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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 <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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 {
$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 {
require_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');
}
}
@@ -0,0 +1,88 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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 <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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 {
$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;
}
}
@@ -0,0 +1,176 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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 <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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;
$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 {
$this->display_preset(true);
}
/**
* Displays a preset information (name, description, settings different from the current configuration...).
*/
public function preview(): void {
$this->display_preset(false, false);
}
/**
* Method to prepare the information to preview/load the preset.
*
* @param bool $displayform Whether the form should be displayed in the page or not.
* @param bool $raiseexception Whether the exception should be raised or not when the preset doesn't exist. When it's set
* to false, a message is displayed, instead of raising the exception.
*/
protected function display_preset(bool $displayform = true, bool $raiseexception = true) {
global $DB, $OUTPUT;
$data = new stdClass();
$data->id = $this->id;
// Preset data.
if (!$preset = $DB->get_record('adminpresets', ['id' => $data->id])) {
if ($raiseexception) {
throw new moodle_exception('errornopreset', 'core_adminpresets');
} else {
$this->outputs = get_string('errornopreset', 'core_adminpresets');
return;
}
}
// 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 ($displayform) {
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;
}
}
@@ -0,0 +1,124 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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 <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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('adminpresets', ['id' => $this->id]);
// Applications data.
$context = new stdClass();
$applications = $DB->get_records('adminpresets_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' => \core_date::strftime($format, (int)$application->time),
'user' => fullname($user),
'action' => $rollbacklink->out(false),
];
}
$this->outputs .= '<br/>' . $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;
require_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('adminpresets', ['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;
}
}
@@ -0,0 +1,54 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* tool_admin_presets export and import option renderer
*
* @package tool_admin_presets
* @copyright 2021 Amaia Anabitarte <amaia@moodle.com>
* @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 <amaia@moodle.com>
* @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;
}
}
@@ -0,0 +1,139 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* tool_admin_presets specific renderers
*
* @package tool_admin_presets
* @copyright 2021 Amaia Anabitarte <amaia@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_admin_presets\output;
use core_adminpresets\manager;
use renderable;
use templatable;
use renderer_base;
use stdClass;
/**
* Class containing data for admin_presets tool
*
* @copyright 2021 Amaia Anabitarte <amaia@moodle.com>
* @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);
$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 == manager::NONCORE_PRESET) {
$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('adminpresets_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;
}
}
@@ -0,0 +1,37 @@
<?php
// This file is part of The Course Module Navigation Block
//
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\privacy;
/**
* Admin tool presets this file handle privacy provider.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> code
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
+66
View File
@@ -0,0 +1,66 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Admin tool presets plugin to load some settings.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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);
$PAGE->set_primary_active_tab('siteadminnode');
// 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();
@@ -0,0 +1,95 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Admin tool presets plugin to load some settings.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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 the site admin preset {$a}?';
$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['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['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.';
$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['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['privacy:metadata'] = 'The Site admin presets tool does not store any personal data.';
$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 the site admin preset {$a}';
$string['rollbackfailures'] = 'The following settings cannot be reverted, as the value was changed after applying the preset.';
$string['rollbackresults'] = 'Settings successfully restored';
$string['rollbackshow'] = '{$a} preset version history';
$string['selectfile'] = 'Select file';
$string['settingname'] = 'Setting name';
$string['settingsapplied'] = 'Setting changes';
$string['settingsappliednotification'] = 'Review the following setting changes which have been applied.
<br/>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['timeapplied'] = 'Date';
$string['wrongfile'] = 'Wrong file';
+32
View File
@@ -0,0 +1,32 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Admin tool presets plugin to load some settings.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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')));
}
+15
View File
@@ -0,0 +1,15 @@
.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;
}
@@ -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 <http://www.gnu.org/licenses/>.
}}
{{!
@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": []
}
}
}}
<div class="mt-2 mb-1 float-right">
{{#export}}{{> core/single_button}}{{/export}}
{{#import}}{{> core/single_button}}{{/import}}
</div>
@@ -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 <http://www.gnu.org/licenses/>.
}}
{{!
@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"
}
]
}
}}
<table class="generaltable boxaligncenter admin_presets_skipped">
<caption class="accesshide">{{{caption}}}</caption>
<thead>
<tr>
<th scope="col">{{#str}}settingname, tool_admin_presets{{/str}}</th>
<th scope="col">{{#str}}plugin{{/str}}</th>
<th scope="col">{{#str}}actualvalue, tool_admin_presets{{/str}}</th>
</tr>
</thead>
<tbody>
{{#settings}}
<tr>
<td>{{{visiblename}}}</td>
<td>{{{plugin}}}</td>
<td>{{{visiblevalue}}}</td>
</tr>
{{/settings}}
</tbody>
</table>
@@ -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 <http://www.gnu.org/licenses/>.
}}
{{!
@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"
}
]
}
}}
<table class="generaltable boxaligncenter admin_presets_applied">
<caption class="accesshide">{{{caption}}}</caption>
<thead>
<tr>
<th scope="col" class="w-50">{{#str}}settingname, tool_admin_presets{{/str}}</th>
<th scope="col" class="w-25">{{#str}}plugin{{/str}}</th>
<th scope="col">
{{^beforeapplying}}
{{#str}}oldvalue, tool_admin_presets{{/str}}
{{/beforeapplying}}
{{#beforeapplying}}
{{#str}}currentvalue, tool_admin_presets{{/str}}
{{/beforeapplying}}
</th>
<th scope="col">{{#str}}newvalue, tool_admin_presets{{/str}}</th>
</tr>
</thead>
<tbody>
{{#settings}}
<tr>
<td>{{{visiblename}}}</td>
<td>{{{plugin}}}</td>
<td>{{{oldvisiblevalue}}}</td>
<td>{{{visiblevalue}}}</td>
</tr>
{{/settings}}
</tbody>
</table>
@@ -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 <http://www.gnu.org/licenses/>.
}}
{{!
@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}}
<div id="id_nopresets" class="box py-3 generalbox">
<p id="{{uniqid}}">{{#str}}nopresets, tool_admin_presets{{/str}}</p>
</div>
{{/noapplications}}
{{#noapplications}}
<table class="generaltable boxaligncenter">
<caption class="accesshide">{{#str}}presetapplicationslisttable, tool_admin_presets{{/str}}</caption>
<thead>
<tr>
<th scope="col">{{#str}}timeapplied, tool_admin_presets{{/str}}</th>
<th scope="col">{{#str}}user{{/str}}</th>
<th scope="col">{{#str}}actions{{/str}}</th>
</tr>
</thead>
<tbody>
{{#applications}}
<tr>
<td>{{{timeapplied}}}</td>
<td>{{{user}}}</td>
<td><a href="{{action}}">{{#str}}rollback, tool_admin_presets{{/str}}</a></td>
</tr>
{{/applications}}
</tbody>
</table>
{{/noapplications}}
@@ -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 <http://www.gnu.org/licenses/>.
}}
{{!
@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}}
<div id="id_nopresets" class="box py-3 generalbox">
<p id="{{uniqid}}">{{#str}}nopresets, tool_admin_presets{{/str}}</p>
</div>
{{/nopresets}}
{{^nopresets}}
<table class="generaltable boxaligncenter mb-5">
<caption class="accesshide">{{#str}}presetslisttable, tool_admin_presets{{/str}}</caption>
<thead>
<tr>
<th scope="col">{{#str}}name{{/str}}</th>
<th scope="col">{{#str}}description{{/str}}</th>
{{^showactions}}
<th scope="col">{{#str}}presetmoodlerelease, tool_admin_presets{{/str}}</th>
<th scope="col">{{#str}}author, tool_admin_presets{{/str}}</th>
<th scope="col">{{#str}}site, tool_admin_presets{{/str}}</th>
<th scope="col">{{#str}}created, tool_admin_presets{{/str}}</th>
<th scope="col">{{#str}}imported, tool_admin_presets{{/str}}</th>
{{/showactions}}
{{#showactions}}
<th scope="col" aria-label="{{#str}}actions{{/str}}"></th>
{{/showactions}}
</tr>
</thead>
<tbody>
{{#presets}}
<tr>
<td>{{{name}}}</td>
<td>{{{description}}}</td>
{{^showactions}}
<td>{{{release}}}</td>
<td>{{{author}}}</td>
<td>{{{site}}}</td>
<td>{{{timecreated}}}</td>
<td>{{{timeimported}}}</td>
{{/showactions}}
{{#showactions}}
<td>
{{#actions}}{{> core/action_menu}}{{/actions}}
</td>
{{/showactions}}
</tr>
{{/presets}}
</tbody>
</table>
{{/nopresets}}
@@ -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 <http://www.gnu.org/licenses/>.
}}
{{!
@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}}
<h3>{{{.}}}</h3>
{{/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}}
<h3>
<a data-toggle="collapse" href="#collapseSkippedChanges" role="button" aria-expanded="false" aria-controls="collapseSkippedChanges">
{{{.}}}
</a>
</h3>
{{/skippedchanges.heading}}
{{#skippedchanges}}
<div class="collapse" id="collapseSkippedChanges">
{{> tool_admin_presets/local/tables/actual_value_settings_table}}
</div>
{{/skippedchanges}}
{{/skippedchanges.show}}
@@ -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 <http://www.gnu.org/licenses/>.
}}
{{!
@template tool_admin_presets/settings_rollback
Template for rolling preset back result.
Example context (json):
{
"nopresets": 0,
"showactions": 1
}
}}
{{#appliedchanges.show}}
<div class="alert alert-success">{{#str}}rollbackresults, tool_admin_presets{{/str}}</div>
{{#appliedchanges}}{{> tool_admin_presets/local/tables/old_and_new_value_settings_table}}{{/appliedchanges}}
{{/appliedchanges.show}}
{{#skippedchanges.show}}
<div class="alert alert-info">{{#str}}rollbackfailures, tool_admin_presets{{/str}}</div>
{{#skippedchanges}}{{> tool_admin_presets/local/tables/old_and_new_value_settings_table}}{{/skippedchanges}}
{{/skippedchanges.show}}
@@ -0,0 +1,154 @@
@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 "Review settings and 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 (we're only testing one of each type).
Given I navigate to "Plugins > Activity modules > Manage activities" in site administration
And "Disable Database" "icon" should exist in the "Database" "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 "Disable Logged in user" "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" "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 "Review settings and apply" in the open action menu
And I should see "Setting changes"
# Checking all the settings to be applied for the Starter (if will help to identify possible regressions).
And I should see "Activities" in the "Setting changes" "table"
And I should see "Activity chooser tabs" in the "Setting changes" "table"
And I should see "Adaptive mode (no penalties)" in the "Setting changes" "table"
And I should see "Analytics" in the "Setting changes" "table"
And I should see "Blog menu" in the "Setting changes" "table"
And I should see "Blog tags" in the "Setting changes" "table"
And I should see "Calculated" in the "Setting changes" "table"
And I should see "Calculated multichoice" in the "Setting changes" "table"
And I should see "Calculated simple" in the "Setting changes" "table"
And I should see "Database" in the "Setting changes" "table"
And "Database" row "Current value" column of "Setting changes" table should contain "Enabled"
And "Database" row "New value" column of "Setting changes" table should contain "Disabled"
And I should see "Cohort sync" in the "Setting changes" "table"
And I should see "Comments" in the "Setting changes" "table"
And I should see "Course completion status" in the "Setting changes" "table"
And I should see "Courses" in the "Setting changes" "table"
And I should see "Database" in the "Setting changes" "table"
And I should see "Deferred feedback with CBM" in the "Setting changes" "table"
And I should see "Drag and drop markers" in the "Setting changes" "table"
And I should see "Drag and drop onto image" in the "Setting changes" "table"
And I should see "Embedded answers (Cloze)" in the "Setting changes" "table"
And I should see "Enable badges" in the "Setting changes" "table"
And "Enable badges" row "Current value" column of "Setting changes" table should contain "Yes"
And "Enable badges" row "New value" column of "Setting changes" table should contain "No"
And I should see "Enable blogs" in the "Setting changes" "table"
And I should see "Enable comments" in the "Setting changes" "table"
And I should see "Enable competencies" in the "core_competency" "table_row"
And I should see "Enable notes" in the "Setting changes" "table"
And I should see "Enable tags functionality" in the "Setting changes" "table"
And I should see "External tool" in the "Setting changes" "table"
And I should see "Flickr" in the "Setting changes" "table"
And I should see "Global search" in the "Setting changes" "table"
And I should see "Guest access" in the "Setting changes" "table"
And I should see "Guest login button" in the "Setting changes" "table"
And I should see "IMS content package" in the "Setting changes" "table"
And I should see "Immediate feedback with CBM" in the "Setting changes" "table"
And I should see "Latest badges" in the "Setting changes" "table"
And I should see "Learning plans" in the "Setting changes" "table"
And I should see "Lesson" in the "Setting changes" "table"
And I should see "Logged in user" in the "Setting changes" "table"
And I should see "Login" in the "Setting changes" "table"
And I should see "Main menu" in the "Setting changes" "table"
And I should see "Maximum number of attachments" in the "Setting changes" "table"
And "Maximum number of attachments" row "Current value" column of "Setting changes" table should contain "9"
And "Maximum number of attachments" row "New value" column of "Setting changes" table should contain "3"
And I should see "Mentees" in the "Setting changes" "table"
And I should see "Network servers" in the "Setting changes" "table"
And I should see "Numerical" in the "Setting changes" "table"
And I should see "Online users" in the "Setting changes" "table"
And I should see "Private files" in the "Setting changes" "table"
And I should see "Random short-answer matching" in the "Setting changes" "table"
And I should see "Recent blog entries" in the "Setting changes" "table"
And I should see "Recently accessed courses" in the "Setting changes" "table"
And I should see "Restriction by grouping" in the "Setting changes" "table"
And I should see "Restriction by profile" in the "Setting changes" "table"
And I should see "SCORM package" in the "Setting changes" "table"
And I should see "Search forums" in the "Setting changes" "table"
And I should see "Section links" in the "Setting changes" "table"
And I should see "Server files" in the "Setting changes" "table"
And I should see "Show data retention summary" in the "Setting changes" "table"
And I should see "Social activities" in the "Setting changes" "table"
And I should see "Social" in the "Setting changes" "table"
And I should see "Starred courses" in the "Setting changes" "table"
And I should see "Lesson" in the "Setting changes" "table"
And I should see "Tags" in the "Setting changes" "table"
And I should see "URL downloader" in the "Setting changes" "table"
And I should see "Wiki" in the "Setting changes" "table"
And I should see "Wikimedia" in the "Setting changes" "table"
And I should see "Workshop" in the "Setting changes" "table"
# Check filters and data formats strings are displayed properly.
And I should see "Activity names auto-linking" in the "Setting changes" "table"
And I should see "Javascript Object Notation (.json)" in the "Setting changes" "table"
# Apply the Starter preset.
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 "Disable Database" "icon" should not exist in the "Database" "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 "Disable Logged in user" "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" "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: Re-applying Starter Moodle preset does not display setting changes
# Apply Starter preset.
Given I navigate to "Site admin presets" in site administration
And I open the action menu in "Starter" "table_row"
And I choose "Review settings and apply" in the open action menu
And I click on "Apply" "button"
And I click on "Continue" "button"
# When the Starter preset it's applied again, no changes should be displayed.
When I open the action menu in "Starter" "table_row"
And I choose "Review settings and apply" in the open action menu
Then I should not see "Setting changes"
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 "Review settings and 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 "Review settings and 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"
@@ -0,0 +1,130 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Steps definitions related with admin presets.
*
* @package tool_admin_presets
* @category test
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @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 <support@pimenko.com><pimenko.com>
* @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<link_string>[^"]*)" "(?P<selector_string>[^"]*)" in the "(?P<element_container_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)" should download between "(?P<min_bytes>\d+)" and "(?P<max_bytes>\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]);
}
}
@@ -0,0 +1,51 @@
@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 "Custom preset" "table_row"
And I should see "Delete"
And I press the escape key
And I open the action menu in "Full" "table_row"
Then I should not see "Delete"
And I press the escape key
And I open the action menu in "Starter" "table_row"
And I should not 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 the site admin preset Custom 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 "Review settings and 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 the site admin preset Custom 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
@@ -0,0 +1,22 @@
@tool @tool_admin_presets
Feature: I can download a preset
Background:
Given the following "tool_admin_presets > preset" exist:
| name |
| Custom preset |
Scenario: 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 "Custom preset" "table_row"
Then following "Download" in the "Custom preset" "table_row" should download a file that:
| Has mimetype | text/xml |
| Contains text in xml element | Custom preset |
Scenario: Core 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" in the "Starter" "table_row" should download a file that:
| Has mimetype | text/xml |
| Contains text in xml element | Starter |
@@ -0,0 +1,33 @@
@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 the following should exist in the "Site admin presets table" table:
| Name | Description |
| Starter | Moodle with all of the most popular features, including Assignment, Feedback, Forum, H5P, Quiz and Completion tracking. |
| Full | All the Starter features plus External (LTI) tool, SCORM, Workshop, Analytics, Badges, Competencies, Learning plans and lots more. |
| Starter | Non-core starter preset |
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 "Review settings and 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 "Review settings and apply" in the open action menu
Then I should see "Setting changes"
@@ -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/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/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/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/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/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 |
@@ -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 "Review settings and 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 "Disable Database" "icon" should not exist in the "Database" "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 "Disable Logged in user" "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" "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 "Disable Database" "icon" should exist in the "Database" "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 "Disable Logged in user" "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" "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"
@@ -0,0 +1,59 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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
* @covers \tool_admin_presets\event\preset_deleted
*/
class preset_deleted_test extends \advanced_testcase {
/**
* Test preset_deleted event.
*/
public function test_preset_deleted_event(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create a preset.
$generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets');
$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);
}
}
@@ -0,0 +1,59 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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
* @covers \tool_admin_presets\event\preset_downloaded
*/
class preset_downloaded_test extends \advanced_testcase {
/**
* Test preset_downloaded event.
*/
public function test_preset_downloaded_event(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create a preset.
$generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets');
$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);
}
}
@@ -0,0 +1,59 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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
* @covers \tool_admin_presets\event\preset_exported
*/
class preset_exported_test extends \advanced_testcase {
/**
* Test preset_exported event.
*/
public function test_preset_exported_event(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create a preset.
$generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets');
$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);
}
}
@@ -0,0 +1,59 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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
* @covers \tool_admin_presets\event\preset_imported
*/
class preset_imported_test extends \advanced_testcase {
/**
* Test preset_imported event.
*/
public function test_preset_imported_event(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create a preset.
$generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets');
$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);
}
}
@@ -0,0 +1,59 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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
* @covers \tool_admin_presets\event\preset_loaded
*/
class preset_loaded_test extends \advanced_testcase {
/**
* Test preset_loaded event.
*/
public function test_preset_loaded_event(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create a preset.
$generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets');
$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);
}
}
@@ -0,0 +1,59 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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
* @covers \tool_admin_presets\event\preset_previewed
*/
class preset_previewed_test extends \advanced_testcase {
/**
* Test preset_previewed event.
*/
public function test_preset_previewed_event(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create a preset.
$generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets');
$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);
}
}
@@ -0,0 +1,59 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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
* @covers \tool_admin_presets\event\preset_reverted
*/
class preset_reverted_test extends \advanced_testcase {
/**
* Test preset_reverted event.
*/
public function test_preset_reverted_event(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create a preset.
$generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets');
$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);
}
}
@@ -0,0 +1,59 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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
* @covers \tool_admin_presets\event\presets_listed
*/
class presets_listed_test extends \advanced_testcase {
/**
* Test presets_listed event.
*/
public function test_presets_listed_event(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create a preset.
$generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets');
$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);
}
}
@@ -0,0 +1,41 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* 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' => [],
],
];
}
}
@@ -0,0 +1,32 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/admin/presets/tests/generator/lib.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 \core_adminpresets_generator {
}
@@ -0,0 +1,146 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\local\action;
/**
* 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,
],
];
}
}
@@ -0,0 +1,150 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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('core_adminpresets');
$presetid1 = $generator->create_preset(['name' => 'Preset 1', 'applypreset' => true]);
$presetid2 = $generator->create_preset(['name' => 'Preset 2']);
$currentpresets = $DB->count_records('adminpresets');
$currentitems = $DB->count_records('adminpresets_it');
$currentadvitems = $DB->count_records('adminpresets_it_a');
$currentplugins = $DB->count_records('adminpresets_plug');
// Only preset1 has been applied.
$this->assertCount(1, $DB->get_records('adminpresets_app'));
// Only the preset1 settings that have changed: enablebadges, mediawidth and maxanswers.
$this->assertCount(3, $DB->get_records('adminpresets_app_it'));
// Only the preset1 advanced settings that have changed: maxanswers_adv.
$this->assertCount(1, $DB->get_records('adminpresets_app_it_a'));
// Only the preset1 plugins that have changed: enrol_guest and mod_glossary.
$this->assertCount(2, $DB->get_records('adminpresets_app_plug'));
// 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('adminpresets');
$this->assertCount($currentpresets - 1, $presets);
$preset = reset($presets);
$this->assertArrayHasKey($presetid2, $presets);
// Check preset items.
$this->assertCount($currentitems - 4, $DB->get_records('adminpresets_it'));
$this->assertCount(0, $DB->get_records('adminpresets_it', ['adminpresetid' => $presetid1]));
// Check preset advanced items.
$this->assertCount($currentadvitems - 1, $DB->get_records('adminpresets_it_a'));
// Check preset plugins.
$this->assertCount($currentplugins - 3, $DB->get_records('adminpresets_plug'));
$this->assertCount(0, $DB->get_records('adminpresets_plug', ['adminpresetid' => $presetid1]));
// Check preset applied tables are empty now.
$this->assertCount(0, $DB->get_records('adminpresets_app'));
$this->assertCount(0, $DB->get_records('adminpresets_app_it'));
$this->assertCount(0, $DB->get_records('adminpresets_app_it_a'));
$this->assertCount(0, $DB->get_records('adminpresets_app_plug'));
// 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('core_adminpresets');
$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('core_adminpresets');
$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();
}
}
@@ -0,0 +1,177 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\local\action;
use core_adminpresets\manager;
/**
* 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('adminpresets');
$currentadvitems = $DB->count_records('adminpresets_it_a');
// Initialise some settings (to compare their values have been exported as expected).
set_config('recaptchapublickey', 'abcde');
set_config('enablebadges', '0');
set_config('mediawidth', '900', 'mod_lesson');
set_config('maxanswers', '2', 'mod_lesson');
set_config('maxanswers_adv', '0', 'mod_lesson');
set_config('defaultfeedback', '0', 'mod_lesson');
set_config('defaultfeedback_adv', '1', 'mod_lesson');
// 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('adminpresets');
$this->assertCount($currentpresets + 1, $presets);
$generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets');
$presetid = $generator->access_protected($action, 'id');
$this->assertArrayHasKey($presetid, $presets);
$preset = $presets[$presetid];
$this->assertEquals($presetname, $preset->name);
$this->assertEquals(manager::NONCORE_PRESET, $preset->iscore);
// Check the items, advanced attributes and plugins have been created.
$this->assertGreaterThan(0, $DB->count_records('adminpresets_it', ['adminpresetid' => $presetid]));
$this->assertGreaterThan($currentadvitems, $DB->count_records('adminpresets_it_a'));
$this->assertGreaterThan(0, $DB->count_records('adminpresets_plug', ['adminpresetid' => $presetid]));
// Check settings have been created with the expected values.
$params = ['adminpresetid' => $presetid, 'plugin' => 'none', 'name' => 'enablebadges'];
$setting = $DB->get_record('adminpresets_it', $params);
$this->assertEquals('0', $setting->value);
$params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'mediawidth'];
$setting = $DB->get_record('adminpresets_it', $params);
$this->assertEquals('900', $setting->value);
$params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'maxanswers'];
$setting = $DB->get_record('adminpresets_it', $params);
$this->assertEquals('2', $setting->value);
$params = ['itemid' => $setting->id, 'name' => 'maxanswers_adv'];
$setting = $DB->get_record('adminpresets_it_a', $params);
$this->assertEquals('0', $setting->value);
$params = ['adminpresetid' => $presetid, 'plugin' => 'mod_lesson', 'name' => 'defaultfeedback'];
$setting = $DB->get_record('adminpresets_it', $params);
$this->assertEquals('0', $setting->value);
$params = ['itemid' => $setting->id, 'name' => 'defaultfeedback_adv'];
$setting = $DB->get_record('adminpresets_it_a', $params);
$this->assertEquals('1', $setting->value);
// Check plugins have been created with the expected values.
$manager = \core_plugin_manager::instance();
$plugintype = 'enrol';
$plugins = $manager->get_present_plugins($plugintype);
$enabledplugins = $manager->get_enabled_plugins($plugintype);
foreach ($plugins as $pluginname => $unused) {
$params = ['adminpresetid' => $presetid, 'plugin' => $plugintype, 'name' => $pluginname];
$plugin = $DB->get_record('adminpresets_plug', $params);
$enabled = (!empty($enabledplugins) && array_key_exists($pluginname, $enabledplugins));
$this->assertEquals($enabled, (bool) $plugin->enabled);
}
// Check whether sensible settings have been exported or not.
$params = ['adminpresetid' => $presetid, 'plugin' => 'none', 'name' => 'recaptchapublickey'];
$recaptchasetting = $DB->get_record('adminpresets_it', $params);
$params = ['adminpresetid' => $presetid, 'plugin' => 'none', 'name' => 'cronremotepassword'];
$cronsetting = $DB->get_record('adminpresets_it', $params);
if ($includesensible) {
$this->assertEquals('abcde', $recaptchasetting->value);
$this->assertNotFalse($cronsetting);
} else {
$this->assertFalse($recaptchasetting);
$this->assertFalse($cronsetting);
}
// 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',
],
];
}
}
@@ -0,0 +1,252 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
namespace tool_admin_presets\local\action;
use core_adminpresets\manager;
/**
* 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('adminpresets');
$currentitems = $DB->count_records('adminpresets_it');
$currentadvitems = $DB->count_records('adminpresets_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('adminpresets');
$this->assertCount($currentpresets + 1, $presets);
$generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets');
$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(manager::NONCORE_PRESET, $preset->iscore);
if ($expectedsettings) {
// Check the items have been created.
$items = $DB->get_records('adminpresets_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('adminpresets_it_a');
$this->assertCount($currentadvitems + 1, $advitems);
$advitemfound = false;
foreach ($advitems as $advitem) {
if ($advitem->name == 'maxanswers_adv') {
$this->assertEmpty($advitem->value);
$advitemfound = true;
}
}
$this->assertTrue($advitemfound);
}
if ($expectedplugins) {
// Check the plugins have been created.
$plugins = $DB->get_records('adminpresets_plug', ['adminpresetid' => $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('adminpresets'));
$this->assertCount($currentitems, $DB->get_records('adminpresets_it'));
$this->assertCount($currentadvitems, $DB->get_records('adminpresets_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 {
$fixturesfolder = __DIR__ . '/../../../../../presets/tests/fixtures/';
return [
'Import settings from an empty file' => [
'filecontents' => '',
'expectedpreset' => false,
],
'Import settings and plugins from a valid XML file' => [
'filecontents' => file_get_contents($fixturesfolder . 'import_settings_plugins.xml'),
'expectedpreset' => true,
'expectedsettings' => true,
'expectedplugins' => true,
],
'Import only settings from a valid XML file' => [
'filecontents' => file_get_contents($fixturesfolder . '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($fixturesfolder . '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($fixturesfolder . 'invalid_xml_file.xml'),
'expectedpreset' => false,
'expectedsettings' => false,
'expectedplugins' => false,
'expecteddebugging' => false,
'expectedexception' => \Exception::class,
],
'Import unexisting settings category' => [
'filecontents' => file_get_contents($fixturesfolder . 'unexisting_category.xml'),
'expectedpreset' => false,
'expectedsettings' => false,
'expectedplugins' => false,
],
'Import unexisting setting' => [
'filecontents' => file_get_contents($fixturesfolder . 'unexisting_setting.xml'),
'expectedpreset' => false,
'expectedsettings' => false,
'expectedplugins' => false,
'expecteddebugging' => true,
],
'Import valid settings with one unexisting setting too' => [
'filecontents' => file_get_contents($fixturesfolder . 'import_settings_with_unexisting_setting.xml'),
'expectedpreset' => true,
'expectedsettings' => false,
'expectedplugins' => false,
'expecteddebugging' => true,
],
];
}
}
@@ -0,0 +1,166 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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('core_adminpresets');
$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 preview() method when the preset id doesn't exist.
*
* @covers ::preview
*/
public function test_load_preview_unexisting_preset(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Create some presets.
$generator = $this->getDataGenerator()->get_plugin_generator('core_adminpresets');
$presetid = $generator->create_preset();
// Initialise the parameters and create the load class.
$_POST['action'] = 'load';
$_POST['mode'] = 'preview';
$_POST['id'] = $presetid * 2; // Unexisting preset identifier.
$action = new load();
$action->preview();
$outputs = $generator->access_protected($action, 'outputs');
// In that case, no exception should be raised and the text of no preset found should be displayed.
$this->assertEquals(get_string('errornopreset', 'core_adminpresets'), $outputs);
}
/**
* 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('core_adminpresets');
$presetid = $generator->create_preset();
$currentpresets = $DB->count_records('adminpresets');
$currentitems = $DB->count_records('adminpresets_it');
$currentadvitems = $DB->count_records('adminpresets_it_a');
$currentplugins = $DB->count_records('adminpresets_plug');
$currentapppresets = $DB->count_records('adminpresets_app');
$currentappitems = $DB->count_records('adminpresets_app_it');
$currentappadvitems = $DB->count_records('adminpresets_app_it_a');
$currentappplugins = $DB->count_records('adminpresets_app_plug');
// Set the config values (to confirm they change after applying the preset).
set_config('enablebadges', 1);
set_config('allowemojipicker', 1);
set_config('mediawidth', '640', 'mod_lesson');
set_config('maxanswers', '5', 'mod_lesson');
set_config('maxanswers_adv', '1', 'mod_lesson');
set_config('enablecompletion', 1);
set_config('usecomments', 0);
// 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('adminpresets_app'));
// Applied items: enablebadges@none, mediawitdh@mod_lesson and maxanswers@@mod_lesson.
$this->assertCount($currentappitems + 3, $DB->get_records('adminpresets_app_it'));
// Applied advanced items: maxanswers_adv@mod_lesson.
$this->assertCount($currentappadvitems + 1, $DB->get_records('adminpresets_app_it_a'));
// Applied plugins: enrol_guest and mod_glossary.
$this->assertCount($currentappplugins + 2, $DB->get_records('adminpresets_app_plug'));
// Check no new preset has been created.
$this->assertCount($currentpresets, $DB->get_records('adminpresets'));
$this->assertCount($currentitems, $DB->get_records('adminpresets_it'));
$this->assertCount($currentadvitems, $DB->get_records('adminpresets_it_a'));
$this->assertCount($currentplugins, $DB->get_records('adminpresets_plug'));
// Check the setting values have changed accordingly with the ones defined in the preset.
$this->assertEquals(0, get_config('core', 'enablebadges'));
$this->assertEquals(900, get_config('mod_lesson', 'mediawidth'));
$this->assertEquals(2, get_config('mod_lesson', 'maxanswers'));
$this->assertEquals(0, get_config('mod_lesson', 'maxanswers_adv'));
// These settings will never change.
$this->assertEquals(1, get_config('core', 'allowemojipicker'));
$this->assertEquals(1, get_config('core', 'enablecompletion'));
$this->assertEquals(0, get_config('core', 'usecomments'));
// Check the plugins visibility have changed accordingly with the ones defined in the preset.
$enabledplugins = \core\plugininfo\enrol::get_enabled_plugins();
$this->assertArrayNotHasKey('guest', $enabledplugins);
$this->assertArrayHasKey('manual', $enabledplugins);
$enabledplugins = \core\plugininfo\mod::get_enabled_plugins();
$this->assertArrayNotHasKey('glossary', $enabledplugins);
$this->assertArrayHasKey('assign', $enabledplugins);
$enabledplugins = \core\plugininfo\qtype::get_enabled_plugins();
$this->assertArrayHasKey('truefalse', $enabledplugins);
}
}
@@ -0,0 +1,148 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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('core_adminpresets');
$presetid = $generator->create_preset(['applypreset' => true]);
$presetappid = $DB->get_field('adminpresets_app', 'id', ['adminpresetid' => $presetid]);
$currentpresets = $DB->count_records('adminpresets');
$currentitems = $DB->count_records('adminpresets_it');
$currentadvitems = $DB->count_records('adminpresets_it_a');
$currentplugins = $DB->count_records('adminpresets_plug');
$this->assertCount(1, $DB->get_records('adminpresets_app'));
$this->assertCount(3, $DB->get_records('adminpresets_app_it'));
$this->assertCount(1, $DB->get_records('adminpresets_app_it_a'));
$this->assertCount(2, $DB->get_records('adminpresets_app_plug'));
// Check the setttings have changed accordingly after applying the preset.
$this->assertEquals(0, get_config('core', 'enablebadges'));
$this->assertEquals(900, get_config('mod_lesson', 'mediawidth'));
$this->assertEquals(2, get_config('mod_lesson', 'maxanswers'));
$this->assertEquals(1, get_config('core', 'allowemojipicker'));
$this->assertEquals(1, get_config('core', 'enablecompletion'));
$this->assertEquals(0, get_config('core', 'usecomments'));
// Check the plugins visibility have changed accordingly with the ones defined in the preset.
$enabledplugins = \core\plugininfo\enrol::get_enabled_plugins();
$this->assertArrayNotHasKey('guest', $enabledplugins);
$enabledplugins = \core\plugininfo\mod::get_enabled_plugins();
$this->assertArrayNotHasKey('glossary', $enabledplugins);
$enabledplugins = \core\plugininfo\qtype::get_enabled_plugins();
$this->assertArrayHasKey('truefalse', $enabledplugins);
// 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('adminpresets_app'));
$this->assertCount(0, $DB->get_records('adminpresets_app_it'));
$this->assertCount(0, $DB->get_records('adminpresets_app_it_a'));
$this->assertCount(0, $DB->get_records('adminpresets_app_plug'));
// Check the preset data hasn't changed.
$this->assertCount($currentpresets, $DB->get_records('adminpresets'));
$this->assertCount($currentitems, $DB->get_records('adminpresets_it'));
$this->assertCount($currentadvitems, $DB->get_records('adminpresets_it_a'));
$this->assertCount($currentplugins, $DB->get_records('adminpresets_plug'));
// Check the setting values have been reverted accordingly.
$this->assertEquals(1, get_config('core', 'enablebadges'));
$this->assertEquals(640, get_config('mod_lesson', 'mediawidth'));
$this->assertEquals(5, get_config('mod_lesson', 'maxanswers'));
$this->assertEquals(1, get_config('mod_lesson', 'maxanswers_adv'));
// These settings won't change, regardless if they are posted to the form.
$this->assertEquals(1, get_config('core', 'allowemojipicker'));
$this->assertEquals(1, get_config('core', 'enablecompletion'));
$this->assertEquals(0, get_config('core', 'usecomments'));
// Check the plugins visibility have been reverted accordingly.
$enabledplugins = \core\plugininfo\enrol::get_enabled_plugins();
$this->assertArrayHasKey('guest', $enabledplugins);
$enabledplugins = \core\plugininfo\mod::get_enabled_plugins();
$this->assertArrayHasKey('glossary', $enabledplugins);
// This plugin won't change (because it had the same value than before the preset was applied).
$enabledplugins = \core\plugininfo\qtype::get_enabled_plugins();
$this->assertArrayHasKey('truefalse', $enabledplugins);
}
/**
* 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('core_adminpresets');
$presetid = $generator->create_preset(['applypreset' => true]);
$presetappid = $DB->get_field('adminpresets_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();
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Admin tool presets plugin to load some settings.
*
* @package tool_admin_presets
* @copyright 2021 Pimenko <support@pimenko.com><pimenko.com>
* @author Jordan Kesraoui | Sylvain Revenu | Pimenko based on David Monllaó <david.monllao@urv.cat> 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 = 2024042200;
$plugin->requires = 2024041600;
+10
View File
@@ -0,0 +1,10 @@
define("tool_analytics/log_info",["exports","core/modal","core/str"],(function(_exports,_modal,_str){var obj;
/**
* Shows a dialogue with info about this logs.
*
* @module tool_analytics/log_info
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.loadInfo=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};_exports.loadInfo=(id,info)=>{document.addEventListener("click",(e=>{if(!e.target.closest('[data-model-log-id="'.concat(id,'"]')))return;e.preventDefault();const bodyInfo=document.createElement("ul");info.forEach((item=>{const li=document.createElement("li");li.innerHTML=item,bodyInfo.append(li)})),_modal.default.create({title:(0,_str.get_string)("loginfo","tool_analytics"),body:bodyInfo.outerHTML,large:!0,show:!0,removeOnClose:!0})}))}}));
//# sourceMappingURL=log_info.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"log_info.min.js","sources":["../src/log_info.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Shows a dialogue with info about this logs.\n *\n * @module tool_analytics/log_info\n * @copyright 2017 David Monllao {@link http://www.davidmonllao.com}\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Modal from 'core/modal';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Prepares a modal info for a log's results.\n *\n * @param {Number} id\n * @param {string[]} info\n */\nexport const loadInfo = (id, info) => {\n document.addEventListener('click', (e) => {\n const link = e.target.closest(`[data-model-log-id=\"${id}\"]`);\n if (!link) {\n return;\n }\n e.preventDefault();\n const bodyInfo = document.createElement('ul');\n info.forEach((item) => {\n const li = document.createElement('li');\n li.innerHTML = item;\n bodyInfo.append(li);\n });\n\n Modal.create({\n title: getString('loginfo', 'tool_analytics'),\n body: bodyInfo.outerHTML,\n large: true,\n show: true,\n removeOnClose: true,\n });\n });\n};\n"],"names":["id","info","document","addEventListener","e","target","closest","preventDefault","bodyInfo","createElement","forEach","item","li","innerHTML","append","create","title","body","outerHTML","large","show","removeOnClose"],"mappings":";;;;;;;8JA+BwB,CAACA,GAAIC,QACzBC,SAASC,iBAAiB,SAAUC,QACnBA,EAAEC,OAAOC,sCAA+BN,iBAIrDI,EAAEG,uBACIC,SAAWN,SAASO,cAAc,MACxCR,KAAKS,SAASC,aACJC,GAAKV,SAASO,cAAc,MAClCG,GAAGC,UAAYF,KACfH,SAASM,OAAOF,sBAGdG,OAAO,CACTC,OAAO,mBAAU,UAAW,kBAC5BC,KAAMT,SAASU,UACfC,OAAO,EACPC,MAAM,EACNC,eAAe"}
+10
View File
@@ -0,0 +1,10 @@
/**
* AMD module for model actions confirmation.
*
* @module tool_analytics/model
* @copyright 2017 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("tool_analytics/model",["jquery","core/str","core/log","core/notification","core/modal_save_cancel","core/modal_cancel","core/modal_events","core/templates"],(function($,Str,log,Notification,ModalSaveCancel,ModalCancel,ModalEvents,Templates){var actionsList={clear:{title:{key:"clearpredictions",component:"tool_analytics"},body:{key:"clearmodelpredictions",component:"tool_analytics"}},delete:{title:{key:"delete",component:"tool_analytics"},body:{key:"deletemodelconfirmation",component:"tool_analytics"}}};return{confirmAction:function(actionId,actionType){$('[data-action-id="'+actionId+'"]').on("click",(function(ev){ev.preventDefault();var a=$(ev.currentTarget);if(void 0!==actionsList[actionType]){var wrap,reqStrings=[actionsList[actionType].title,actionsList[actionType].body];reqStrings[1].param=(wrap=$(a).closest("[data-model-name]")).length?wrap.attr("data-model-name"):(log.error("Unexpected DOM error - unable to obtain the model name"),"");var stringsPromise=Str.get_strings(reqStrings),modalPromise=ModalSaveCancel.create({});$.when(stringsPromise,modalPromise).then((function(strings,modal){return modal.setTitle(strings[0]),modal.setBody(strings[1]),modal.setSaveButtonText(strings[0]),modal.getRoot().on(ModalEvents.save,(function(){window.location.href=a.attr("href")})),modal.show(),modal})).fail(Notification.exception)}else log.error('Action "'+actionType+'" is not allowed.')}))},selectEvaluationOptions:function(actionId,trainedOnlyExternally){$('[data-action-id="'+actionId+'"]').on("click",(function(ev){ev.preventDefault();var a=$(ev.currentTarget),timeSplittingMethods=$(this).attr("data-timesplitting-methods");ModalSaveCancel.create({title:Str.get_string("evaluatemodel","tool_analytics"),body:Templates.render("tool_analytics/evaluation_options",{trainedexternally:trainedOnlyExternally,timesplittingmethods:JSON.parse(timeSplittingMethods)}),removeOnClose:!0,buttons:{save:Str.get_string("evaluate","tool_analytics")},show:!0}).then((modal=>(modal.getRoot().on(ModalEvents.save,(function(){"trainedmodel"==$("input[name='evaluationmode']:checked").val()&&a.attr("href",a.attr("href")+"&mode=trainedmodel");var timeSplittingMethod=$("#id-evaluation-timesplitting").val();a.attr("href",a.attr("href")+"&timesplitting="+timeSplittingMethod),window.location.href=a.attr("href")})),modal))).catch(Notification.exception)}))},selectExportOptions:function(actionId,isTrained){$('[data-action-id="'+actionId+'"]').on("click",(function(ev){ev.preventDefault();var a=$(ev.currentTarget);if(!isTrained)return a.attr("href",a.attr("href")+"&action=exportmodel&includeweights=0"),void(window.location.href=a.attr("href"));var stringsPromise=Str.get_strings([{key:"export",component:"tool_analytics"}]),modalPromise=ModalSaveCancel.create({body:Templates.render("tool_analytics/export_options",{}),removeOnClose:!0});$.when(stringsPromise,modalPromise).then((function(strings,modal){return modal.setTitle(strings[0]),modal.setSaveButtonText(strings[0]),modal.getRoot().on(ModalEvents.save,(function(){"exportdata"==$("input[name='exportoption']:checked").val()?a.attr("href",a.attr("href")+"&action=exportdata"):(a.attr("href",a.attr("href")+"&action=exportmodel"),$("#id-includeweights").is(":checked")?a.attr("href",a.attr("href")+"&includeweights=1"):a.attr("href",a.attr("href")+"&includeweights=0")),window.location.href=a.attr("href")})),modal.show(),modal})).fail(Notification.exception)}))}}}));
//# sourceMappingURL=model.min.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
/**
* Potential contexts selector module.
*
* @module tool_analytics/potential-contexts
* @copyright 2019 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("tool_analytics/potential-contexts",["jquery","core/ajax"],(function($,Ajax){return{processResults:function(selector,results){var contexts=[];return $.isArray(results)?($.each(results,(function(index,context){contexts.push({value:context.id,label:context.name})})),contexts):results},transport:function(selector,query,success,failure){let modelid=$(selector).attr("modelid")||null;Ajax.call([{methodname:"tool_analytics_potential_contexts",args:{query:query,modelid:modelid}}])[0].then(success).fail(failure)}}}));
//# sourceMappingURL=potential-contexts.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"potential-contexts.min.js","sources":["../src/potential-contexts.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Potential contexts selector module.\n *\n * @module tool_analytics/potential-contexts\n * @copyright 2019 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax'], function($, Ajax) {\n\n return /** @alias module:tool_analytics/potential-contexts */ {\n\n processResults: function(selector, results) {\n var contexts = [];\n if ($.isArray(results)) {\n $.each(results, function(index, context) {\n contexts.push({\n value: context.id,\n label: context.name\n });\n });\n return contexts;\n\n } else {\n return results;\n }\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n\n let modelid = $(selector).attr('modelid') || null;\n promise = Ajax.call([{\n methodname: 'tool_analytics_potential_contexts',\n args: {\n query: query,\n modelid: modelid\n }\n }]);\n\n promise[0].then(success).fail(failure);\n }\n\n };\n\n});\n"],"names":["define","$","Ajax","processResults","selector","results","contexts","isArray","each","index","context","push","value","id","label","name","transport","query","success","failure","modelid","attr","call","methodname","args","then","fail"],"mappings":";;;;;;;AAuBAA,2CAAO,CAAC,SAAU,cAAc,SAASC,EAAGC,YAEsB,CAE1DC,eAAgB,SAASC,SAAUC,aAC3BC,SAAW,UACXL,EAAEM,QAAQF,UACVJ,EAAEO,KAAKH,SAAS,SAASI,MAAOC,SAC5BJ,SAASK,KAAK,CACVC,MAAOF,QAAQG,GACfC,MAAOJ,QAAQK,UAGhBT,UAGAD,SAIfW,UAAW,SAASZ,SAAUa,MAAOC,QAASC,aAGtCC,QAAUnB,EAAEG,UAAUiB,KAAK,YAAc,KACnCnB,KAAKoB,KAAK,CAAC,CACjBC,WAAY,oCACZC,KAAM,CACFP,MAAOA,MACPG,QAASA,YAIT,GAAGK,KAAKP,SAASQ,KAAKP"}
+54
View File
@@ -0,0 +1,54 @@
// 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 <http://www.gnu.org/licenses/>.
/**
* Shows a dialogue with info about this logs.
*
* @module tool_analytics/log_info
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Modal from 'core/modal';
import {get_string as getString} from 'core/str';
/**
* Prepares a modal info for a log's results.
*
* @param {Number} id
* @param {string[]} info
*/
export const loadInfo = (id, info) => {
document.addEventListener('click', (e) => {
const link = e.target.closest(`[data-model-log-id="${id}"]`);
if (!link) {
return;
}
e.preventDefault();
const bodyInfo = document.createElement('ul');
info.forEach((item) => {
const li = document.createElement('li');
li.innerHTML = item;
bodyInfo.append(li);
});
Modal.create({
title: getString('loginfo', 'tool_analytics'),
body: bodyInfo.outerHTML,
large: true,
show: true,
removeOnClose: true,
});
});
};
+223
View File
@@ -0,0 +1,223 @@
// 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 <http://www.gnu.org/licenses/>.
/**
* AMD module for model actions confirmation.
*
* @module tool_analytics/model
* @copyright 2017 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define([
'jquery', 'core/str', 'core/log', 'core/notification', 'core/modal_save_cancel',
'core/modal_cancel', 'core/modal_events', 'core/templates'],
function($, Str, log, Notification, ModalSaveCancel, ModalCancel, ModalEvents, Templates) {
/**
* List of actions that require confirmation and confirmation message.
*/
var actionsList = {
clear: {
title: {
key: 'clearpredictions',
component: 'tool_analytics'
}, body: {
key: 'clearmodelpredictions',
component: 'tool_analytics'
}
},
'delete': {
title: {
key: 'delete',
component: 'tool_analytics'
}, body: {
key: 'deletemodelconfirmation',
component: 'tool_analytics'
}
}
};
/**
* Returns the model name.
*
* @param {Object} actionItem The action item DOM node.
* @return {String}
*/
var getModelName = function(actionItem) {
var wrap = $(actionItem).closest('[data-model-name]');
if (wrap.length) {
return wrap.attr('data-model-name');
} else {
log.error('Unexpected DOM error - unable to obtain the model name');
return '';
}
};
/** @alias module:tool_analytics/model */
return {
/**
* Displays a confirm modal window before executing the action.
*
* @param {String} actionId
* @param {String} actionType
*/
confirmAction: function(actionId, actionType) {
$('[data-action-id="' + actionId + '"]').on('click', function(ev) {
ev.preventDefault();
var a = $(ev.currentTarget);
if (typeof actionsList[actionType] === "undefined") {
log.error('Action "' + actionType + '" is not allowed.');
return;
}
var reqStrings = [
actionsList[actionType].title,
actionsList[actionType].body
];
reqStrings[1].param = getModelName(a);
var stringsPromise = Str.get_strings(reqStrings);
var modalPromise = ModalSaveCancel.create({});
$.when(stringsPromise, modalPromise).then(function(strings, modal) {
modal.setTitle(strings[0]);
modal.setBody(strings[1]);
modal.setSaveButtonText(strings[0]);
modal.getRoot().on(ModalEvents.save, function() {
window.location.href = a.attr('href');
});
modal.show();
return modal;
}).fail(Notification.exception);
});
},
/**
* Displays evaluation mode and time-splitting method choices.
*
* @param {String} actionId
* @param {Boolean} trainedOnlyExternally
*/
selectEvaluationOptions: function(actionId, trainedOnlyExternally) {
$('[data-action-id="' + actionId + '"]').on('click', function(ev) {
ev.preventDefault();
var a = $(ev.currentTarget);
var timeSplittingMethods = $(this).attr('data-timesplitting-methods');
ModalSaveCancel.create({
title: Str.get_string('evaluatemodel', 'tool_analytics'),
body: Templates.render('tool_analytics/evaluation_options', {
trainedexternally: trainedOnlyExternally,
timesplittingmethods: JSON.parse(timeSplittingMethods)
}),
removeOnClose: true,
buttons: {
save: Str.get_string('evaluate', 'tool_analytics'),
},
show: true,
})
.then((modal) => {
modal.getRoot().on(ModalEvents.save, function() {
// Evaluation mode.
var evaluationMode = $("input[name='evaluationmode']:checked").val();
if (evaluationMode == 'trainedmodel') {
a.attr('href', a.attr('href') + '&mode=trainedmodel');
}
// Selected time-splitting id.
var timeSplittingMethod = $("#id-evaluation-timesplitting").val();
a.attr('href', a.attr('href') + '&timesplitting=' + timeSplittingMethod);
window.location.href = a.attr('href');
return;
});
return modal;
}).catch(Notification.exception);
});
},
/**
* Displays export options.
*
* We have two main options: export training data and export configuration.
* The 2nd option has an extra option: include the trained algorithm weights.
*
* @param {String} actionId
* @param {Boolean} isTrained
*/
selectExportOptions: function(actionId, isTrained) {
$('[data-action-id="' + actionId + '"]').on('click', function(ev) {
ev.preventDefault();
var a = $(ev.currentTarget);
if (!isTrained) {
// Export the model configuration if the model is not trained. We can't export anything else.
a.attr('href', a.attr('href') + '&action=exportmodel&includeweights=0');
window.location.href = a.attr('href');
return;
}
var stringsPromise = Str.get_strings([
{
key: 'export',
component: 'tool_analytics'
}
]);
var modalPromise = ModalSaveCancel.create({
body: Templates.render('tool_analytics/export_options', {}),
removeOnClose: true,
});
$.when(stringsPromise, modalPromise).then(function(strings, modal) {
modal.setTitle(strings[0]);
modal.setSaveButtonText(strings[0]);
modal.getRoot().on(ModalEvents.save, function() {
var exportOption = $("input[name='exportoption']:checked").val();
if (exportOption == 'exportdata') {
a.attr('href', a.attr('href') + '&action=exportdata');
} else {
a.attr('href', a.attr('href') + '&action=exportmodel');
if ($("#id-includeweights").is(':checked')) {
a.attr('href', a.attr('href') + '&includeweights=1');
} else {
a.attr('href', a.attr('href') + '&includeweights=0');
}
}
window.location.href = a.attr('href');
return;
});
modal.show();
return modal;
}).fail(Notification.exception);
});
}
};
});
@@ -0,0 +1,61 @@
// 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 <http://www.gnu.org/licenses/>.
/**
* Potential contexts selector module.
*
* @module tool_analytics/potential-contexts
* @copyright 2019 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/ajax'], function($, Ajax) {
return /** @alias module:tool_analytics/potential-contexts */ {
processResults: function(selector, results) {
var contexts = [];
if ($.isArray(results)) {
$.each(results, function(index, context) {
contexts.push({
value: context.id,
label: context.name
});
});
return contexts;
} else {
return results;
}
},
transport: function(selector, query, success, failure) {
var promise;
let modelid = $(selector).attr('modelid') || null;
promise = Ajax.call([{
methodname: 'tool_analytics_potential_contexts',
args: {
query: query,
modelid: modelid
}
}]);
promise[0].then(success).fail(failure);
}
};
});
@@ -0,0 +1,55 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Helper class that contains helper functions for cli scripts.
*
* @package tool_analytics
* @copyright 2017 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics;
defined('MOODLE_INTERNAL') || die();
/**
* Helper class that contains helper functions for cli scripts.
*
* @package tool_analytics
* @copyright 2017 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class clihelper {
/**
* List all models in the system. To be used from cli scripts.
*
* @return void
*/
public static function list_models() {
cli_heading("List of models");
echo str_pad(get_string('modelid', 'tool_analytics'), 15, ' ') . ' ' . str_pad(get_string('name'), 50, ' ') .
' ' . str_pad(get_string('status'), 15, ' ') . "\n";
$models = \core_analytics\manager::get_all_models();
foreach ($models as $model) {
$modelid = $model->get_id();
$isenabled = $model->is_enabled() ? get_string('enabled', 'tool_analytics') : get_string('disabled', 'tool_analytics');
$name = $model->get_name();
echo str_pad($modelid, 15, ' ') . ' ' . str_pad($name, 50, ' ') . ' ' . str_pad($isenabled, 15, ' ') . "\n";
}
}
}
+109
View File
@@ -0,0 +1,109 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* This is the external API for this component.
*
* @package tool_analytics
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_value;
use core_external\external_single_structure;
use core_external\external_multiple_structure;
/**
* This is the external API for this component.
*
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class external extends external_api {
const MAX_CONTEXTS_RETURNED = 100;
/**
* potential_contexts parameters.
*
* @since Moodle 3.8
* @return external_function_parameters
*/
public static function potential_contexts_parameters() {
return new external_function_parameters(
array(
'query' => new external_value(PARAM_NOTAGS, 'The model id', VALUE_DEFAULT),
'modelid' => new external_value(PARAM_INT, 'The model id', VALUE_DEFAULT)
)
);
}
/**
* Return the contexts that match the provided query.
*
* @since Moodle 3.8
* @param string|null $query
* @param int|null $modelid
* @return array an array of contexts
*/
public static function potential_contexts(?string $query = null, ?int $modelid = null) {
$params = self::validate_parameters(self::potential_contexts_parameters(), ['modelid' => $modelid, 'query' => $query]);
\core_analytics\manager::check_can_manage_models();
if ($params['modelid']) {
$model = new \core_analytics\model($params['modelid']);
$contexts = ($model->get_analyser(['notimesplitting' => true]))::potential_context_restrictions($params['query']);
} else {
$contexts = \core_analytics\manager::get_potential_context_restrictions(null, $params['query']);
}
$contextoptions = [];
$i = 0;
foreach ($contexts as $contextid => $contextname) {
if ($i === self::MAX_CONTEXTS_RETURNED) {
// Limited to MAX_CONTEXTS_RETURNED items.
break;
}
$contextoptions[] = ['id' => $contextid, 'name' => $contextname];
$i++;
}
return $contextoptions;
}
/**
* potential_contexts return
*
* @since Moodle 3.8
* @return \core_external\external_description
*/
public static function potential_contexts_returns() {
return new external_multiple_structure(
new external_single_structure([
'id' => new external_value(PARAM_INT, 'ID of the context'),
'name' => new external_value(PARAM_NOTAGS, 'The context name')
])
);
}
}
@@ -0,0 +1,235 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Model edit form.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Model edit form.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class edit_model extends \moodleform {
/**
* Form definition
*/
public function definition() {
global $OUTPUT;
$mform = $this->_form;
if ($this->_customdata['trainedmodel'] && $this->_customdata['staticmodel'] === false) {
$message = get_string('edittrainedwarning', 'tool_analytics');
$mform->addElement('html', $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING));
}
$mform->addElement('advcheckbox', 'enabled', get_string('enabled', 'tool_analytics'));
// Target.
if (!empty($this->_customdata['targets'])) {
$targets = array('' => '');
foreach ($this->_customdata['targets'] as $classname => $target) {
$optionname = \tool_analytics\output\helper::class_to_option($classname);
$targets[$optionname] = $target->get_name();
}
$mform->addElement('select', 'target', get_string('target', 'tool_analytics'), $targets);
$mform->addHelpButton('target', 'target', 'tool_analytics');
$mform->addRule('target', get_string('required'), 'required', null, 'client');
}
if (!empty($this->_customdata['targetname']) && !empty($this->_customdata['targetclass'])) {
$mform->addElement('static', 'targetname', get_string('target', 'tool_analytics'), $this->_customdata['targetname']);
$mform->addElement('hidden', 'target',
\tool_analytics\output\helper::class_to_option($this->_customdata['targetclass']));
// We won't update the model's target so no worries about its format (we can't use PARAM_ALPHANUMEXT
// because of class_to_option).
$mform->setType('target', PARAM_TEXT);
}
// Indicators.
if (!$this->_customdata['staticmodel']) {
$indicators = array();
foreach ($this->_customdata['indicators'] as $classname => $indicator) {
$optionname = \tool_analytics\output\helper::class_to_option($classname);
$indicators[$optionname] = $indicator->get_name();
}
$options = array(
'multiple' => true
);
$mform->addElement('autocomplete', 'indicators', get_string('indicators', 'tool_analytics'), $indicators, $options);
$mform->setType('indicators', PARAM_ALPHANUMEXT);
$mform->addHelpButton('indicators', 'indicators', 'tool_analytics');
}
// Time-splitting methods.
if (!empty($this->_customdata['invalidcurrenttimesplitting'])) {
$mform->addElement('html', $OUTPUT->notification(
get_string('invalidcurrenttimesplitting', 'tool_analytics'),
\core\output\notification::NOTIFY_WARNING)
);
}
$timesplittings = array('' => '');
foreach ($this->_customdata['timesplittings'] as $classname => $timesplitting) {
$optionname = \tool_analytics\output\helper::class_to_option($classname);
$timesplittings[$optionname] = $timesplitting->get_name();
}
$mform->addElement('select', 'timesplitting', get_string('timesplittingmethod', 'analytics'), $timesplittings);
$mform->addHelpButton('timesplitting', 'timesplittingmethod', 'analytics');
// Contexts restriction.
if (!empty($this->_customdata['supportscontexts'])) {
$options = [
'ajax' => 'tool_analytics/potential-contexts',
'multiple' => true,
'noselectionstring' => get_string('all')
];
if (!empty($this->_customdata['id'])) {
$options['modelid'] = $this->_customdata['id'];
$contexts = $this->load_current_contexts();
} else {
// No need to preload any selected contexts.
$contexts = [];
}
$mform->addElement('autocomplete', 'contexts', get_string('contexts', 'tool_analytics'), $contexts, $options);
$mform->setType('contexts', PARAM_INT);
$mform->addHelpButton('contexts', 'contexts', 'tool_analytics');
}
// Predictions processor.
if (!$this->_customdata['staticmodel']) {
$defaultprocessor = \core_analytics\manager::get_predictions_processor_name(
\core_analytics\manager::get_predictions_processor()
);
$predictionprocessors = ['' => get_string('defaultpredictoroption', 'analytics', $defaultprocessor)];
foreach ($this->_customdata['predictionprocessors'] as $classname => $predictionsprocessor) {
if ($predictionsprocessor->is_ready() !== true) {
continue;
}
$optionname = \tool_analytics\output\helper::class_to_option($classname);
$predictionprocessors[$optionname] = \core_analytics\manager::get_predictions_processor_name($predictionsprocessor);
}
$mform->addElement('select', 'predictionsprocessor', get_string('predictionsprocessor', 'analytics'),
$predictionprocessors);
$mform->addHelpButton('predictionsprocessor', 'predictionsprocessor', 'analytics');
}
if (!empty($this->_customdata['id'])) {
$mform->addElement('hidden', 'id', $this->_customdata['id']);
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'action', 'edit');
$mform->setType('action', PARAM_ALPHANUMEXT);
}
$this->add_action_buttons();
}
/**
* Form validation
*
* @param array $data data from the form.
* @param array $files files uploaded.
*
* @return array of errors.
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);
$targetclass = \tool_analytics\output\helper::option_to_class($data['target']);
$target = \core_analytics\manager::get_target($targetclass);
if (!empty($data['timesplitting'])) {
$timesplittingclass = \tool_analytics\output\helper::option_to_class($data['timesplitting']);
if (\core_analytics\manager::is_valid($timesplittingclass, '\core_analytics\local\time_splitting\base') === false) {
$errors['timesplitting'] = get_string('errorinvalidtimesplitting', 'analytics');
}
$timesplitting = \core_analytics\manager::get_time_splitting($timesplittingclass);
if (!$target->can_use_timesplitting($timesplitting)) {
$errors['timesplitting'] = get_string('invalidtimesplitting', 'tool_analytics');
}
}
if (!empty($data['contexts'])) {
$analyserclass = $target->get_analyser_class();
if (!$potentialcontexts = $analyserclass::potential_context_restrictions()) {
$errors['contexts'] = get_string('errornocontextrestrictions', 'analytics');
} else {
// Flip the contexts array so we can just diff by key.
$selectedcontexts = array_flip($data['contexts']);
$invalidcontexts = array_diff_key($selectedcontexts, $potentialcontexts);
if (!empty($invalidcontexts)) {
$errors['contexts'] = get_string('errorinvalidcontexts', 'analytics');
}
}
}
if (!$this->_customdata['staticmodel']) {
if (empty($data['indicators'])) {
$errors['indicators'] = get_string('errornoindicators', 'analytics');
} else {
foreach ($data['indicators'] as $indicator) {
$realindicatorname = \tool_analytics\output\helper::option_to_class($indicator);
if (\core_analytics\manager::is_valid($realindicatorname, '\core_analytics\local\indicator\base') === false) {
$errors['indicators'] = get_string('errorinvalidindicator', 'analytics', $realindicatorname);
}
}
}
}
if (!empty($data['enabled']) && empty($data['timesplitting'])) {
$errors['enabled'] = get_string('errorcantenablenotimesplitting', 'tool_analytics');
}
return $errors;
}
/**
* Load the currently selected context options.
*
* @return array
*/
protected function load_current_contexts() {
$contexts = [];
foreach ($this->_customdata['contexts'] as $context) {
$contexts[$context->id] = $context->get_context_name(true, true);
}
return $contexts;
}
}
@@ -0,0 +1,58 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Model upload form.
*
* @package tool_analytics
* @copyright 2017 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
/**
* Model upload form.
*
* @package tool_analytics
* @copyright 2017 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class import_model extends \moodleform {
/**
* Form definition.
*
* @return null
*/
public function definition() {
$mform = $this->_form;
$mform->addElement('header', 'settingsheader', get_string('importmodel', 'tool_analytics'));
$mform->addElement('filepicker', 'modelfile', get_string('file'), null, ['accepted_types' => '.zip']);
$mform->addRule('modelfile', null, 'required');
$mform->addElement('advcheckbox', 'ignoreversionmismatches', get_string('ignoreversionmismatches', 'tool_analytics'),
get_string('ignoreversionmismatchescheckbox', 'tool_analytics'));
$this->add_action_buttons(true, get_string('import'));
}
}
@@ -0,0 +1,158 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Typical crappy helper class with tiny functions.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die();
/**
* Helper class with general purpose tiny functions.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class helper {
/**
* Converts a class full name to a select option key
*
* @param string $class
* @return string
*/
public static function class_to_option($class) {
// Form field is PARAM_ALPHANUMEXT and we are sending fully qualified class names
// as option names, but replacing the backslash for a string that is really unlikely
// to ever be part of a class name.
return str_replace('\\', '__', $class ?? '');
}
/**
* option_to_class
*
* @param string $option
* @return string
*/
public static function option_to_class($option) {
// Really unlikely but yeah, I'm a bad booyyy.
return str_replace('__', '\\', $option);
}
/**
* Sets an analytics > analytics models > $title breadcrumb.
*
* @param string $title
* @param \moodle_url $url
* @param \context|null $context Defaults to context_system
* @return null
*/
public static function set_navbar(string $title, \moodle_url $url, ?\context $context = null) {
global $PAGE;
if (!$context) {
$context = \context_system::instance();
}
$PAGE->set_context($context);
$PAGE->set_url($url);
$PAGE->set_secondary_active_tab('siteadminnode');
$PAGE->set_primary_active_tab('siteadminnode');
if ($siteadmin = $PAGE->settingsnav->find('root', \navigation_node::TYPE_SITE_ADMIN)) {
$PAGE->navbar->add($siteadmin->get_content(), $siteadmin->action(),
\breadcrumb_navigation_node::TYPE_SITE_ADMIN, null, 'root');
}
if ($analytics = $PAGE->settingsnav->find('analytics', \navigation_node::TYPE_SETTING)) {
$PAGE->navbar->add($analytics->get_content(), $analytics->action());
}
if ($analyticmodels = $PAGE->settingsnav->find('analyticmodels', \navigation_node::TYPE_SETTING)) {
$PAGE->navbar->add($analyticmodels->get_content(), $analyticmodels->action());
}
$PAGE->navbar->add($title);
$PAGE->set_pagelayout('report');
$PAGE->set_title($title);
$PAGE->set_heading($title);
}
/**
* Resets the current page.
*
* Note that this function can only be used by analytics pages that work at the system context.
*
* @return null
*/
public static function reset_page() {
global $PAGE;
$PAGE->reset_theme_and_output();
$PAGE->set_context(\context_system::instance());
}
/**
* Convert a list of contexts to an associative array where the value is the context name.
*
* @param array $contexts
* @param \moodle_url $url
* @param \renderer_base $output
* @param int|null $selected
* @param bool $includeall
* @param bool $shortentext
* @return \stdClass
*/
public static function prediction_context_selector(array $contexts, \moodle_url $url, \renderer_base $output,
?int $selected = null, ?bool $includeall = false, ?bool $shortentext = true): \stdClass {
foreach ($contexts as $contextid => $unused) {
// We prepare this to be used as single_select template options.
$context = \context::instance_by_id($contextid);
// Special name for system level predictions as showing "System is not visually nice".
if ($contextid == SYSCONTEXTID) {
$contextname = get_string('allpredictions', 'tool_analytics');
} else {
if ($shortentext) {
$contextname = shorten_text($context->get_context_name(false, true), 40);
} else {
$contextname = $context->get_context_name(false, true);
}
}
$contexts[$contextid] = $contextname;
}
if ($includeall) {
$contexts[0] = get_string('all');
$nothing = '';
} else {
$nothing = array('' => 'choosedots');
}
\core_collator::asort($contexts);
if (!$selected) {
$selected = '';
}
$singleselect = new \single_select($url, 'contextid', $contexts, $selected, $nothing);
return $singleselect->export_for_template($output);
}
}
@@ -0,0 +1,184 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Insights report renderable.
*
* @package tool_analytics
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die;
/**
* Insights report renderable.
*
* @package tool_analytics
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class insights_report implements \renderable, \templatable {
/**
* @var \core_analytics\model
*/
private $model = null;
/**
* @var \context
*/
private $context = null;
/**
* Inits the insights report renderable.
*
* @param \core_analytics\model $model
* @param int|null $contextid
* @return null
*/
public function __construct(\core_analytics\model $model, ?int $contextid = null) {
$this->model = $model;
if ($contextid) {
$this->context = \context::instance_by_id($contextid);
}
}
/**
* Export the data.
*
* @param \renderer_base $output
* @return \stdClass
*/
public function export_for_template(\renderer_base $output): \stdClass {
// Prepare the context object.
$data = new \stdClass();
$data->modelname = $this->model->get_name();
$data->charts = [];
$predictionactionrecords = $this->model->get_prediction_actions($this->context);
// Context selector.
$predictioncontexts = $this->model->get_predictions_contexts(false);
if ($predictioncontexts && count($predictioncontexts) > 1) {
$url = new \moodle_url('/admin/tool/analytics/model.php', ['id' => $this->model->get_id(),
'action' => 'insightsreport']);
if ($this->context) {
$selected = $this->context->id;
} else {
// This is the 'all' option.
$selected = 0;
}
$data->contextselect = \tool_analytics\output\helper::prediction_context_selector($predictioncontexts,
$url, $output, $selected, true, false);
}
if ($predictionactionrecords->valid()) {
foreach ($predictionactionrecords as $record) {
// Using this unusual execution flow to init the chart data because $predictionactionrecords
// is a \moodle_recordset.
if (empty($actionlabels)) {
list($actionlabels, $actionvalues, $actiontypes) = $this->init_action_labels($record);
}
// One value for each action.
$actionvalues['separated'][$record->actionname]++;
// Grouped value.
$actiontype = $actiontypes[$record->actionname];
$actionvalues['grouped'][$actiontype]++;
}
$predictionactionrecords->close();
// Actions doughtnut.
$chart = new \core\chart_pie();
$chart->set_doughnut(true);
$chart->set_title(get_string('actionsexecutedbyusers', 'tool_analytics'));
$series = new \core\chart_series(get_string('actions', 'tool_analytics'),
array_values($actionvalues['separated']));
$chart->add_series($series);
$chart->set_labels(array_values($actionlabels['separated']));
$data->separatedchart = $output->render($chart);
// Positive/negative/neutral bar chart.
$chart = new \core\chart_bar();
$chart->set_title(get_string('actionexecutedgroupedusefulness', 'tool_analytics'));
$series = new \core\chart_series(get_string('actions', 'tool_analytics'),
array_values($actionvalues['grouped']));
$chart->add_series($series);
$chart->set_labels(array_values($actionlabels['grouped']));
$data->groupedchart = $output->render($chart);
} else {
$predictionactionrecords->close();
$data->noactions = [
'message' => get_string('noactionsfound', 'tool_analytics'),
'announce' => true,
];
}
return $data;
}
/**
* Initialises the action labels and values in this model.
*
* @param \stdClass $predictionactionrecord
* @return array Two-dimensional array with the labels and values initialised to zero.
*/
private function init_action_labels(\stdClass $predictionactionrecord): array {
$predictioncontext = \context::instance_by_id($predictionactionrecord->contextid);
// Just 1 result, we just want to retrieve the prediction action names.
list ($unused, $predictions) = $this->model->get_predictions($predictioncontext, false, 0, 1);
// We pass 'true' for $isinsightuser so all the prediction actions available for this target are returning.
$predictionactions = $this->model->get_target()->prediction_actions(reset($predictions), true, true);
$actionlabels = [];
$actionvalues = ['separated' => [], 'grouped' => []];
$actiontypes = [];
foreach ($predictionactions as $action) {
$actionlabels['separated'][$action->get_action_name()] = $action->get_text();
$actionvalues['separated'][$action->get_action_name()] = 0;
$actiontypes[$action->get_action_name()] = $action->get_type();
}
$bulkactions = $this->model->get_target()->bulk_actions($predictions);
foreach ($bulkactions as $action) {
$actionlabels['separated'][$action->get_action_name()] = $action->get_text();
$actionvalues['separated'][$action->get_action_name()] = 0;
$actiontypes[$action->get_action_name()] = $action->get_type();
}
$actionlabels['grouped'][\core_analytics\action::TYPE_POSITIVE] = get_string('useful', 'analytics');
$actionlabels['grouped'][\core_analytics\action::TYPE_NEUTRAL] = get_string('neutral', 'analytics');
$actionlabels['grouped'][\core_analytics\action::TYPE_NEGATIVE] = get_string('notuseful', 'analytics');
$actionvalues['grouped'][\core_analytics\action::TYPE_POSITIVE] = 0;
$actionvalues['grouped'][\core_analytics\action::TYPE_NEUTRAL] = 0;
$actionvalues['grouped'][\core_analytics\action::TYPE_NEGATIVE] = 0;
return [$actionlabels, $actionvalues, $actiontypes];
}
}
@@ -0,0 +1,167 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Invalid analysables renderable.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die;
/**
* Invalid analysables renderable.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class invalid_analysables implements \renderable, \templatable {
/**
* @var \core_analytics\model
*/
protected $model = null;
/**
* @var int
*/
protected $page = 0;
/**
* @var int
*/
protected $perpage = 0;
/**
* Inits the invalid analysables renderable.
*
* @param \core_analytics\model $model
* @param int $page
* @param int $perpage
* @return \stdClass
*/
public function __construct(\core_analytics\model $model, $page, $perpage) {
$this->model = $model;
$this->page = $page;
$this->perpage = $perpage;
}
/**
* Export the data.
*
* @param \renderer_base $output
* @return \stdClass
*/
public function export_for_template(\renderer_base $output) {
global $PAGE;
$offset = $this->page * $this->perpage;
$contexts = $this->model->get_contexts();
$analysables = $this->model->get_analyser(['notimesplitting' => true])->get_analysables_iterator(null, $contexts);
$skipped = 0;
$enoughresults = false;
$morepages = false;
$results = array();
foreach ($analysables as $analysable) {
if (!$analysable) {
continue;
}
$validtraining = $this->model->get_target()->is_valid_analysable($analysable, true);
if ($validtraining === true) {
if ($this->model->is_static()) {
// We still want to show this analysable if it is not valid to get predictions.
$validtraining = get_string('notrainingbasedassumptions', 'analytics');
} else {
// We skip analysables that are valid for training or valid for prediction.
continue;
}
}
$validprediction = $this->model->get_target()->is_valid_analysable($analysable, false);
if ($validprediction === true) {
// We skip analysables that are valid for training or valid for prediction.
continue;
}
if ($offset && $skipped < $offset) {
$skipped++;
continue;
}
// Add a new results if we don't have enough yet.
if (!$enoughresults) {
$results[$analysable->get_id()] = array($analysable, $validtraining, $validprediction);
if ($this->perpage && count($results) === $this->perpage) {
$enoughresults = true;
}
} else {
// Confirmed that we have results we can not fit into this page.
$morepages = true;
break;
}
}
// Prepare the context object.
$data = new \stdClass();
$data->modelname = $this->model->get_name();
if ($this->page > 0) {
$prev = clone $PAGE->url;
$prev->param('page', $this->page - 1);
$button = new \single_button($prev, get_string('previouspage', 'tool_analytics'), 'get');
$data->prev = $button->export_for_template($output);
}
if ($morepages) {
$next = clone $PAGE->url;
$next->param('page', $this->page + 1);
$button = new \single_button($next, get_string('nextpage', 'tool_analytics'), 'get');
$data->next = $button->export_for_template($output);
}
$data->analysables = [];
foreach ($results as list($analysable, $validtraining, $validprediction)) {
$obj = new \stdClass();
$obj->url = \html_writer::link($analysable->get_context()->get_url(), $analysable->get_name(),
array('target' => '_blank'));
if ($validtraining !== true) {
$obj->validtraining = $validtraining;
}
if ($validprediction !== true) {
$obj->validprediction = $validprediction;
}
$data->analysables[] = $obj;
}
if (empty($data->analysables)) {
$data->noanalysables = [
'message' => get_string('noinvalidanalysables', 'tool_analytics'),
'announce' => true,
];
}
return $data;
}
}
@@ -0,0 +1,217 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Model logs table class.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->libdir . '/tablelib.php');
/**
* Model logs table class.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class model_logs extends \table_sql {
/**
* @var \core_analytics\model
*/
protected $model = null;
/**
* @var string|false
*/
protected $evaluationmode = false;
/**
* Sets up the table_log parameters.
*
* @param string $uniqueid unique id of form.
* @param \core_analytics\model $model
*/
public function __construct($uniqueid, $model) {
global $PAGE;
parent::__construct($uniqueid);
$this->model = $model;
$this->set_attribute('class', 'modellog generaltable generalbox');
$this->set_attribute('aria-live', 'polite');
$this->define_columns(array('time', 'version', 'evaluationmode', 'indicators', 'timesplitting',
'accuracy', 'info', 'usermodified'));
$this->define_headers(array(
get_string('time'),
get_string('version'),
get_string('evaluationmode', 'tool_analytics'),
get_string('indicators', 'tool_analytics'),
get_string('timesplittingmethod', 'analytics'),
get_string('accuracy', 'tool_analytics'),
get_string('info', 'tool_analytics'),
get_string('fullnameuser'),
));
$evaluationmodehelp = new \help_icon('evaluationmode', 'tool_analytics');
$this->define_help_for_headers([null, null, $evaluationmodehelp, null, null, null, null, null]);
$this->pageable(true);
$this->collapsible(false);
$this->sortable(false);
$this->is_downloadable(false);
$this->evaluationmode = optional_param('evaluationmode', false, PARAM_ALPHANUM);
if ($this->evaluationmode && $this->evaluationmode != 'configuration' && $this->evaluationmode != 'trainedmodel') {
$this->evaluationmode = '';
}
$this->define_baseurl($PAGE->url);
}
/**
* Generate the version column.
*
* @param \stdClass $log log data.
* @return string HTML for the version column
*/
public function col_version($log) {
$recenttimestr = get_string('strftimerecent', 'core_langconfig');
return userdate($log->version, $recenttimestr);
}
/**
* Generate the evaluation mode column.
*
* @param \stdClass $log log data.
* @return string HTML for the evaluationmode column
*/
public function col_evaluationmode($log) {
return get_string('evaluationmodecol' . $log->evaluationmode, 'tool_analytics');
}
/**
* Generate the time column.
*
* @param \stdClass $log log data.
* @return string HTML for the time column
*/
public function col_time($log) {
$recenttimestr = get_string('strftimerecent', 'core_langconfig');
return userdate($log->timecreated, $recenttimestr);
}
/**
* Generate the indicators column.
*
* @param \stdClass $log log data.
* @return string HTML for the indicators column
*/
public function col_indicators($log) {
$indicatorclasses = json_decode($log->indicators);
$indicators = array();
foreach ($indicatorclasses as $indicatorclass) {
$indicator = \core_analytics\manager::get_indicator($indicatorclass);
if ($indicator) {
$indicators[] = $indicator->get_name();
} else {
debugging('Can\'t load ' . $indicatorclass . ' indicator', DEBUG_DEVELOPER);
}
}
return '<ul><li>' . implode('</li><li>', $indicators) . '</li></ul>';
}
/**
* Generate the context column.
*
* @param \stdClass $log log data.
* @return string HTML for the context column
*/
public function col_timesplitting($log) {
$timesplitting = \core_analytics\manager::get_time_splitting($log->timesplitting);
return $timesplitting->get_name();
}
/**
* Generate the accuracy column.
*
* @param \stdClass $log log data.
* @return string HTML for the accuracy column
*/
public function col_accuracy($log) {
return strval(round($log->score * 100, 2)) . '%';
}
/**
* Generate the info column.
*
* @param \stdClass $log log data.
* @return string HTML for the score column
*/
public function col_info($log) {
global $PAGE;
if (empty($log->info) && empty($log->dir)) {
return '';
}
$info = array();
if (!empty($log->info)) {
$info = json_decode($log->info);
}
if (!empty($log->dir)) {
$info[] = get_string('predictorresultsin', 'tool_analytics', $log->dir);
}
$PAGE->requires->js_call_amd('tool_analytics/log_info', 'loadInfo', array($log->id, $info));
return \html_writer::link('#', get_string('view'), array('data-model-log-id' => $log->id));
}
/**
* Generate the usermodified column.
*
* @param \stdClass $log log data.
* @return string HTML for the usermodified column
*/
public function col_usermodified($log) {
$user = \core_user::get_user($log->usermodified);
return fullname($user);
}
/**
* Query the logs table. Store results in the object for use by build_table.
*
* @param int $pagesize size of page for paginated displayed table.
* @param bool $useinitialsbar do you want to use the initials bar.
*/
public function query_db($pagesize, $useinitialsbar = true) {
$total = count($this->model->get_logs());
$this->pagesize($pagesize, $total);
$this->rawdata = $this->model->get_logs($this->get_page_start(), $this->get_page_size());
// Set initial bars.
if ($useinitialsbar) {
$this->initialbars($total > $pagesize);
}
}
}
@@ -0,0 +1,387 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Prediction models list page.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die();
/**
* Shows tool_analytics models list.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class models_list implements \renderable, \templatable {
/**
* models
*
* @var \core_analytics\model[]
*/
protected $models = array();
/**
* __construct
*
* @param \core_analytics\model[] $models
* @return void
*/
public function __construct($models) {
$this->models = $models;
}
/**
* Exports the data.
*
* @param \renderer_base $output
* @return \stdClass
*/
public function export_for_template(\renderer_base $output) {
global $PAGE;
$data = new \stdClass();
$newmodelmenu = new \action_menu();
$newmodelmenu->set_menu_trigger(get_string('newmodel', 'tool_analytics'), 'btn btn-secondary');
$newmodelmenu->set_menu_left();
$newmodelmenu->add(new \action_menu_link(
new \moodle_url('/admin/tool/analytics/createmodel.php'),
new \pix_icon('i/edit', ''),
get_string('createmodel', 'tool_analytics'),
false
));
$newmodelmenu->add(new \action_menu_link(
new \moodle_url('/admin/tool/analytics/importmodel.php'),
new \pix_icon('i/import', ''),
get_string('importmodel', 'tool_analytics'),
false
));
$newmodelmenu->add(new \action_menu_link(
new \moodle_url('/admin/tool/analytics/restoredefault.php'),
new \pix_icon('i/reload', ''),
get_string('restoredefault', 'tool_analytics'),
false
));
$data->newmodelmenu = $newmodelmenu->export_for_template($output);
$onlycli = get_config('analytics', 'onlycli');
if ($onlycli === false) {
// Default applied if no config found.
$onlycli = 1;
}
// Evaluation options.
$timesplittingsforevaluation = \core_analytics\manager::get_time_splitting_methods_for_evaluation(true);
$misconfiguredmodels = [];
$data->models = array();
foreach ($this->models as $model) {
$modeldata = $model->export($output);
// Check if there is a help icon for the target to show.
$identifier = $modeldata->target->get_identifier();
$component = $modeldata->target->get_component();
if (get_string_manager()->string_exists($identifier . '_help', $component)) {
$helpicon = new \help_icon($identifier, $component);
$modeldata->targethelp = $helpicon->export_for_template($output);
} else {
// We really want to encourage developers to add help to their targets.
debugging("The target '{$modeldata->target}' should include a '{$identifier}_help' string to
describe its purpose.", DEBUG_DEVELOPER);
}
if ($model->invalid_timesplitting_selected()) {
$misconfiguredmodels[$model->get_id()] = $model->get_name();
}
// Check if there is a help icon for the indicators to show.
if (!empty($modeldata->indicators)) {
$indicators = array();
foreach ($modeldata->indicators as $ind) {
// Create the indicator with the details we want for the context.
$indicator = new \stdClass();
$indicator->name = $ind->out();
$identifier = $ind->get_identifier();
$component = $ind->get_component();
if (get_string_manager()->string_exists($identifier . '_help', $component)) {
$helpicon = new \help_icon($identifier, $component);
$indicator->help = $helpicon->export_for_template($output);
} else {
// We really want to encourage developers to add help to their indicators.
debugging("The indicator '{$ind}' should include a '{$identifier}_help' string to
describe its purpose.", DEBUG_DEVELOPER);
}
$indicators[] = $indicator;
}
$modeldata->indicators = $indicators;
}
$modeldata->indicatorsnum = count($modeldata->indicators);
// Check if there is a help icon for the time splitting method.
if (!empty($modeldata->timesplitting)) {
$identifier = $modeldata->timesplitting->get_identifier();
$component = $modeldata->timesplitting->get_component();
if (get_string_manager()->string_exists($identifier . '_help', $component)) {
$helpicon = new \help_icon($identifier, $component);
$modeldata->timesplittinghelp = $helpicon->export_for_template($output);
} else {
// We really want to encourage developers to add help to their time splitting methods.
debugging("The analysis interval '{$modeldata->timesplitting}' should include a '{$identifier}_help'
string to describe its purpose.", DEBUG_DEVELOPER);
}
} else {
$helpicon = new \help_icon('timesplittingnotdefined', 'tool_analytics');
$modeldata->timesplittinghelp = $helpicon->export_for_template($output);
}
// Has this model generated predictions?.
$predictioncontexts = $model->get_predictions_contexts();
$anypredictionobtained = $model->any_prediction_obtained();
// Model predictions list.
if (!$model->is_enabled()) {
$modeldata->noinsights = get_string('disabledmodel', 'analytics');
} else if ($model->uses_insights()) {
if ($predictioncontexts) {
$url = new \moodle_url('/report/insights/insights.php', array('modelid' => $model->get_id()));
$modeldata->insights = \tool_analytics\output\helper::prediction_context_selector($predictioncontexts,
$url, $output);
}
if (empty($modeldata->insights)) {
if ($anypredictionobtained) {
$modeldata->noinsights = get_string('noinsights', 'analytics');
} else {
$modeldata->noinsights = get_string('nopredictionsyet', 'analytics');
}
}
} else {
$modeldata->noinsights = get_string('noinsightsmodel', 'analytics');
}
// Actions.
$actionsmenu = new \action_menu();
$actionsmenu->set_menu_trigger(get_string('actions'));
$actionsmenu->set_owner_selector('model-actions-' . $model->get_id());
$urlparams = ['id' => $model->get_id(), 'sesskey' => sesskey()];
// Get predictions.
if (!$onlycli && $modeldata->enabled && !empty($modeldata->timesplitting)) {
$urlparams['action'] = 'scheduledanalysis';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url,
new \pix_icon('i/notifications', get_string('executescheduledanalysis', 'tool_analytics')),
get_string('executescheduledanalysis', 'tool_analytics'));
$actionsmenu->add($icon);
}
// Evaluate machine-learning-based models.
if (!$onlycli && $model->get_indicators() && !$model->is_static()) {
// Extra is_trained call as trained_locally returns false if the model has not been trained yet.
$trainedonlyexternally = !$model->trained_locally() && $model->is_trained();
$actionid = 'evaluate-' . $model->get_id();
// Evaluation options.
$modeltimesplittingmethods = $this->timesplittings_options_for_evaluation($model, $timesplittingsforevaluation);
// Include the current time-splitting method as the default selection method the model already have one.
if ($model->get_model_obj()->timesplitting) {
$currenttimesplitting = ['id' => 'current', 'text' => get_string('currenttimesplitting', 'tool_analytics')];
array_unshift($modeltimesplittingmethods, $currenttimesplitting);
}
$evaluateparams = [$actionid, $trainedonlyexternally];
$PAGE->requires->js_call_amd('tool_analytics/model', 'selectEvaluationOptions', $evaluateparams);
$urlparams['action'] = 'evaluate';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('i/calc', get_string('evaluate', 'tool_analytics')),
get_string('evaluate', 'tool_analytics'), ['data-action-id' => $actionid,
'data-timesplitting-methods' => json_encode($modeltimesplittingmethods)]);
$actionsmenu->add($icon);
}
// Machine-learning-based models evaluation log.
if (!$model->is_static() && $model->get_logs()) {
$urlparams['action'] = 'log';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('i/report', get_string('viewlog', 'tool_analytics')),
get_string('viewlog', 'tool_analytics'));
$actionsmenu->add($icon);
}
// Edit model.
$urlparams['action'] = 'edit';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('t/edit', get_string('edit')), get_string('edit'));
$actionsmenu->add($icon);
// Enable / disable.
if ($model->is_enabled() || !empty($modeldata->timesplitting)) {
// If there is no timesplitting method set, the model can not be enabled.
if ($model->is_enabled()) {
$action = 'disable';
$text = get_string('disable');
$icontype = 't/block';
} else {
$action = 'enable';
$text = get_string('enable');
$icontype = 'i/checked';
}
$urlparams['action'] = $action;
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon($icontype, $text), $text);
$actionsmenu->add($icon);
}
// Export.
if (!$model->is_static()) {
$fullysetup = $model->get_indicators() && !empty($modeldata->timesplitting);
$istrained = $model->is_trained();
if ($fullysetup || $istrained) {
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
// Clear the previous action param from the URL, we will set it in JS.
$url->remove_params('action');
$actionid = 'export-' . $model->get_id();
$PAGE->requires->js_call_amd('tool_analytics/model', 'selectExportOptions',
[$actionid, $istrained]);
$icon = new \action_menu_link_secondary($url, new \pix_icon('i/export',
get_string('export', 'tool_analytics')), get_string('export', 'tool_analytics'),
['data-action-id' => $actionid]);
$actionsmenu->add($icon);
}
}
// Insights report.
if (!empty($anypredictionobtained) && $model->uses_insights()) {
$urlparams['action'] = 'insightsreport';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$pix = new \pix_icon('i/report', get_string('insightsreport', 'tool_analytics'));
$icon = new \action_menu_link_secondary($url, $pix, get_string('insightsreport', 'tool_analytics'));
$actionsmenu->add($icon);
}
// Invalid analysables.
$analyser = $model->get_analyser(['notimesplitting' => true]);
if (!$analyser instanceof \core_analytics\local\analyser\sitewide) {
$urlparams['action'] = 'invalidanalysables';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$pix = new \pix_icon('i/report', get_string('invalidanalysables', 'tool_analytics'));
$icon = new \action_menu_link_secondary($url, $pix, get_string('invalidanalysables', 'tool_analytics'));
$actionsmenu->add($icon);
}
// Clear model.
if (!empty($anypredictionobtained) || $model->is_trained()) {
$actionid = 'clear-' . $model->get_id();
$PAGE->requires->js_call_amd('tool_analytics/model', 'confirmAction', [$actionid, 'clear']);
$urlparams['action'] = 'clear';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('e/cleanup_messy_code',
get_string('clearpredictions', 'tool_analytics')), get_string('clearpredictions', 'tool_analytics'),
['data-action-id' => $actionid]);
$actionsmenu->add($icon);
}
// Delete model.
$actionid = 'delete-' . $model->get_id();
$PAGE->requires->js_call_amd('tool_analytics/model', 'confirmAction', [$actionid, 'delete']);
$urlparams['action'] = 'delete';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('t/delete',
get_string('delete', 'tool_analytics')), get_string('delete', 'tool_analytics'),
['data-action-id' => $actionid]);
$actionsmenu->add($icon);
$modeldata->actions = $actionsmenu->export_for_template($output);
$data->models[] = $modeldata;
}
$data->warnings = [];
$data->infos = [];
if (!$onlycli) {
$data->warnings[] = (object)array('message' => get_string('bettercli', 'tool_analytics'), 'closebutton' => true);
} else {
$url = new \moodle_url('/admin/settings.php', array('section' => 'analyticssettings'),
'id_s_analytics_onlycli');
$langstrid = 'clievaluationandpredictionsnoadmin';
if (is_siteadmin()) {
$langstrid = 'clievaluationandpredictions';
}
$data->infos[] = (object)array('message' => get_string($langstrid, 'tool_analytics', $url->out()),
'closebutton' => true);
}
if ($misconfiguredmodels) {
$warningstr = get_string('invalidtimesplittinginmodels', 'tool_analytics', implode(', ', $misconfiguredmodels));
$data->warnings[] = (object)array('message' => $warningstr, 'closebutton' => true);
}
return $data;
}
/**
* Returns the list of time splitting methods that are available for evaluation.
*
* @param \core_analytics\model $model
* @param array $timesplittingsforevaluation
* @return array
*/
private function timesplittings_options_for_evaluation(\core_analytics\model $model,
array $timesplittingsforevaluation): array {
$modeltimesplittingmethods = [
['id' => 'all', 'text' => get_string('alltimesplittingmethods', 'tool_analytics')],
];
$potentialtimesplittingmethods = $model->get_potential_timesplittings();
foreach ($timesplittingsforevaluation as $timesplitting) {
if (empty($potentialtimesplittingmethods[$timesplitting->get_id()])) {
// This time-splitting method can not be used for this model.
continue;
}
$modeltimesplittingmethods[] = [
'id' => \tool_analytics\output\helper::class_to_option($timesplitting->get_id()),
'text' => $timesplitting->get_name()->out(),
];
}
return $modeltimesplittingmethods;
}
}
@@ -0,0 +1,259 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Renderer.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die();
use plugin_renderer_base;
/**
* Renderer class.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends plugin_renderer_base {
/**
* Defer to template.
*
* @param \tool_analytics\output\models_list $modelslist
* @return string HTML
*/
protected function render_models_list(\tool_analytics\output\models_list $modelslist) {
$data = $modelslist->export_for_template($this);
return parent::render_from_template('tool_analytics/models_list', $data);
}
/**
* Renders a table.
*
* @param \table_sql $table
* @return string HTML
*/
public function render_table(\table_sql $table) {
ob_start();
$table->out(10, true);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
/**
* Web interface evaluate results.
*
* @param \stdClass[] $results
* @param string[] $logs
* @return string HTML
*/
public function render_evaluate_results($results, $logs = array()) {
$output = '';
foreach ($results as $timesplittingid => $result) {
if (!CLI_SCRIPT) {
$output .= $this->output->box_start('generalbox mb-3');
}
// Check that the array key is a string, not all results depend on time splitting methods (e.g. general errors).
if (!is_numeric($timesplittingid)) {
$timesplitting = \core_analytics\manager::get_time_splitting($timesplittingid);
$langstrdata = (object)array('name' => $timesplitting->get_name(), 'id' => $timesplittingid);
if (CLI_SCRIPT) {
$output .= $this->output->heading(get_string('scheduledanalysisresultscli', 'tool_analytics', $langstrdata), 3);
} else {
$output .= $this->output->heading(get_string('scheduledanalysisresults', 'tool_analytics', $langstrdata), 3);
}
}
if ($result->status == 0) {
$output .= $this->output->notification(get_string('goodmodel', 'tool_analytics'),
\core\output\notification::NOTIFY_SUCCESS);
} else if ($result->status === \core_analytics\model::NO_DATASET) {
$output .= $this->output->notification(get_string('nodatatoevaluate', 'tool_analytics'),
\core\output\notification::NOTIFY_WARNING);
}
if (isset($result->score)) {
// Score.
$output .= $this->output->heading(get_string('accuracy', 'tool_analytics') . ': ' .
round(floatval($result->score), 4) * 100 . '%', 4);
}
if (!empty($result->info)) {
foreach ($result->info as $message) {
$output .= $this->output->notification($message, \core\output\notification::NOTIFY_WARNING);
}
}
if (!CLI_SCRIPT) {
$output .= $this->output->box_end();
}
}
// Info logged during evaluation.
if (!empty($logs) && debugging()) {
$output .= $this->output->heading(get_string('extrainfo', 'tool_analytics'), 3);
foreach ($logs as $log) {
$output .= $this->output->notification($log, \core\output\notification::NOTIFY_WARNING);
}
}
if (!CLI_SCRIPT) {
$output .= $this->output->single_button(new \moodle_url('/admin/tool/analytics/index.php'),
get_string('continue'), 'get');
}
return $output;
}
/**
* Web interface training & prediction results.
*
* @param \stdClass|false $trainresults
* @param string[] $trainlogs
* @param \stdClass|false $predictresults
* @param string[] $predictlogs
* @return string HTML
*/
public function render_get_predictions_results($trainresults = false, $trainlogs = array(), $predictresults = false, $predictlogs = array()) {
$output = '';
if ($trainresults || (!empty($trainlogs) && debugging())) {
$output .= $this->output->heading(get_string('trainingresults', 'tool_analytics'), 3);
}
if ($trainresults) {
if ($trainresults->status == 0) {
$output .= $this->output->notification(
get_string('trainingprocessfinished', 'tool_analytics'),
\core\output\notification::NOTIFY_SUCCESS);
} else if ($trainresults->status === \core_analytics\model::NO_DATASET ||
$trainresults->status === \core_analytics\model::NOT_ENOUGH_DATA) {
$output .= $this->output->notification(
get_string('nodatatotrain', 'tool_analytics'),
\core\output\notification::NOTIFY_WARNING);
} else {
$output .= $this->output->notification(
get_string('generalerror', 'tool_analytics', $trainresults->status),
\core\output\notification::NOTIFY_ERROR);
}
}
if (!empty($trainlogs) && debugging()) {
$output .= $this->output->heading(get_string('extrainfo', 'tool_analytics'), 4);
foreach ($trainlogs as $log) {
$output .= $this->output->notification($log, \core\output\notification::NOTIFY_WARNING);
}
}
if ($predictresults || (!empty($predictlogs) && debugging())) {
$output .= $this->output->heading(
get_string('predictionresults', 'tool_analytics'), 3, 'main mt-3');
}
if ($predictresults) {
if ($predictresults->status == 0) {
$output .= $this->output->notification(
get_string('predictionprocessfinished', 'tool_analytics'),
\core\output\notification::NOTIFY_SUCCESS);
} else if ($predictresults->status === \core_analytics\model::NO_DATASET ||
$predictresults->status === \core_analytics\model::NOT_ENOUGH_DATA) {
$output .= $this->output->notification(
get_string('nodatatopredict', 'tool_analytics'),
\core\output\notification::NOTIFY_WARNING);
} else {
$output .= $this->output->notification(
get_string('generalerror', 'tool_analytics', $predictresults->status),
\core\output\notification::NOTIFY_ERROR);
}
}
if (!empty($predictlogs) && debugging()) {
$output .= $this->output->heading(get_string('extrainfo', 'tool_analytics'), 4);
foreach ($predictlogs as $log) {
$output .= $this->output->notification($log, \core\output\notification::NOTIFY_WARNING);
}
}
if (!CLI_SCRIPT) {
$output .= $this->output->single_button(new \moodle_url('/admin/tool/analytics/index.php'),
get_string('continue'), 'get');
}
return $output;
}
/**
* Defer to template.
*
* @param \tool_analytics\output\insights_report $insightsreport
* @return string HTML
*/
protected function render_insights_report(\tool_analytics\output\insights_report $insightsreport): string {
$data = $insightsreport->export_for_template($this);
return parent::render_from_template('tool_analytics/insights_report', $data);
}
/**
* Defer to template.
*
* @param \tool_analytics\output\invalid_analysables $invalidanalysables
* @return string HTML
*/
protected function render_invalid_analysables(\tool_analytics\output\invalid_analysables $invalidanalysables) {
$data = $invalidanalysables->export_for_template($this);
return parent::render_from_template('tool_analytics/invalid_analysables', $data);
}
/**
* Renders an analytics disabled notification.
*
* @return string HTML
*/
public function render_analytics_disabled() {
global $FULLME;
$this->page->set_url($FULLME);
$this->page->set_title(get_string('pluginname', 'tool_analytics'));
$this->page->set_heading(get_string('pluginname', 'tool_analytics'));
$output = $this->output->header();
$output .= $this->output->notification(get_string('analyticsdisabled', 'analytics'),
\core\output\notification::NOTIFY_INFO);
$output .= \html_writer::tag('a', get_string('continue'), ['class' => 'btn btn-primary',
'href' => (new \moodle_url('/'))->out()]);
$output .= $this->output->footer();
return $output;
}
}
@@ -0,0 +1,142 @@
<?php
// This file is part of Moodle - https://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 <http://www.gnu.org/licenses/>.
/**
* Provides {@link \tool_analytics\output\restorable_models} class.
*
* @package tool_analytics
* @category output
* @copyright 2019 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die();
/**
* Represents the list of default models that can be eventually restored.
*
* @copyright 2019 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restorable_models implements \renderable, \templatable {
/** @var array */
protected $models;
/**
* Instantiate an object of this class.
*
* @param array $models List of models as returned by {@link \core_analytics\manager::load_default_models_for_all_components()}
*/
public function __construct(array $models) {
$this->models = $models;
}
/**
* Export the list of models to be rendered.
*
* @param \renderer_base $output
* @return string
*/
public function export_for_template(\renderer_base $output) {
$components = [];
foreach ($this->models as $componentname => $modelslist) {
$component = [
'name' => $this->component_name($componentname),
'component' => $componentname,
'models' => [],
];
foreach ($modelslist as $definition) {
list($target, $indicators) = \core_analytics\manager::get_declared_target_and_indicators_instances($definition);
if (\core_analytics\model::exists($target, $indicators)) {
continue;
}
$targetnamelangstring = $target->get_name();
$model = [
'defid' => \core_analytics\manager::model_declaration_identifier($definition),
'targetname' => $targetnamelangstring,
'targetclass' => $definition['target'],
'indicatorsnum' => count($definition['indicators']),
'indicators' => [],
];
if (get_string_manager()->string_exists($targetnamelangstring->get_identifier().'_help',
$targetnamelangstring->get_component())) {
$helpicon = new \help_icon($targetnamelangstring->get_identifier(), $targetnamelangstring->get_component());
$model['targethelp'] = $helpicon->export_for_template($output);
}
foreach ($indicators as $indicator) {
$indicatornamelangstring = $indicator->get_name();
$indicatordata = [
'name' => $indicatornamelangstring,
'classname' => $indicator->get_id(),
];
if (get_string_manager()->string_exists($indicatornamelangstring->get_identifier().'_help',
$indicatornamelangstring->get_component())) {
$helpicon = new \help_icon($indicatornamelangstring->get_identifier(),
$indicatornamelangstring->get_component());
$indicatordata['indicatorhelp'] = $helpicon->export_for_template($output);
}
$model['indicators'][] = $indicatordata;
}
$component['models'][] = $model;
}
if (!empty($component['models'])) {
$components[] = $component;
}
}
$result = [
'hasdata' => !empty($components),
'components' => array_values($components),
'submiturl' => new \moodle_url('/admin/tool/analytics/restoredefault.php'),
'backurl' => new \moodle_url('/admin/tool/analytics/index.php'),
'sesskey' => sesskey(),
];
return $result;
}
/**
* Return a human readable name for the given frankenstyle component.
*
* @param string $component Frankenstyle component such as 'core', 'core_analytics' or 'mod_workshop'
* @return string Human readable name of the component
*/
protected function component_name(string $component): string {
if ($component === 'core' || strpos($component, 'core_')) {
return get_string('componentcore', 'tool_analytics');
} else {
return get_string('pluginname', $component);
}
}
}
@@ -0,0 +1,46 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for tool_analytics.
*
* @package tool_analytics
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for tool_analytics implementing null_provider.
*
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,82 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Predict system models with new data available.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\task;
defined('MOODLE_INTERNAL') || die();
/**
* Predict system models with new data available.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class predict_models extends \core\task\scheduled_task {
/**
* get_name
*
* @return string
*/
public function get_name() {
return get_string('predictmodels', 'tool_analytics');
}
/**
* Executes the prediction task.
*
* @return void
*/
public function execute() {
global $OUTPUT, $PAGE;
if (!\core_analytics\manager::is_analytics_enabled()) {
mtrace(get_string('analyticsdisabled', 'analytics'));
return;
}
$models = \core_analytics\manager::get_all_models(true, true);
if (!$models) {
mtrace(get_string('errornoenabledandtrainedmodels', 'tool_analytics'));
return;
}
foreach ($models as $model) {
$renderer = $PAGE->get_renderer('tool_analytics');
$result = $model->predict();
// Reset the page as some indicators may call external functions that overwrite the page context.
\tool_analytics\output\helper::reset_page();
if ($result) {
echo $OUTPUT->heading(get_string('modelresults', 'tool_analytics', $model->get_name()));
echo $renderer->render_get_predictions_results(false, array(), $result, $model->get_analyser()->get_logs());
}
}
}
}
@@ -0,0 +1,91 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Train system models with new data available.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\task;
defined('MOODLE_INTERNAL') || die();
/**
* Train system models with new data available.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class train_models extends \core\task\scheduled_task {
/**
* get_name
*
* @return string
*/
public function get_name() {
return get_string('trainmodels', 'tool_analytics');
}
/**
* Executes the prediction task.
*
* @return void
*/
public function execute() {
global $OUTPUT, $PAGE;
if (!\core_analytics\manager::is_analytics_enabled()) {
mtrace(get_string('analyticsdisabled', 'analytics'));
return;
}
$models = \core_analytics\manager::get_all_models(true);
if (!$models) {
mtrace(get_string('errornoenabledmodels', 'tool_analytics'));
return;
}
foreach ($models as $model) {
if ($model->is_static()) {
// Skip models based on assumptions.
continue;
}
if (!$model->get_time_splitting()) {
// Can not train if there is no time splitting method selected.
continue;
}
$renderer = $PAGE->get_renderer('tool_analytics');
$result = $model->train();
// Reset the page as some indicators may call external functions that overwrite the page context.
\tool_analytics\output\helper::reset_page();
if ($result) {
echo $OUTPUT->heading(get_string('modelresults', 'tool_analytics', $model->get_name()));
echo $renderer->render_get_predictions_results($result, $model->get_analyser()->get_logs());
}
}
}
}
+84
View File
@@ -0,0 +1,84 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Enables the provided model.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require_once(__DIR__ . '/../../../../config.php');
require_once($CFG->libdir.'/clilib.php');
$help = "Enables the provided model.
Options:
--modelid Model id
--list List models
--analysisinterval Time splitting method full class name
-h, --help Print out this help
Example:
\$ php admin/tool/analytics/cli/enable_model.php --modelid=1 --analysisinterval=\"\\core\\analytics\\time_splitting\\quarters\"
";
// Now get cli options.
list($options, $unrecognized) = cli_get_params(
array(
'help' => false,
'list' => false,
'modelid' => false,
'analysisinterval' => false
),
array(
'h' => 'help',
)
);
if ($options['help']) {
echo $help;
exit(0);
}
if (!\core_analytics\manager::is_analytics_enabled()) {
echo get_string('analyticsdisabled', 'analytics') . PHP_EOL;
exit(0);
}
if ($options['list'] || $options['modelid'] === false) {
\tool_analytics\clihelper::list_models();
exit(0);
}
if ($options['analysisinterval'] === false) {
echo $help;
exit(0);
}
// We need admin permissions.
\core\session\manager::set_user(get_admin());
$model = new \core_analytics\model($options['modelid']);
// Evaluate its suitability to predict accurately.
$model->enable($options['analysisinterval']);
cli_heading(get_string('success'));
exit(0);
+152
View File
@@ -0,0 +1,152 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Evaluates the provided model.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require_once(__DIR__ . '/../../../../config.php');
require_once($CFG->libdir.'/clilib.php');
$help = "Evaluates the provided model.
Options:
--modelid Model id
--list List models
--non-interactive Not interactive questions
--analysisinterval Restrict the evaluation to 1 single analysis interval (Optional)
--mode 'configuration' or 'trainedmodel'. You can only use mode=trainedmodel when the trained" .
" model was imported" . "
--reuse-prev-analysed Reuse recently analysed courses instead of analysing the whole site. Set it to false while" .
" coding indicators. Defaults to true (Optional)" . "
-h, --help Print out this help
Example:
\$ php admin/tool/analytics/cli/evaluate_model.php --modelid=1 --analysisinterval='\\core\\analytics\\time_splitting\\quarters'
";
// Now get cli options.
list($options, $unrecognized) = cli_get_params(
array(
'help' => false,
'modelid' => false,
'list' => false,
'analysisinterval' => false,
'mode' => 'configuration',
'reuse-prev-analysed' => true,
'non-interactive' => false,
),
array(
'h' => 'help',
)
);
if ($options['help']) {
echo $help;
exit(0);
}
if (!\core_analytics\manager::is_analytics_enabled()) {
echo get_string('analyticsdisabled', 'analytics') . PHP_EOL;
exit(0);
}
if ($options['list']) {
\tool_analytics\clihelper::list_models();
exit(0);
}
if ($options['modelid'] === false) {
// All actions but --list require a modelid.
echo $help;
exit(0);
}
if ($options['mode'] !== 'configuration' && $options['mode'] !== 'trainedmodel') {
cli_error('Error: The provided mode is not supported');
}
if ($options['mode'] == 'trainedmodel' && $options['analysisinterval']) {
cli_error('Sorry, no analysis interval can be specified when using \'trainedmodel\' mode.');
}
// We need admin permissions.
\core\session\manager::set_user(get_admin());
$model = new \core_analytics\model($options['modelid']);
mtrace(get_string('analysingsitedata', 'tool_analytics'));
if ($options['reuse-prev-analysed']) {
mtrace(get_string('evaluationinbatches', 'tool_analytics'));
}
$renderer = $PAGE->get_renderer('tool_analytics');
$analyseroptions = array(
'timesplitting' => $options['analysisinterval'],
'reuseprevanalysed' => $options['reuse-prev-analysed'],
'mode' => $options['mode'],
);
// Evaluate its suitability to predict accurately.
$results = $model->evaluate($analyseroptions);
// Reset the page as some indicators may call external functions that overwrite the page context.
\tool_analytics\output\helper::reset_page();
echo $renderer->render_evaluate_results($results, $model->get_analyser()->get_logs());
// Check that we have, at leasa,t 1 valid dataset (not necessarily good) to use.
foreach ($results as $result) {
if ($result->status !== \core_analytics\model::NO_DATASET &&
$result->status !== \core_analytics\model::GENERAL_ERROR) {
$validdatasets = true;
}
}
if (!empty($validdatasets) && !$model->is_enabled() && $options['non-interactive'] === false) {
// Select a dataset, train and enable the model.
$input = cli_input(get_string('clienablemodel', 'tool_analytics'));
while (!\core_analytics\manager::is_valid($input, '\core_analytics\local\time_splitting\base') && $input !== 'none') {
mtrace(get_string('errorunexistingtimesplitting', 'analytics'));
$input = cli_input(get_string('clienablemodel', 'tool_analytics'));
}
if ($input === 'none') {
exit(0);
}
// Refresh the instance to prevent unexpected issues.
$model = new \core_analytics\model($options['modelid']);
// Set the time splitting method file and enable it.
$model->enable($input);
mtrace(get_string('trainandpredictmodel', 'tool_analytics'));
// Train the model with the selected time splitting method and start predicting.
$model->train();
$model->predict();
}
exit(0);
@@ -0,0 +1,229 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Guesses course start and end dates based on activity logs.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require_once(__DIR__ . '/../../../../config.php');
require_once($CFG->libdir.'/clilib.php');
require_once($CFG->dirroot . '/course/lib.php');
require_once($CFG->dirroot . '/course/format/weeks/lib.php');
$help = "Guesses course start and end dates based on activity logs.
IMPORTANT: Don't use this script if you keep previous academic years users enrolled in courses. Guesses would not be accurate.
Options:
--guessstart Guess the course start date (default to true)
--guessend Guess the course end date (default to true)
--guessall Guess all start and end dates, even if they are already set (default to false)
--update Update the db or just notify the guess (default to false)
--filter Analyser dependant. e.g. A courseid would evaluate the model using a single course (Optional)
-h, --help Print out this help
Example:
\$ php admin/tool/analytics/cli/guess_course_start_and_end_dates.php --update=1 --filter=123,321
";
// Now get cli options.
list($options, $unrecognized) = cli_get_params(
array(
'help' => false,
'guessstart' => true,
'guessend' => true,
'guessall' => false,
'update' => false,
'filter' => false
),
array(
'h' => 'help',
)
);
if ($options['help']) {
echo $help;
exit(0);
}
if ($options['guessstart'] === false && $options['guessend'] === false && $options['guessall'] === false) {
echo $help;
exit(0);
}
// Reformat them as an array.
if ($options['filter'] !== false) {
$options['filter'] = explode(',', clean_param($options['filter'], PARAM_SEQUENCE));
}
// We need admin permissions.
\core\session\manager::set_user(get_admin());
$conditions = array('id != 1');
if (!$options['guessall']) {
if ($options['guessstart']) {
$conditions[] = '(startdate is null or startdate = 0)';
}
if ($options['guessend']) {
$conditions[] = '(enddate is null or enddate = 0)';
}
}
$coursessql = '';
$params = null;
if ($options['filter']) {
list($coursessql, $params) = $DB->get_in_or_equal($options['filter'], SQL_PARAMS_NAMED);
$conditions[] = 'id ' . $coursessql;
}
$courses = $DB->get_recordset_select('course', implode(' AND ', $conditions), $params, 'sortorder ASC');
foreach ($courses as $course) {
tool_analytics_calculate_course_dates($course, $options);
}
$courses->close();
/**
* tool_analytics_calculate_course_dates
*
* @param stdClass $course
* @param array $options CLI options
* @return void
*/
function tool_analytics_calculate_course_dates($course, $options) {
global $DB, $OUTPUT;
$courseman = new \core_analytics\course($course);
$notification = $course->shortname . ' (id = ' . $course->id . '): ';
$originalenddate = null;
$guessedstartdate = null;
$guessedenddate = null;
$samestartdate = null;
$lowerenddate = null;
if ($options['guessstart'] || $options['guessall']) {
$originalstartdate = $course->startdate;
$guessedstartdate = $courseman->guess_start();
$samestartdate = ($guessedstartdate == $originalstartdate);
$lowerenddate = ($course->enddate && ($course->enddate < $guessedstartdate));
if ($samestartdate) {
if (!$guessedstartdate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessstartdate', 'tool_analytics');
} else {
// No need to update.
$notification .= PHP_EOL . ' ' . get_string('samestartdate', 'tool_analytics') . ': ' . userdate($guessedstartdate);
}
} else if (!$guessedstartdate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessstartdate', 'tool_analytics');
} else if ($lowerenddate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessstartdate', 'tool_analytics') . ': ' .
get_string('enddatebeforestartdate', 'error') . ' - ' . userdate($guessedstartdate);
} else {
// Update it to something we guess.
// We set it to $course even if we don't update because may be needed to guess the end one.
$course->startdate = $guessedstartdate;
$notification .= PHP_EOL . ' ' . get_string('startdate') . ': ' . userdate($guessedstartdate);
// Two different course updates because week's end date may be recalculated after setting the start date.
if ($options['update']) {
update_course($course);
// Refresh course data as end date may have been updated.
$course = $DB->get_record('course', array('id' => $course->id));
$courseman = new \core_analytics\course($course);
}
}
}
if ($options['guessend'] || $options['guessall']) {
if (!empty($lowerenddate) && !empty($guessedstartdate)) {
$course->startdate = $guessedstartdate;
}
$originalenddate = $course->enddate;
$format = course_get_format($course);
$formatoptions = $format->get_format_options();
// Change this for a course formats API level call in MDL-60702.
if ((get_class($format) == 'format_weeks' || is_subclass_of($format, 'format_weeks')) &&
method_exists($format, 'update_end_date') && $formatoptions['automaticenddate']) {
// Special treatment for weeks-based formats with automatic end date.
if ($options['update']) {
$format::update_end_date($course->id);
$course->enddate = $DB->get_field('course', 'enddate', array('id' => $course->id));
$notification .= PHP_EOL . ' ' . get_string('weeksenddateautomaticallyset', 'tool_analytics') . ': ' .
userdate($course->enddate);
} else {
// We can't provide more info without actually updating it in db.
$notification .= PHP_EOL . ' ' . get_string('weeksenddatedefault', 'tool_analytics');
}
} else {
$guessedenddate = $courseman->guess_end();
if ($guessedenddate == $originalenddate) {
if (!$guessedenddate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessenddate', 'tool_analytics');
} else {
// No need to update.
$notification .= PHP_EOL . ' ' . get_string('sameenddate', 'tool_analytics') . ': ' . userdate($guessedenddate);
}
} else if (!$guessedenddate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessenddate', 'tool_analytics');
} else {
// Update it to something we guess.
$course->enddate = $guessedenddate;
$updateit = false;
if ($course->enddate < $course->startdate) {
$notification .= PHP_EOL . ' ' . get_string('errorendbeforestart', 'course', userdate($course->enddate));
} else if ($course->startdate + (YEARSECS + (WEEKSECS * 4)) > $course->enddate) {
$notification .= PHP_EOL . ' ' . get_string('coursetoolong', 'course');
} else {
$notification .= PHP_EOL . ' ' . get_string('enddate') . ': ' . userdate($course->enddate);
$updateit = true;
}
if ($options['update'] && $updateit) {
update_course($course);
}
}
}
}
mtrace($notification);
}
mtrace(get_string('success'));
exit(0);
+105
View File
@@ -0,0 +1,105 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Create model form.
*
* @package tool_analytics
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../config.php');
require_login();
\core_analytics\manager::check_can_manage_models();
if (!\core_analytics\manager::is_analytics_enabled()) {
$PAGE->set_context(\context_system::instance());
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_analytics_disabled();
exit(0);
}
$returnurl = new \moodle_url('/admin/tool/analytics/index.php');
$url = new \moodle_url('/admin/tool/analytics/createmodel.php');
$title = get_string('createmodel', 'tool_analytics');
\tool_analytics\output\helper::set_navbar($title, $url);
// Static targets are not editable, we discard them.
$targets = array_filter(\core_analytics\manager::get_all_targets(), function($target) {
return (!$target->based_on_assumptions());
});
// Set 'supportscontexts' to true as at this stage we don't know if the contexts are supported by
// the selected target.
$customdata = array(
'trainedmodel' => false,
'staticmodel' => false,
'targets' => $targets,
'indicators' => \core_analytics\manager::get_all_indicators(),
'timesplittings' => \core_analytics\manager::get_all_time_splittings(),
'predictionprocessors' => \core_analytics\manager::get_all_prediction_processors(),
'supportscontexts' => true,
);
$mform = new \tool_analytics\output\form\edit_model(null, $customdata);
if ($mform->is_cancelled()) {
redirect($returnurl);
} else if ($data = $mform->get_data()) {
// Converting option names to class names.
$targetclass = \tool_analytics\output\helper::option_to_class($data->target);
if (empty($targets[$targetclass])) {
throw new \moodle_exception('errorinvalidtarget', 'analytics', '', $targetclass);
}
$target = $targets[$targetclass];
$indicators = array();
foreach ($data->indicators as $indicator) {
$indicatorinstance = \core_analytics\manager::get_indicator(
\tool_analytics\output\helper::option_to_class($indicator)
);
$indicators[$indicatorinstance->get_id()] = $indicatorinstance;
}
$timesplitting = \tool_analytics\output\helper::option_to_class($data->timesplitting);
$predictionsprocessor = \tool_analytics\output\helper::option_to_class($data->predictionsprocessor);
// Insert the model into db.
$model = \core_analytics\model::create($target, []);
// Filter out indicators that can not be used by this target.
$invalidindicators = array_diff_key($indicators, $model->get_potential_indicators());
if ($invalidindicators) {
$indicators = array_diff_key($indicators, $invalidindicators);
}
// Update the model with the rest of the data provided in the form.
$model->update($data->enabled, $indicators, $timesplitting, $predictionsprocessor, $data->contexts);
$message = '';
$messagetype = \core\output\notification::NOTIFY_SUCCESS;
if (!empty($invalidindicators)) {
$message = get_string('invalidindicatorsremoved', 'tool_analytics');
}
redirect($returnurl, $message, 0, $messagetype);
}
echo $OUTPUT->header();
$mform->display();
echo $OUTPUT->footer();
+37
View File
@@ -0,0 +1,37 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Tool analytics webservice definitions.
*
* @package tool_analytics
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$functions = array(
'tool_analytics_potential_contexts' => array(
'classname' => 'tool_analytics\external',
'methodname' => 'potential_contexts',
'description' => 'Retrieve the list of potential contexts for a model.',
'type' => 'read',
'ajax' => true,
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
);
+47
View File
@@ -0,0 +1,47 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* This file defines tasks performed by the tool.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
// List of tasks.
$tasks = array(
array(
'classname' => 'tool_analytics\task\train_models',
'blocking' => 0,
'minute' => '0',
'hour' => 'R',
'day' => '*',
'dayofweek' => '*',
'month' => '*'
),
array(
'classname' => 'tool_analytics\task\predict_models',
'blocking' => 0,
'minute' => '0',
'hour' => 'R',
'day' => '*',
'dayofweek' => '*',
'month' => '*'
),
);
+66
View File
@@ -0,0 +1,66 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Import models tool frontend.
*
* @package tool_analytics
* @copyright 2017 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../config.php');
require_login();
\core_analytics\manager::check_can_manage_models();
if (!\core_analytics\manager::is_analytics_enabled()) {
$PAGE->set_context(\context_system::instance());
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_analytics_disabled();
exit(0);
}
$returnurl = new \moodle_url('/admin/tool/analytics/index.php');
$url = new \moodle_url('/admin/tool/analytics/importmodel.php');
$title = get_string('importmodel', 'tool_analytics');
\tool_analytics\output\helper::set_navbar($title, $url);
$form = new \tool_analytics\output\form\import_model();
if ($form->is_cancelled()) {
redirect($returnurl);
} else if ($data = $form->get_data()) {
$modelconfig = new \core_analytics\model_config();
$zipfilepath = $form->save_temp_file('modelfile');
list ($modeldata, $unused) = $modelconfig->extract_import_contents($zipfilepath);
if ($error = $modelconfig->check_dependencies($modeldata, $data->ignoreversionmismatches)) {
// The file is not available until the form is validated so we need an alternative method to show errors.
redirect($url, $error, 0, \core\output\notification::NOTIFY_ERROR);
}
\core_analytics\model::import_model($zipfilepath);
redirect($returnurl, get_string('importedsuccessfully', 'tool_analytics'), 0,
\core\output\notification::NOTIFY_SUCCESS);
}
echo $OUTPUT->header();
$form->display();
echo $OUTPUT->footer();
+37
View File
@@ -0,0 +1,37 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Prediction models tool frontend.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/adminlib.php');
admin_externalpage_setup('analyticmodels', '', null, '', array('pagelayout' => 'report'));
$models = \core_analytics\manager::get_all_models();
echo $OUTPUT->header();
$templatable = new \tool_analytics\output\models_list($models);
echo $PAGE->get_renderer('tool_analytics')->render($templatable);
echo $OUTPUT->footer();
@@ -0,0 +1,154 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Strings for tool_analytics.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['accuracy'] = 'Accuracy';
$string['actions'] = 'Actions';
$string['actionsexecutedbyusers'] = 'Actions executed by users';
$string['actionsexecutedbyusersfor'] = 'Actions executed by users for "{$a}" model';
$string['actionexecutedgroupedusefulness'] = 'Grouped actions';
$string['allpredictions'] = 'All predictions';
$string['alltimesplittingmethods'] = 'All analysis intervals';
$string['analysingsitedata'] = 'Analysing the site';
$string['analysis'] = 'Analysis';
$string['analyticmodels'] = 'Analytics models';
$string['bettercli'] = 'Evaluating models and generating predictions may involve heavy processing. It is recommended to run these actions from the command line.';
$string['cantguessstartdate'] = 'Can\'t guess the start date';
$string['cantguessenddate'] = 'Can\'t guess the end date';
$string['classdoesnotexist'] = 'Class {$a} does not exist';
$string['clearpredictions'] = 'Clear predictions';
$string['clearmodelpredictions'] = 'Are you sure you want to clear all "{$a}" predictions?';
$string['clienablemodel'] = 'You can enable the model by selecting an analysis interval by its ID. Note that you can also enable it later using the web interface (\'none\' to exit).';
$string['clievaluationandpredictions'] = 'A scheduled task iterates through enabled models and gets predictions. Models evaluation via the web interface is disabled. You can allow these processes to be executed manually via the web interface by disabling the <a href="{$a}">\'onlycli\'</a> analytics setting.';
$string['clievaluationandpredictionsnoadmin'] = 'A scheduled task iterates through enabled models and gets predictions. Models evaluation via the web interface is disabled. It may be enabled by a site administrator.';
$string['component'] = 'Component';
$string['componentcore'] = 'Core';
$string['componentselect'] = 'Select all models provided by the component \'{$a}\'';
$string['componentselectnone'] = 'Unselect all';
$string['contexts'] = 'Contexts';
$string['contexts_help'] = 'The model will be limited to this set of contexts. No context restrictions will be applied if no contexts are selected.';
$string['createmodel'] = 'Create model';
$string['currenttimesplitting'] = 'Current analysis interval';
$string['delete'] = 'Delete';
$string['deletemodelconfirmation'] = 'Are you sure you want to delete "{$a}"? These changes cannot be reverted.';
$string['disabled'] = 'Disabled';
$string['editmodel'] = 'Edit "{$a}" model';
$string['edittrainedwarning'] = 'This model has already been trained. Note that changing its indicators or its analysis interval will delete its previous predictions and start generating new predictions.';
$string['enabled'] = 'Enabled';
$string['errorcantenablenotimesplitting'] = 'You need to select an analysis interval before enabling the model';
$string['errornoenabledandtrainedmodels'] = 'There are no enabled and trained models to predict.';
$string['errornoenabledmodels'] = 'There are no enabled models to train.';
$string['errornoexport'] = 'Only trained models can be exported';
$string['errornostaticevaluated'] = 'Models based on assumptions cannot be evaluated. They are always 100% correct according to how they were defined.';
$string['errornostaticlog'] = 'Models based on assumptions cannot be evaluated because there is no performance log.';
$string['erroronlycli'] = 'Execution only allowed via command line';
$string['errortrainingdataexport'] = 'The model training data could not be exported';
$string['evaluate'] = 'Evaluate';
$string['evaluatemodel'] = 'Evaluate model';
$string['evaluationmode'] = 'Evaluation mode';
$string['evaluationmode_help'] = 'There are two evaluation modes:
* Trained model - Site data is used as testing data to evaluate the accuracy of the trained model.
* Configuration - Site data is split into training and testing data, to both train and test the accuracy of the model configuration.
Trained model is only available if a trained model has been imported into the site, and has not yet been re-trained using site data.';
$string['evaluationmodeinfo'] = 'This model has been imported into the site. You can either evaluate the performance of the model, or you can evaluate the performance of the model configuration using site data.';
$string['evaluationmodetrainedmodel'] = 'Evaluate the trained model';
$string['evaluationmodecoltrainedmodel'] = 'Trained model';
$string['evaluationmodecolconfiguration'] = 'Configuration';
$string['evaluationmodeconfiguration'] = 'Evaluate the model configuration';
$string['evaluationinbatches'] = 'The site contents are calculated and stored in batches. The evaluation process may be stopped at any time. The next time it is run, it will continue from the point when it was stopped.';
$string['executescheduledanalysis'] = 'Execute scheduled analysis';
$string['export'] = 'Export';
$string['exportincludeweights'] = 'Include the weights of the trained model';
$string['exportmodel'] = 'Export configuration';
$string['exporttrainingdata'] = 'Export training data';
$string['extrainfo'] = 'Info';
$string['generalerror'] = 'Evaluation error. Status code {$a}';
$string['goodmodel'] = 'This is a good model for using to obtain predictions. Enable it to start obtaining predictions.';
$string['importmodel'] = 'Import model';
$string['indicators'] = 'Indicators';
$string['indicators_help'] = 'The indicators are what you think will lead to an accurate prediction of the target.';
$string['indicators_link'] = 'Indicators';
$string['indicatorsnum'] = 'Number of indicators: {$a}';
$string['info'] = 'Info';
$string['insightsreport'] = 'Insights report';
$string['ignoreversionmismatches'] = 'Ignore version mismatches';
$string['ignoreversionmismatchescheckbox'] = 'Ignore the differences between this site version and the original site version.';
$string['importedsuccessfully'] = 'The model has been successfully imported.';
$string['insights'] = 'Insights';
$string['invalidanalysables'] = 'Invalid site elements';
$string['invalidanalysablesinfo'] = 'This page lists analysable elements that can\'t be used by this prediction model. The listed elements can\'t be used either to train the prediction model nor can the prediction model obtain predictions for them.';
$string['invalidanalysablestable'] = 'Invalid site analysable elements table';
$string['invalidcurrenttimesplitting'] = 'The current analysis interval is invalid for the target of this model. Please select a different interval.';
$string['invalidindicatorsremoved'] = 'A new model has been added. Indicators that don\'t work with the selected target have been automatically removed.';
$string['invalidtimesplitting'] = 'The selected analysis interval is invalid for the selected target.';
$string['invalidtimesplittinginmodels'] = 'The analysis interval used by some of the models is invalid. Please select a different interval for the following models: {$a}';
$string['invalidprediction'] = 'Invalid to get predictions';
$string['invalidtraining'] = 'Invalid to train the model';
$string['loginfo'] = 'Log extra info';
$string['missingmoodleversion'] = 'Imported file doesn\'t define a version number';
$string['modelid'] = 'Model ID';
$string['modelinvalidanalysables'] = 'Invalid analysable elements for "{$a}" model';
$string['modelname'] = 'Model name';
$string['modelresults'] = '{$a} results';
$string['modeltimesplitting'] = 'Analysis interval';
$string['newmodel'] = 'New model';
$string['nextpage'] = 'Next page';
$string['noactionsfound'] = 'Users have not executed any actions on the generated insights.';
$string['nodatatoevaluate'] = 'There is no data to evaluate the model';
$string['nodatatopredict'] = 'No new elements to get predictions for.';
$string['nodatatotrain'] = 'There is no new data that can be used for training.';
$string['noinvalidanalysables'] = 'This site does not contain any invalid analysable element.';
$string['notdefined'] = 'Not yet defined';
$string['pluginname'] = 'Analytic models';
$string['predictionresults'] = 'Prediction results';
$string['predictmodels'] = 'Predict models';
$string['predictorresultsin'] = 'Predictor logged information in {$a} directory';
$string['predictionprocessfinished'] = 'Prediction process finished';
$string['previouspage'] = 'Previous page';
$string['restoredefault'] = 'Restore default models';
$string['restoredefaultempty'] = 'Please select models to be restored.';
$string['restoredefaultinfo'] = 'These default models are missing or have changed since being installed. You can restore selected default models.';
$string['restoredefaultnone'] = 'All default models provided by core and installed plugins have been created. No new models were found; there is nothing to restore.';
$string['restoredefaultsome'] = 'Succesfully re-created {$a->count} new model(s).';
$string['restoredefaultsubmit'] = 'Restore selected';
$string['samestartdate'] = 'Current start date is good';
$string['sameenddate'] = 'Current end date is good';
$string['scheduledanalysisresults'] = 'Results using {$a->name} analysis interval';
$string['scheduledanalysisresultscli'] = 'Results using {$a->name} (id: {$a->id}) analysis interval';
$string['selecttimesplittingforevaluation'] = 'Select the analysis interval you want to use to evaluate the model configuration.';
$string['target'] = 'Target';
$string['target_help'] = 'The target is what the model will predict.';
$string['target_link'] = 'Targets';
$string['timesplittingnotdefined'] = 'No analysis interval is defined.';
$string['timesplittingnotdefined_help'] = 'You need to select an analysis interval before enabling the model.';
$string['trainandpredictmodel'] = 'Training model and calculating predictions';
$string['trainingprocessfinished'] = 'Training process finished';
$string['trainingresults'] = 'Training results';
$string['trainmodels'] = 'Train models';
$string['versionnotsame'] = 'Imported file was from a different version ({$a->importedversion}) than the current one ({$a->version})';
$string['viewlog'] = 'Evaluation log';
$string['weeksenddateautomaticallyset'] = 'End date automatically set based on start date and the number of sections';
$string['weeksenddatedefault'] = 'End date automatically calculated from the course start date.';
$string['privacy:metadata'] = 'The Analytic models plugin does not store any personal data.';
+328
View File
@@ -0,0 +1,328 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Model-related actions.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/filelib.php');
$id = required_param('id', PARAM_INT);
$action = required_param('action', PARAM_ALPHANUMEXT);
require_login();
$model = new \core_analytics\model($id);
\core_analytics\manager::check_can_manage_models();
if (!\core_analytics\manager::is_analytics_enabled()) {
$PAGE->set_context(\context_system::instance());
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_analytics_disabled();
exit(0);
}
$returnurl = new \moodle_url('/admin/tool/analytics/index.php');
$params = array('id' => $id, 'action' => $action);
$url = new \moodle_url('/admin/tool/analytics/model.php', $params);
switch ($action) {
case 'edit':
$title = get_string('editmodel', 'tool_analytics', $model->get_name());
break;
case 'evaluate':
$title = get_string('evaluatemodel', 'tool_analytics');
break;
case 'scheduledanalysis':
$title = get_string('analysis', 'tool_analytics');
break;
case 'log':
$title = get_string('viewlog', 'tool_analytics');
break;
case 'enable':
$title = get_string('enable');
break;
case 'disable':
$title = get_string('disable');
break;
case 'delete':
$title = get_string('delete');
break;
case 'exportdata':
$title = get_string('exporttrainingdata', 'tool_analytics');
break;
case 'exportmodel':
$title = get_string('exportmodel', 'tool_analytics');
break;
case 'clear':
$title = get_string('clearpredictions', 'tool_analytics');
break;
case 'insightsreport':
$title = get_string('insightsreport', 'tool_analytics');
break;
case 'invalidanalysables':
$title = get_string('invalidanalysables', 'tool_analytics');
break;
default:
throw new moodle_exception('errorunknownaction', 'analytics');
}
\tool_analytics\output\helper::set_navbar($title, $url);
$onlycli = get_config('analytics', 'onlycli');
if ($onlycli === false) {
// Default applied if no config found.
$onlycli = 1;
}
switch ($action) {
case 'enable':
require_sesskey();
$model->enable();
redirect($returnurl);
break;
case 'disable':
require_sesskey();
$model->update(0, false, false);
redirect($returnurl);
break;
case 'delete':
require_sesskey();
$model->delete();
redirect($returnurl);
break;
case 'edit':
require_sesskey();
$invalidcurrenttimesplitting = $model->invalid_timesplitting_selected();
$potentialtimesplittings = $model->get_potential_timesplittings();
$customdata = array(
'id' => $model->get_id(),
'trainedmodel' => $model->is_trained(),
'staticmodel' => $model->is_static(),
'invalidcurrenttimesplitting' => (!empty($invalidcurrenttimesplitting)),
'targetclass' => $model->get_target()->get_id(),
'targetname' => $model->get_target()->get_name(),
'indicators' => $model->get_potential_indicators(),
'timesplittings' => $potentialtimesplittings,
'predictionprocessors' => \core_analytics\manager::get_all_prediction_processors(),
'supportscontexts' => ($model->get_analyser(['notimesplitting' => true]))::context_restriction_support(),
'contexts' => $model->get_contexts(),
);
$mform = new \tool_analytics\output\form\edit_model(null, $customdata);
if ($mform->is_cancelled()) {
redirect($returnurl);
} else if ($data = $mform->get_data()) {
$timesplitting = \tool_analytics\output\helper::option_to_class($data->timesplitting);
if (!$model->is_static()) {
// Converting option names to class names.
$indicators = array();
foreach ($data->indicators as $indicator) {
$indicatorclass = \tool_analytics\output\helper::option_to_class($indicator);
$indicators[] = \core_analytics\manager::get_indicator($indicatorclass);
}
$predictionsprocessor = \tool_analytics\output\helper::option_to_class($data->predictionsprocessor);
} else {
// These fields can not be modified.
$indicators = false;
$predictionsprocessor = false;
}
if (!isset($data->contexts)) {
$data->contexts = null;
}
$model->update($data->enabled, $indicators, $timesplitting, $predictionsprocessor, $data->contexts);
redirect($returnurl);
}
echo $OUTPUT->header();
$modelobj = $model->get_model_obj();
$callable = array('\tool_analytics\output\helper', 'class_to_option');
$modelobj->indicators = array_map($callable, json_decode($modelobj->indicators));
$modelobj->timesplitting = \tool_analytics\output\helper::class_to_option($modelobj->timesplitting);
if ($modelobj->contextids) {
$modelobj->contexts = array_map($callable, json_decode($modelobj->contextids));
}
$modelobj->predictionsprocessor = \tool_analytics\output\helper::class_to_option($modelobj->predictionsprocessor);
$mform->set_data($modelobj);
$mform->display();
break;
case 'evaluate':
require_sesskey();
if ($model->is_static()) {
throw new moodle_exception('errornostaticevaluate', 'tool_analytics');
}
if ($onlycli) {
throw new moodle_exception('erroronlycli', 'tool_analytics');
}
// Web interface is used by people who can not use CLI nor code stuff, always use
// cached stuff as they will change the model through the web interface as well
// which invalidates the previously analysed stuff.
$options = ['reuseprevanalysed' => true];
$mode = optional_param('mode', false, PARAM_ALPHANUM);
if ($mode == 'trainedmodel') {
$options['mode'] = 'trainedmodel';
} else {
// All is the default in core_analytics\model::evaluate() as well.
$timesplitting = optional_param('timesplitting', 'all', PARAM_ALPHANUMEXT);
if ($timesplitting === 'current') {
$options['timesplitting'] = \core_analytics\manager::get_time_splitting($model->get_model_obj()->timesplitting);
} else if ($timesplitting !== 'all') {
$options['timesplitting'] = \core_analytics\manager::get_time_splitting(
\tool_analytics\output\helper::option_to_class($timesplitting)
);
}
}
$results = $model->evaluate($options);
// We reset the theme and the output as some indicators may be using external functions
// which reset $PAGE.
\tool_analytics\output\helper::reset_page();
echo $OUTPUT->header();
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_evaluate_results($results, $model->get_analyser()->get_logs());
break;
case 'scheduledanalysis':
require_sesskey();
if ($onlycli) {
throw new moodle_exception('erroronlycli', 'tool_analytics');
}
$trainresults = $model->train();
$trainlogs = $model->get_analyser()->get_logs();
// Looks dumb to get a new instance but better be conservative.
$model = new \core_analytics\model($model->get_model_obj());
if ($model->is_trained()) {
$predictresults = $model->predict();
$predictlogs = $model->get_analyser()->get_logs();
} else {
$predictresults = false;
$predictlogs = array();
}
// We reset the theme and the output as some indicators may be using external functions
// which reset $PAGE.
\tool_analytics\output\helper::reset_page();
echo $OUTPUT->header();
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_get_predictions_results($trainresults, $trainlogs, $predictresults, $predictlogs);
break;
case 'log':
echo $OUTPUT->header();
if ($model->is_static()) {
throw new moodle_exception('errornostaticlog', 'tool_analytics');
}
$renderer = $PAGE->get_renderer('tool_analytics');
$modellogstable = new \tool_analytics\output\model_logs('model-' . $model->get_id(), $model);
echo $renderer->render_table($modellogstable);
break;
case 'exportdata':
if ($model->is_static() || !$model->is_trained()) {
throw new moodle_exception('errornoexport', 'tool_analytics');
}
$file = $model->get_training_data();
if (!$file) {
redirect($returnurl, get_string('errortrainingdataexport', 'tool_analytics'),
null, \core\output\notification::NOTIFY_ERROR);
}
$filename = 'training-data.' . $model->get_id() . '.' . time() . '.csv';
send_file($file, $filename, null, 0, false, true);
break;
case 'exportmodel':
$includeweights = optional_param('includeweights', 1, PARAM_INT);
$zipfilename = 'model-' . $model->get_unique_id() . '-' . microtime(false) . '.zip';
$zipfilepath = $model->export_model($zipfilename, $includeweights);
send_temp_file($zipfilepath, $zipfilename);
break;
case 'clear':
require_sesskey();
$model->clear();
redirect($returnurl);
break;
case 'insightsreport':
$contextid = optional_param('contextid', null, PARAM_INT);
echo $OUTPUT->header();
$renderable = new \tool_analytics\output\insights_report($model, $contextid);
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render($renderable);
break;
case 'invalidanalysables':
echo $OUTPUT->header();
$page = optional_param('page', 0, PARAM_INT);
// No option in the UI to change this, only for url hackers ;).
$perpage = optional_param('perpage', 10, PARAM_INT);
$renderable = new \tool_analytics\output\invalid_analysables($model, $page, $perpage);
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render($renderable);
break;
}
echo $OUTPUT->footer();
+86
View File
@@ -0,0 +1,86 @@
<?php
// This file is part of Moodle - https://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 <http://www.gnu.org/licenses/>.
/**
* Check and create missing default prediction models.
*
* @package tool_analytics
* @copyright 2019 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../config.php');
require_login();
\core_analytics\manager::check_can_manage_models();
if (!\core_analytics\manager::is_analytics_enabled()) {
$PAGE->set_context(\context_system::instance());
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_analytics_disabled();
exit(0);
}
$confirmed = optional_param('confirmed', false, PARAM_BOOL);
$restoreids = optional_param_array('restoreid', [], PARAM_ALPHANUM);
$returnurl = new \moodle_url('/admin/tool/analytics/index.php');
$myurl = new \moodle_url('/admin/tool/analytics/restoredefault.php');
\tool_analytics\output\helper::set_navbar(get_string('restoredefault', 'tool_analytics'), $myurl);
if (data_submitted()) {
require_sesskey();
if (empty($restoreids)) {
$message = get_string('restoredefaultempty', 'tool_analytics');
$type = \core\output\notification::NOTIFY_WARNING;
redirect($myurl, $message, null, $type);
}
$numcreated = 0;
foreach (\core_analytics\manager::load_default_models_for_all_components() as $componentname => $modelslist) {
foreach ($modelslist as $definition) {
if (!in_array(\core_analytics\manager::model_declaration_identifier($definition), $restoreids)) {
// This model has not been selected by the user.
continue;
}
list($target, $indicators) = \core_analytics\manager::get_declared_target_and_indicators_instances($definition);
if (\core_analytics\model::exists($target, $indicators)) {
// This model exists (normally this should not happen as we do not show such models in the UI to select).
continue;
}
\core_analytics\manager::create_declared_model($definition);
$numcreated++;
}
}
$message = get_string('restoredefaultsome', 'tool_analytics', ['count' => $numcreated]);
$type = \core\output\notification::NOTIFY_SUCCESS;
redirect($returnurl, $message, null, $type);
}
$models = \core_analytics\manager::load_default_models_for_all_components();
$ui = new \tool_analytics\output\restorable_models($models);
echo $OUTPUT->header();
echo $PAGE->get_renderer('tool_analytics')->render($ui);
echo $OUTPUT->footer();
+30
View File
@@ -0,0 +1,30 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Adds settings links to admin tree.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
if (\core_analytics\manager::is_analytics_enabled()) {
$ADMIN->add('analytics', new admin_externalpage('analyticmodels', get_string('analyticmodels', 'tool_analytics'),
"$CFG->wwwroot/$CFG->admin/tool/analytics/index.php", 'moodle/analytics:managemodels'));
}
@@ -0,0 +1,82 @@
{{!
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 <http://www.gnu.org/licenses/>.
}}
{{!
@template tool_analytics/evaluation_options
Evaluation selector.
The purpose of this template is to render the evaluation mode options.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
"trainedexternally": "1",
"timesplittingmethods": [
{
"id": "ou",
"name": "Quarters"
}, {
"id": "yeah",
"name": "Tenths"
}
]
}
}}
{{#trainedexternally}}
<div class="box mb-4">{{#str}} evaluationmodeinfo, tool_analytics {{/str}}</div>
<div class="custom-control custom-radio">
<input class="custom-control-input" type="radio" name="evaluationmode" id="id-mode-trainedmodel" value="trainedmodel" checked>
<label class="custom-control-label" for="id-mode-trainedmodel">{{#str}} evaluationmodetrainedmodel, tool_analytics {{/str}}</label>
</div>
<div class="custom-control custom-radio">
<input class="custom-control-input" type="radio" name="evaluationmode" id="id-mode-configuration" value="configuration">
<label class="custom-control-label" for="id-mode-configuration">{{#str}} evaluationmodeconfiguration, tool_analytics {{/str}}</label>
</div>
{{/trainedexternally}}
{{! Hidden by default if #trainedexternally as the default option is trainedmodel in this case.}}
<div id="id-evaluation-timesplitting-container" class="mt-3 {{#trainedexternally}}hidden{{/trainedexternally}}">
{{#str}} selecttimesplittingforevaluation, tool_analytics {{/str}}
<div>
<select id="id-evaluation-timesplitting" name="timesplitting" class="custom-select mt-3">
{{#timesplittingmethods}}
<option value="{{id}}">{{text}}</option>
{{/timesplittingmethods}}
</select>
</div>
</div>
{{#js}}
require(['jquery'], function($) {
$("input[name='evaluationmode']:radio").change(function() {
if ($(this).val() == 'configuration') {
$('#id-evaluation-timesplitting-container').show();
} else {
$('#id-evaluation-timesplitting-container').hide();
}
});
});
{{/js}}
@@ -0,0 +1,57 @@
{{!
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 <http://www.gnu.org/licenses/>.
}}
{{!
@template tool_analytics/export_options
Export options.
The purpose of this template is to render the exporting options.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
<div class="custom-control custom-radio">
<input class="custom-control-input" type="radio" name="exportoption" id="id-mode-exportdata" value="exportdata">
<label class="custom-control-label" for="id-mode-exportdata">{{#str}} exporttrainingdata, tool_analytics {{/str}}</label>
</div>
<div class="custom-control custom-radio">
<input class="custom-control-input" type="radio" name="exportoption" id="id-mode-exportmodel" value="exportmodel" checked>
<label class="custom-control-label" for="id-mode-exportmodel">{{#str}} exportmodel, tool_analytics {{/str}}</label>
</div>
<div class="custom-control custom-checkbox ml-5" id="id-includeweights-container">
<input class="custom-control-input" type="checkbox" id="id-includeweights" value="1" checked>
<label class="custom-control-label" for="id-includeweights">{{#str}} exportincludeweights, tool_analytics {{/str}}</label>
</div>
{{#js}}
require(['jquery'], function($) {
$("input[name='exportoption']:radio").change(function() {
if ($(this).val() == 'exportdata') {
$('#id-includeweights-container').hide();
} else {
$('#id-includeweights-container').show();
}
});
});
{{/js}}
@@ -0,0 +1,65 @@
{{!
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 <http://www.gnu.org/licenses/>.
}}
{{!
@template tool_analytics/insights_report
Template for the insights report.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* none
Example context (json):
{
"modelname": "Not engaging courses",
"noactions": {
"message": "Users have not executed any actions on the generated insights.",
"announce": "true"
}
}
}}
<div class="box">
<h3>{{#str}}actionsexecutedbyusersfor, tool_analytics, {{modelname}}{{/str}}</h3>
{{#contextselect}}
<div class="mt-3">
{{> core/single_select }}
</div>
{{/contextselect}}
{{#noactions}}
<div class="mt-3 mb-1">
{{> core/notification_info}}
</div>
{{/noactions}}
{{^noanalysables}}
<div class="row">
<div class="col-xl-6">
{{{separatedchart}}}
</div>
<div class="col-xl-6">
{{{groupedchart}}}
</div>
</div>
{{/noanalysables}}
</div>
@@ -0,0 +1,85 @@
{{!
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 <http://www.gnu.org/licenses/>.
}}
{{!
@template tool_analytics/invalid_analysables
Template for invalid analysables.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* none
Example context (json):
{
"modelname": "Not engaging courses",
"analysables": [
{
"url": "<a href=\"#\">Maths</a>",
"validtraining": "Ongoing course",
"validprediction": "Not enough students activity"
}, {
"url": "<a href=\"#\">Psichology</a>",
"validtraining": "No students",
"validprediction": "No students"
}
]
}
}}
<div class="box">
<h3>{{#str}}modelinvalidanalysables, tool_analytics, {{modelname}}{{/str}}</h3>
<div>{{#str}}invalidanalysablesinfo, tool_analytics{{/str}}</div>
{{#noanalysables}}
<div class="mt-2 mb-1">
{{> core/notification_info}}
</div>
{{/noanalysables}}
{{^noanalysables}}
<div class="mt-2 mb-1">
<span>{{#prev}}{{> core/single_button}}{{/prev}}</span>
<span>{{#next}}{{> core/single_button}}{{/next}}</span>
</div>
<table class="generaltable fullwidth">
<caption class="accesshide">{{#str}}invalidanalysablestable, tool_analytics{{/str}}</caption>
<thead>
<tr>
<th scope="col">{{#str}}name{{/str}}</th>
<th scope="col">{{#str}}invalidtraining, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}invalidprediction, tool_analytics{{/str}}</th>
</tr>
</thead>
<tbody>
{{#analysables}}
<tr>
<td>{{{url}}}</td>
<td>{{validtraining}}</td>
<td>{{validprediction}}</td>
</tr>
{{/analysables}}
</tbody>
</table>
<div class="mt-1 mb-2">
<span>{{#prev}}{{> core/single_button}}{{/prev}}</span>
<span>{{#next}}{{> core/single_button}}{{/next}}</span>
</div>
{{/noanalysables}}
</div>
@@ -0,0 +1,260 @@
{{!
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 <http://www.gnu.org/licenses/>.
}}
{{!
@template tool_analytics/models_list
Template for models list.
Classes required for JS:
* The list od models wrapped within a id="predictionmodelslist" element.
Data attributes required for JS:
* [data-widget="toggle"] indicates the clickable element for expanding/collapsing
the list of indicators used by the given model.
* [data-model-name="..."] should be provided by an element wrapping the model's actions menu
and contain the plain text name of the model.
Context variables required for this template:
* models: array - list of models to display
- id: int - model unique identifier
- modelname: string - name of the model
- name: object - data for the inplace editable element template
- target: string - name of the target associated with the model
- targetclass: string - fully qualified name of the target class
- targethelp: object - data for the help tooltip template
- enabled: bool - is the model enabled
- indicatorsnum: int - number of indicators
- indicators: array - list of indicators used by the model
+ name: string - name of the indicator
+ help: object - data for the help tooltip template
- insights: object - data for the single select template
- noinsights: string - text to display instead of insights
* warnings: array - list of data for notification warning template
* infos: array - list of data for notification info template
* createmodelurl: string - URL to create a new model
* importmodelurl: string - URL to import a model
Example context (json):
{
"models": [
{
"id": 11,
"modelname": "Prevent devs at risk",
"name": {
"component": "local_analyticsdemo",
"itemtype": "modelname",
"itemid": 42,
"displayvalue": "Prevent devs at risk",
"value": ""
},
"target": "Prevent devs at risk",
"targetclass": "\\local_analyticsdemo\\analytics\\target\\dev_risk",
"targethelp": {
"title": "Help with Prevent devs at risk",
"text": "This target blah blah ...",
"url": "http://example.org/help",
"linktext": "",
"icon": {
"extraclasses": "iconhelp",
"attributes": [
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
},
"enabled": 1,
"indicatorsnum": 2,
"indicators": [
{
"name": "Indicator 1",
"help": {
"text": "This indicator blah blah ...",
"title": "Help with Indicator 1",
"url": "http://example.org/help",
"linktext": "",
"icon": {
"extraclasses": "iconhelp",
"attributes": [
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
}
},
{
"name": "Indicator 2",
"help": {
"text": "This indicator blah blah ...",
"title": "Help with Indicator 2",
"url": "http://example.org/help",
"linktext": "",
"icon": {
"extraclasses": "iconhelp",
"attributes": [
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
}
}
],
"timesplitting": "Quarters",
"timesplittinghelp": {
"text": "This time splitting methof blah blah ...",
"title": "Help with Quarters",
"url": "http://example.org/help",
"linktext": "",
"icon": {
"extraclasses": "iconhelp",
"attributes": [
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
},
"noinsights": "No insights available yet"
}
],
"warnings": [
{
"message": "Be ware, this is just an example!"
}
],
"createmodelurl": "#",
"importmodelurl": "#"
}
}}
{{#warnings}}
{{> core/notification_warning}}
{{/warnings}}
{{#infos}}
{{> core/notification_info}}
{{/infos}}
<div class="box">
<div class="top-nav d-flex">
{{#newmodelmenu}}
{{>core/action_menu}}
{{/newmodelmenu}}
</div>
<table id="predictionmodelslist" class="generaltable fullwidth">
<caption>{{#str}}analyticmodels, tool_analytics{{/str}}</caption>
<thead>
<tr>
<th scope="col">{{#str}}modelname, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}enabled, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}indicators, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}modeltimesplitting, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}insights, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}actions{{/str}}</th>
</tr>
</thead>
<tbody>
{{#models}}
<tr data-model-name="{{modelname}}">
<td>
{{#name}}
<span class="model-name">{{>core/inplace_editable}}</span>
{{/name}}
<div>
<small class="target-class">{{targetclass}}</small>
{{#targethelp}}
{{>core/help_icon}}
{{/targethelp}}
</div>
</td>
<td>
{{#enabled}}
{{#pix}}i/checked, core, {{#str}}yes{{/str}}{{/pix}}
{{/enabled}}
{{^enabled}}
{{#str}}no{{/str}}
{{/enabled}}
</td>
<td>
<a data-widget="toggle"
title="{{#str}} clicktohideshow {{/str}}"
aria-expanded="false"
aria-controls="indicators-{{id}}"
role="button"
href="">
{{#str}} indicatorsnum, tool_analytics, {{indicatorsnum}} {{/str}}
</a>
<ul class="hidden" id="indicators-{{id}}">
{{#indicators}}
<li>
{{name}}
{{#help}}
{{>core/help_icon}}
{{/help}}
</li>
{{/indicators}}
</ul>
</td>
<td>
{{#timesplitting}}
{{timesplitting}}
{{#timesplittinghelp}}
{{>core/help_icon}}
{{/timesplittinghelp}}
{{/timesplitting}}
{{^timesplitting}}
{{#str}}notdefined, tool_analytics{{/str}}
{{#timesplittinghelp}}
{{>core/help_icon}}
{{/timesplittinghelp}}
{{/timesplitting}}
</td>
<td>
{{! models_list renderer is responsible of sending one or the other}}
{{#insights}}
{{> core/single_select }}
{{/insights}}
{{#noinsights}}
{{.}}
{{/noinsights}}
</td>
<td>
{{#actions}}
{{> core/action_menu}}
{{/actions}}
</td>
</tr>
{{/models}}
</tbody>
</table>
</div>
{{#js}}
require(['jquery'], function($) {
// Toggle the visibility of the indicators list.
$('#predictionmodelslist').on('click', '[data-widget="toggle"]', function(e) {
e.preventDefault();
var toggle = $(e.currentTarget);
var listid = toggle.attr('aria-controls');
$(document.getElementById(listid)).toggle();
if (toggle.attr('aria-expanded') == 'false') {
toggle.attr('aria-expanded', 'true');
} else {
toggle.attr('aria-expanded', 'false');
}
});
});
{{/js}}
@@ -0,0 +1,225 @@
{{!
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 <http://www.gnu.org/licenses/>.
}}
{{!
@template tool_analytics/restorable_models
Displays the list of missing prediction models that can be restored.
Classes required for JS:
* The list should be wrapped within a id="restorablemodelslist" element.
Data attributes required for JS:
* [data-widget="toggle"] indicates the clickable element for expanding/collapsing
the list of indicators used by the given model.
* [data-select] indicates a clickable element used for selecting multiple checkboxes.
* [data-component] should be set for checkboxes that select the particular model.
Context variables required for this template:
* hasdata: boolean - do we have data to display
* submiturl: string - URL where the form should be submitted
* backurl: string - URL where the user should be sent without making any changes
* sesskey: string
* components: array - list of components to display
- name: string - human readable name of the component
- component: string - frankenstyle name of the component
- models: array - list of restorable models provided by the component
+ defid: string - model definition identifier
+ targetname: string - human readable name of the target
+ targetclass: string - fully qualified classname of the target
+ indicatorsnum: int - number of indicators
+ indicators: array - list of indicators
~ name: string - human readable name of the indicator
~ classname: string - fully qualified classname of the indicator
Example context (json):
{
"hasdata": true,
"submiturl": "https://example.com/moodle/admin/tool/analytics/restoredefault.php",
"backurl": "https://example.com/moodle/admin/tool/analytics/index.php",
"sesskey": "abcdefg123456",
"components": [
{
"name": "Core",
"component": "core",
"models": [
{
"defid": "id24680aceg",
"targetname": "Courses at risk of not starting",
"targetclass": "\\core\\analytics\\target\\no_teaching",
"indicatorsnum": 2,
"indicators": [
{
"name": "There are no teachers",
"classname": "\\core\\analytics\\indicator\\no_teacher"
},
{
"name": "There are no students",
"classname": "\\core\\analytics\\indicator\\no_students"
}
]
},
{
"defid": "id13579bdfi",
"targetname": "Students at risk of dropping out",
"targetclass": "\\core\\analytics\\target\\course_dropout",
"indicatorsnum": 1,
"indicators": [
{
"name": "Read actions amount",
"classname": "\\core\\analytics\\indicator\\read_actions"
}
]
}
]
},
{
"name": "Custom analytics plugin",
"component": "tool_customanalytics",
"models": [
{
"defid": "id566dsgffg655",
"targetname": "Cheater",
"targetclass": "\\tool_customanalytics\\analytics\\target\\cheater",
"indicatorsnum": 1,
"indicators": [
{
"name": "Copy-pasted submissions",
"classname": "\\tool_customanalytics\\analytics\\indicator\\copy_paster_submissions"
}
]
}
]
}
]
}
}}
<div class="box">
{{^hasdata}}
<p>{{#str}} restoredefaultnone, tool_analytics {{/str}}</p>
<div><a href="{{backurl}}" class="btn btn-secondary">{{#str}} back {{/str}}</a></div>
{{/hasdata}}
{{#hasdata}}
<p>{{#str}} restoredefaultinfo, tool_analytics {{/str}}</p>
<form method="post" action="{{submiturl}}">
<table id="restorablemodelslist" class="generaltable fullwidth">
<colgroup>
<col width="10%">
<col width="45%">
<col width="45%">
</colgroup>
<thead>
<tr>
<th scope="col"><a href="" data-select="*">{{#str}} selectall {{/str}}</a></th>
<th scope="col">{{#str}} target, tool_analytics {{/str}}</th>
<th scope="col">{{#str}} indicators, tool_analytics {{/str}}</th>
</tr>
</thead>
<tbody>
{{#components}}
<tr>
<th scope="rowgroup" colspan="3">
<span class="component-name">
<a href=""
title="{{#str}} componentselect, tool_analytics, {{name}} {{/str}}"
data-select="{{component}}">
{{name}}
</a>
</span>
<div><small class="component-frankenstyle">{{component}}</small></div>
</th>
</tr>
{{#models}}
<tr>
<td>
<input data-component="{{component}}" type="checkbox" name="restoreid[]" value="{{defid}}">
</td>
<td>
<span class="target-name">{{targetname}}</span>
{{#targethelp}}
{{>core/help_icon}}
{{/targethelp}}
<div><small class="target-class">{{targetclass}}</small></div>
</td>
<td>
<a data-widget="toggle"
title="{{#str}} clicktohideshow {{/str}}"
aria-expanded="false"
aria-controls="indicators-{{defid}}"
role="button"
href="">
{{#str}} indicatorsnum, tool_analytics, {{indicatorsnum}} {{/str}}
</a>
<ul class="hidden listunstyled" id="indicators-{{defid}}">
{{#indicators}}
<li>
{{name}}
{{#indicatorhelp}}
{{>core/help_icon}}
{{/indicatorhelp}}
<div><small>{{classname}}</small></div>
</li>
{{/indicators}}
</ul>
</td>
</tr>
{{/models}}
{{/components}}
</tbody>
</table>
<div>
<input class="btn btn-primary" type="submit" value="{{#str}} restoredefaultsubmit, tool_analytics {{/str}}">
<input class="btn btn-secondary" type="reset" value="{{#str}} componentselectnone, tool_analytics {{/str}}">
<a href="{{backurl}}" class="btn btn-secondary">{{#str}} back {{/str}}</a>
<input type="hidden" name="sesskey" value="{{sesskey}}">
</div>
</form>
{{/hasdata}}
</div>
{{#js}}
require(['jquery'], function($) {
// Toggle the visibility of the indicators list.
$('#restorablemodelslist').on('click', '[data-widget="toggle"]', function(e) {
e.preventDefault();
var toggle = $(e.currentTarget);
var listid = toggle.attr('aria-controls');
$(document.getElementById(listid)).toggle();
if (toggle.attr('aria-expanded') == 'false') {
toggle.attr('aria-expanded', 'true');
} else {
toggle.attr('aria-expanded', 'false');
}
});
// Selecting all / all in component checkboxes.
$('#restorablemodelslist').on('click', '[data-select]', function(e) {
e.preventDefault();
var handler = $(e.currentTarget);
var component = handler.attr('data-select');
if (component == '*') {
$('input[data-component]').prop('checked', true);
} else {
$('input[data-component="' + component + '"]').prop('checked', true);
}
});
});
{{/js}}
@@ -0,0 +1,103 @@
@tool @tool_analytics
Feature: Restoring default models
In order to get prediction models into their initial state
As a manager
I need to be able to restore deleted default models
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| manager | Max | Manager | man@example.com |
And the following "role assigns" exist:
| user | role | contextlevel | reference |
| manager | manager | System | |
Scenario: Restore a single deleted default model
Given I log in as "manager"
And I navigate to "Analytics > Analytics models" in site administration
# Delete 'Courses at risk of not starting' model.
And I click on "Delete" "link" in the "Courses at risk of not starting" "table_row"
And I should see "Analytics models"
And I should not see "Courses at risk of not starting"
# Delete 'Students at risk of dropping out' model.
And I click on "Delete" "link" in the "Students at risk of dropping out" "table_row"
And I should see "Analytics models"
And I should not see "Students at risk of dropping out"
# Go to the page for restoring deleted models.
When I click on "Restore default models" "link"
And I should see "Courses at risk of not starting"
And I should see "Students at risk of dropping out"
# Select and restore the 'Courses at risk of not starting' model.
And I set the field with xpath "//tr[contains(normalize-space(.), 'Courses at risk of not starting')]//input[@type='checkbox']" to "1"
And I click on "Restore selected" "button"
Then I should see "Succesfully re-created 1 new model(s)."
And I should see "Analytics models"
And I should see "Courses at risk of not starting"
And I should not see "Students at risk of dropping out"
Scenario: Restore multiple deleted default models at once
Given I log in as "manager"
And I navigate to "Analytics > Analytics models" in site administration
# Delete 'Courses at risk of not starting' model.
And I click on "Delete" "link" in the "Courses at risk of not starting" "table_row"
And I should see "Analytics models"
And I should not see "Courses at risk of not starting"
# Delete 'Students at risk of dropping out' model.
And I click on "Delete" "link" in the "Students at risk of dropping out" "table_row"
And I should see "Analytics models"
And I should not see "Students at risk of dropping out"
# Go to the page for restoring deleted models.
When I click on "Restore default models" "link"
And I should see "Courses at risk of not starting"
And I should see "Students at risk of dropping out"
# Select and restore both models.
And I set the field with xpath "//tr[contains(normalize-space(.), 'Courses at risk of not starting')]//input[@type='checkbox']" to "1"
And I set the field with xpath "//tr[contains(normalize-space(.), 'Students at risk of dropping out')]//input[@type='checkbox']" to "1"
And I click on "Restore selected" "button"
Then I should see "Succesfully re-created 2 new model(s)."
And I should see "Analytics models"
And I should see "Courses at risk of not starting"
And I should see "Students at risk of dropping out"
Scenario: Going to the restore page while no models can be restored
Given I log in as "manager"
And I navigate to "Analytics > Analytics models" in site administration
And I should see "Analytics models"
And I should see "Courses at risk of not starting"
When I click on "Restore default models" "link"
Then I should see "All default models provided by core and installed plugins have been created. No new models were found; there is nothing to restore."
And I click on "Back" "link"
And I should see "Analytics models"
@javascript
Scenario: User can select and restore all missing models
Given I log in as "manager"
And I navigate to "Analytics > Analytics models" in site administration
# Delete 'Courses at risk of not starting' model.
And I click on "Actions" "link" in the "Courses at risk of not starting" "table_row"
And I click on "Delete" "link" in the "Courses at risk of not starting" "table_row"
And I click on "Delete" "button" in the "Delete" "dialogue"
And I should see "Analytics models"
And I should not see "Courses at risk of not starting"
# Delete 'Students at risk of dropping out' model.
And I click on "Actions" "link" in the "Students at risk of dropping out" "table_row"
And I click on "Delete" "link" in the "Students at risk of dropping out" "table_row"
And I click on "Delete" "button" in the "Delete" "dialogue"
And I should see "Analytics models"
And I should not see "Courses at risk of not starting"
And I should not see "Students at risk of dropping out"
# Go to the page for restoring deleted models.
And I click on "New model" "link"
And I click on "Restore default models" "link"
And I should see "Courses at risk of not starting"
And I should see "Students at risk of dropping out"
# Attempt to submit the form without selecting any model.
And I click on "Restore selected" "button"
And I should see "Please select models to be restored."
# Select all models.
When I click on "Select all" "link"
And I click on "Restore selected" "button"
Then I should see "Succesfully re-created 2 new model(s)."
And I should see "Analytics models"
And I should see "Courses at risk of not starting"
And I should see "Students at risk of dropping out"
@@ -0,0 +1,37 @@
@core @tool @tool_analytics @javascript
Feature: Verify the breadcrumbs in analytics site administration pages
Whenever I navigate to analytics page in site administration to create, import, edit or restore models
As an admin
The breadcrumbs should be visible
Background:
Given I log in as "admin"
Scenario: Verify the breadcrumbs in analytics models page by visiting the create model, import model, restore model and edit page
Given I navigate to "Analytics > Analytics models" in site administration
And I click on "New model" "link"
When I click on "Create model" "link"
Then "Create model" "text" should exist in the ".breadcrumb" "css_element"
And "Analytics model" "link" should exist in the ".breadcrumb" "css_element"
And "Analytics" "link" should exist in the ".breadcrumb" "css_element"
And I press "Cancel"
# Testing import model page
And I click on "New model" "link"
And I click on "Import model" "link"
And "Import model" "text" should exist in the ".breadcrumb" "css_element"
And "Analytics model" "link" should exist in the ".breadcrumb" "css_element"
And "Analytics" "link" should exist in the ".breadcrumb" "css_element"
And I press "Cancel"
# Testing restore defaults
And I click on "New model" "link"
And I click on "Restore default models" "link"
And "Restore default models" "text" should exist in the ".breadcrumb" "css_element"
And "Analytics model" "link" should exist in the ".breadcrumb" "css_element"
And "Analytics" "link" should exist in the ".breadcrumb" "css_element"
And I click on "Back" "link"
# Testing edit page
And I click on "Actions" "link"
And I click on "Edit" "link"
And "Edit \"Courses at risk of not starting\" model" "text" should exist in the ".breadcrumb" "css_element"
And "Analytics model" "link" should exist in the ".breadcrumb" "css_element"
And "Analytics" "link" should exist in the ".breadcrumb" "css_element"
+83
View File
@@ -0,0 +1,83 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
/**
* Tool analytics external functions tests.
*
* @package tool_analytics
* @category external
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.8
*/
namespace tool_analytics\external;
use externallib_advanced_testcase;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
require_once($CFG->dirroot . '/analytics/tests/fixtures/test_indicator_max.php');
require_once($CFG->dirroot . '/analytics/tests/fixtures/test_target_course_level_shortname.php');
/**
* Tool analytics external functions tests
*
* @package tool_analytics
* @category external
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.8
*/
class external_test extends externallib_advanced_testcase {
/**
* test_potential_contexts description
*/
public function test_potential_contexts(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Include the all context levels so the misc. category get included.
$this->assertCount(1, \tool_analytics\external::potential_contexts());
// The frontpage is not included.
$this->assertCount(0, \tool_analytics\external::potential_contexts('PHPUnit'));
$target = \core_analytics\manager::get_target('test_target_course_level_shortname');
$indicators = ['test_indicator_max' => \core_analytics\manager::get_indicator('test_indicator_max')];
$model = \core_analytics\model::create($target, $indicators);
$this->assertCount(1, \tool_analytics\external::potential_contexts(null, $model->get_id()));
}
/**
* test_potential_contexts description
*/
public function test_potential_contexts_no_manager(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(\required_capability_exception::class);
$this->assertCount(2, \tool_analytics\external::potential_contexts());
}
}

Some files were not shown because too many files have changed in this diff Show More