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
+9
View File
@@ -0,0 +1,9 @@
ENROLMENT MODULES
-----------------
(Yes, that's the correct English spelling ;-) )
All enrolment modules must extend base class enrol_plugin
which is defined in lib/enrollib.php. You can find documentation
of each method in the base class.
+129
View File
@@ -0,0 +1,129 @@
<?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 processes AJAX enrolment actions and returns JSON
*
* The general idea behind this file is that any errors should throw exceptions
* which will be returned and acted upon by the calling AJAX script.
*
* @package core_enrol
* @copyright 2010 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('AJAX_SCRIPT', true);
require('../config.php');
require_once("$CFG->dirroot/enrol/locallib.php");
require_once("$CFG->dirroot/enrol/renderer.php");
require_once("$CFG->dirroot/group/lib.php");
// Must have the sesskey
$id = required_param('id', PARAM_INT); // course id
$action = required_param('action', PARAM_ALPHANUMEXT);
$PAGE->set_url(new moodle_url('/enrol/ajax.php', array('id'=>$id, 'action'=>$action)));
$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
$context = context_course::instance($course->id, MUST_EXIST);
if ($course->id == SITEID) {
throw new moodle_exception('invalidcourse');
}
require_login($course);
require_capability('moodle/course:enrolreview', $context);
require_sesskey();
echo $OUTPUT->header(); // send headers
$manager = new course_enrolment_manager($PAGE, $course);
$outcome = new stdClass();
$outcome->success = true;
$outcome->response = new stdClass();
$outcome->error = '';
$searchanywhere = get_user_preferences('userselector_searchtype') === USER_SEARCH_CONTAINS;
switch ($action) {
case 'unenrol':
$ue = $DB->get_record('user_enrolments', array('id'=>required_param('ue', PARAM_INT)), '*', MUST_EXIST);
list ($instance, $plugin) = $manager->get_user_enrolment_components($ue);
if (!$instance || !$plugin || !enrol_is_enabled($instance->enrol) || !$plugin->allow_unenrol_user($instance, $ue) || !has_capability("enrol/$instance->enrol:unenrol", $manager->get_context()) || !$manager->unenrol_user($ue)) {
throw new enrol_ajax_exception('unenrolnotpermitted');
}
break;
case 'unassign':
$role = required_param('role', PARAM_INT);
$user = required_param('user', PARAM_INT);
if (!has_capability('moodle/role:assign', $manager->get_context()) || !$manager->unassign_role_from_user($user, $role)) {
throw new enrol_ajax_exception('unassignnotpermitted');
}
break;
case 'assign':
$user = $DB->get_record('user', array('id'=>required_param('user', PARAM_INT)), '*', MUST_EXIST);
$roleid = required_param('roleid', PARAM_INT);
if (!array_key_exists($roleid, $manager->get_assignable_roles())) {
throw new enrol_ajax_exception('invalidrole');
}
if (!has_capability('moodle/role:assign', $manager->get_context()) || !$manager->assign_role_to_user($roleid, $user->id)) {
throw new enrol_ajax_exception('assignnotpermitted');
}
$outcome->response->roleid = $roleid;
break;
case 'getassignable':
$otheruserroles = optional_param('otherusers', false, PARAM_BOOL);
$outcome->response = $manager->get_assignable_roles_for_json($otheruserroles);
break;
case 'searchotherusers':
$search = optional_param('search', '', PARAM_RAW);
$page = optional_param('page', 0, PARAM_INT);
$outcome->response = $manager->search_other_users($search, $searchanywhere, $page);
// TODO Does not support custom user profile fields (MDL-70456).
$extrafields = \core_user\fields::get_identity_fields($context, false);
$useroptions = array();
// User is not enrolled, either link to site profile or do not link at all.
if (has_capability('moodle/user:viewdetails', context_system::instance())) {
$useroptions['courseid'] = SITEID;
} else {
$useroptions['link'] = false;
}
foreach ($outcome->response['users'] as &$user) {
$user->userId = $user->id;
$user->picture = $OUTPUT->user_picture($user, $useroptions);
$user->fullname = fullname($user);
$fieldvalues = array();
foreach ($extrafields as $field) {
$fieldvalues[] = s($user->{$field});
unset($user->{$field});
}
$user->extrafields = implode(', ', $fieldvalues);
unset($user->id);
}
// Chrome will display users in the order of the array keys, so we need
// to ensure that the results ordered array keys. Fortunately, the JavaScript
// does not care what the array keys are. It uses user.id where necessary.
$outcome->response['users'] = array_values($outcome->response['users']);
$outcome->success = true;
break;
default:
throw new enrol_ajax_exception('unknowajaxaction');
}
echo json_encode($outcome);
die();
+116
View File
@@ -0,0 +1,116 @@
<?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 contains form for bulk changing user enrolments.
*
* @package core_enrol
* @copyright 2011 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once("$CFG->libdir/formslib.php");
/**
* A base class that can be used to easily construct a form for use with bulk operations
*
* @copyright 2011 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class enrol_bulk_enrolment_change_form extends moodleform {
/**
* Defines the standard structure of the form
*/
protected function definition() {
$form = $this->_form;
$users = $this->_customdata['users'];
$statusoptions = $this->get_status_options();
$form->addElement('html', $this->get_users_table($users, $statusoptions));
$form->addElement('select', 'status', get_string('alterstatus', 'enrol_manual'), $statusoptions, array('optional' => true));
$form->addElement('date_time_selector', 'timestart', get_string('altertimestart', 'enrol_manual'), array('optional' => true));
$form->addElement('date_time_selector', 'timeend', get_string('altertimeend', 'enrol_manual'), array('optional' => true));
$this->add_action_buttons();
}
/**
* Returns an array of status options
* @return array
*/
protected function get_status_options() {
return array(-1 => get_string('nochange', 'enrol'),
ENROL_USER_ACTIVE => get_string('participationactive', 'enrol'),
ENROL_USER_SUSPENDED => get_string('participationsuspended', 'enrol'));
}
/**
* Generates an HTML table to display the users being affected by the bulk change.
*
* @param array $users
* @param array $statusoptions
* @return string
*/
protected function get_users_table(array $users, array $statusoptions) {
$table = new html_table();
$table->head = array(
get_string('name'),
get_string('participationstatus', 'enrol'),
get_string('enroltimestart', 'enrol'),
get_string('enroltimeend', 'enrol'),
);
$table->data = array();
foreach ($users as $user) {
foreach ($user->enrolments as $enrolment) {
$input = html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'bulkuser[]', 'value' => $user->id));
$table->data[] = array(
fullname($user).$input,
$statusoptions[$enrolment->status],
(!empty($enrolment->timestart))?userdate($enrolment->timestart):'',
(!empty($enrolment->timeend))?userdate($enrolment->timeend):'',
);
}
}
return html_writer::table($table);
}
}
/**
* A convenience class to allow the quick generation of a confirmation form for a bulk operation.
* @copyright 2011 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class enrol_bulk_enrolment_confirm_form extends enrol_bulk_enrolment_change_form {
/**
* Defines the standard structure of the form
*/
protected function definition() {
$form = $this->_form;
$users = $this->_customdata['users'];
$title = $this->_customdata['title'];
$message = $this->_customdata['message'];
$button = $this->_customdata['button'];
$form->addElement('html', $this->get_users_table($users, $this->get_status_options()));
$form->addElement('header', 'ebecf_header', $title);
$form->addElement('html', html_writer::tag('p', $message));
$this->add_action_buttons(true, $button);
}
}
+153
View File
@@ -0,0 +1,153 @@
<?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/>.
/**
* Local stuff for category enrolment plugin.
*
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Event handler for category enrolment plugin.
*
* We try to keep everything in sync via listening to events,
* it may fail sometimes, so we always do a full sync in cron too.
*/
class enrol_category_observer {
/**
* Triggered when user is assigned a new role.
*
* @param \core\event\role_assigned $event
*/
public static function role_assigned(\core\event\role_assigned $event) {
global $DB;
if (!enrol_is_enabled('category')) {
return;
}
$ra = new stdClass();
$ra->roleid = $event->objectid;
$ra->userid = $event->relateduserid;
$ra->contextid = $event->contextid;
//only category level roles are interesting
$parentcontext = context::instance_by_id($ra->contextid);
if ($parentcontext->contextlevel != CONTEXT_COURSECAT) {
return;
}
// Make sure the role is to be actually synchronised,
// please note we are ignoring overrides of the synchronised capability (for performance reasons in full sync).
$syscontext = context_system::instance();
if (!$DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$ra->roleid, 'capability'=>'enrol/category:synchronised', 'permission'=>CAP_ALLOW))) {
return;
}
// Add necessary enrol instances.
$plugin = enrol_get_plugin('category');
$sql = "SELECT c.*
FROM {course} c
JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel AND ctx.path LIKE :match)
LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
WHERE e.id IS NULL";
$params = array('courselevel'=>CONTEXT_COURSE, 'match'=>$parentcontext->path.'/%');
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $course) {
$plugin->add_instance($course);
}
$rs->close();
// Now look for missing enrolments.
$sql = "SELECT e.*
FROM {course} c
JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel AND ctx.path LIKE :match)
JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)
WHERE ue.id IS NULL";
$params = array('courselevel'=>CONTEXT_COURSE, 'match'=>$parentcontext->path.'/%', 'userid'=>$ra->userid);
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $instance) {
$plugin->enrol_user($instance, $ra->userid, null, time());
}
$rs->close();
}
/**
* Triggered when user role is unassigned.
*
* @param \core\event\role_unassigned $event
*/
public static function role_unassigned(\core\event\role_unassigned $event) {
global $DB;
if (!enrol_is_enabled('category')) {
return;
}
$ra = new stdClass();
$ra->userid = $event->relateduserid;
$ra->contextid = $event->contextid;
// only category level roles are interesting
$parentcontext = context::instance_by_id($ra->contextid);
if ($parentcontext->contextlevel != CONTEXT_COURSECAT) {
return;
}
// Now this is going to be a bit slow, take all enrolments in child courses and verify each separately.
$syscontext = context_system::instance();
if (!$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext)) {
return;
}
$plugin = enrol_get_plugin('category');
$sql = "SELECT e.*
FROM {course} c
JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel AND ctx.path LIKE :match)
JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)";
$params = array('courselevel'=>CONTEXT_COURSE, 'match'=>$parentcontext->path.'/%', 'userid'=>$ra->userid);
$rs = $DB->get_recordset_sql($sql, $params);
list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r');
$params['userid'] = $ra->userid;
foreach ($rs as $instance) {
$coursecontext = context_course::instance($instance->courseid);
$contextids = $coursecontext->get_parent_context_ids();
array_pop($contextids); // Remove system context, we are interested in categories only.
list($contextids, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'c');
$params = array_merge($params, $contextparams);
$sql = "SELECT ra.id
FROM {role_assignments} ra
WHERE ra.userid = :userid AND ra.contextid $contextids AND ra.roleid $roleids";
if (!$DB->record_exists_sql($sql, $params)) {
// User does not have any interesting role in any parent context, let's unenrol.
$plugin->unenrol_user($instance, $ra->userid);
}
}
$rs->close();
}
}
@@ -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/>.
/**
* Privacy Subsystem implementation for enrol_category.
*
* @package enrol_category
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_category\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for enrol_category implementing null_provider.
*
* @copyright 2018 Carlos Escobedo <carlos@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,63 @@
<?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/>.
/**
* Syncing enrolments task.
*
* @package enrol_category
* @author Farhan Karmali <farhan6318@gmail.com>
* @copyright Farhan Karmali
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_category\task;
defined('MOODLE_INTERNAL') || die();
/**
* Syncing enrolments task.
*
* @package enrol_category
* @author Farhan Karmali <farhan6318@gmail.com>
* @copyright Farhan Karmali
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol_category_sync extends \core\task\scheduled_task {
/**
* Name for this task.
*
* @return string
*/
public function get_name() {
return get_string('enrolcategorysynctask', 'enrol_category');
}
/**
* Run task for syncing category enrolments.
*/
public function execute() {
global $CFG;
if (!enrol_is_enabled('category')) {
return;
}
require_once("$CFG->dirroot/enrol/category/locallib.php");
$trace = new \null_progress_trace();
enrol_category_sync_full($trace);
}
}
+75
View File
@@ -0,0 +1,75 @@
<?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/>.
/**
* CLI sync for full category enrol synchronisation.
*
* Sample execution:
* $ sudo -u www-data /usr/bin/php /var/www/moodle/enrol/category/cli/sync.php
*
* Notes:
* - it is required to use the web server account when executing PHP CLI scripts
* - you need to change the "www-data" to match the apache user account
* - use "su" if "sudo" not available
*
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__.'/../../../config.php');
require_once("$CFG->dirroot/enrol/category/locallib.php");
require_once("$CFG->libdir/clilib.php");
// Now get cli options.
list($options, $unrecognized) = cli_get_params(array('verbose'=>false, 'help'=>false), array('v'=>'verbose', 'h'=>'help'));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
if ($options['help']) {
$help =
"Execute course category enrolment sync.
Options:
-v, --verbose Print verbose progress information
-h, --help Print out this help
Example:
\$ sudo -u www-data /usr/bin/php enrol/category/cli/sync.php
";
echo $help;
die;
}
if (!enrol_is_enabled('category')) {
cli_error('enrol_category plugin is disabled, synchronisation stopped', 2);
}
if (empty($options['verbose'])) {
$trace = new null_progress_trace();
} else {
$trace = new text_progress_trace();
}
$result = enrol_category_sync_full($trace);
exit($result);
+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/>.
/**
* Capabilities for category access plugin.
*
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
// Marks roles that have category role assignments synchronised to course enrolments
// overrides below system context are ignored (for performance reasons).
// By default his is not allowed in new installs, admins have to explicitly allow category enrolments.
'enrol/category:synchronised' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => array(
)
),
'enrol/category:config' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'manager' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
)
),
);
+40
View File
@@ -0,0 +1,40 @@
<?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/>.
/**
* Category enrolment plugin event handler definition.
*
* @package enrol_category
* @category event
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$observers = array (
array (
'eventname' => '\core\event\role_assigned',
'callback' => 'enrol_category_observer::role_assigned',
),
array (
'eventname' => '\core\event\role_unassigned',
'callback' => 'enrol_category_observer::role_unassigned',
),
);
+31
View File
@@ -0,0 +1,31 @@
<?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/>.
/**
* category enrolment plugin installation.
*
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
function xmldb_enrol_category_install() {
global $CFG, $DB;
}
+39
View File
@@ -0,0 +1,39 @@
<?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/>.
/**
* Task definition for enrol_category.
* @author Farhan Karmali <farhan6318@gmail.com>
* @copyright Farhan Karmali
* @package enrol_category
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$tasks = array(
array(
'classname' => '\enrol_category\task\enrol_category_sync',
'blocking' => 0,
'minute' => '*',
'hour' => '*',
'day' => '*',
'month' => '*',
'dayofweek' => '*',
'disabled' => 0
)
);
+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/>.
/**
* Strings for component 'enrol_category', language 'en'.
*
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['category:config'] = 'Configure category enrol instances';
$string['category:synchronised'] = 'Role assignments synchronised to course enrolment';
$string['enrolcategorysynctask'] = 'Category enrolment sync task';
$string['pluginname'] = 'Category enrolments';
$string['pluginname_desc'] = 'The category enrolments plugin synchronises any role assignments in the category context for roles with the capability enrol/category:synchronised allowed.';
$string['privacy:metadata'] = 'The Category enrolments plugin does not store any personal data.';
+107
View File
@@ -0,0 +1,107 @@
<?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/>.
/**
* Category enrolment plugin.
*
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* category enrolment plugin implementation.
* @author Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol_category_plugin extends enrol_plugin {
/**
* Is it possible to delete enrol instance via standard UI?
*
* @param stdClass $instance
* @return bool
*/
public function can_delete_instance($instance) {
global $DB;
$context = context_course::instance($instance->courseid);
if (!has_capability('enrol/category:config', $context)) {
return false;
}
if (!enrol_is_enabled('category')) {
return true;
}
// Allow delete only when no synced users here.
return !$DB->record_exists('user_enrolments', array('enrolid'=>$instance->id));
}
/**
* Is it possible to hide/show enrol instance via standard UI?
*
* @param stdClass $instance
* @return bool
*/
public function can_hide_show_instance($instance) {
$context = context_course::instance($instance->courseid);
return has_capability('enrol/category:config', $context);
}
/**
* Returns link to page which may be used to add new instance of enrolment plugin in course.
* @param int $courseid
* @return moodle_url page url
*/
public function get_newinstance_link($courseid) {
// Instances are added automatically as necessary.
return null;
}
/**
* Called after updating/inserting course.
*
* @param bool $inserted true if course just inserted
* @param stdClass $course
* @param stdClass $data form data
* @return void
*/
public function course_updated($inserted, $course, $data) {
global $CFG;
if (!enrol_is_enabled('category')) {
return;
}
// Sync category enrols.
require_once("$CFG->dirroot/enrol/category/locallib.php");
enrol_category_sync_course($course);
}
/**
* Automatic enrol sync executed during restore.
* Useful for automatic sync by course->idnumber or course category.
* @param stdClass $course course record
*/
public function restore_sync_course($course) {
global $CFG;
require_once("$CFG->dirroot/enrol/category/locallib.php");
enrol_category_sync_course($course);
}
}
+261
View File
@@ -0,0 +1,261 @@
<?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/>.
/**
* Local stuff for category enrolment plugin.
*
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Sync all category enrolments in one course
* @param stdClass $course
* @return void
*/
function enrol_category_sync_course($course) {
global $DB;
if (!enrol_is_enabled('category')) {
return;
}
$plugin = enrol_get_plugin('category');
$syscontext = context_system::instance();
$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext);
if (!$roles) {
// Nothing to sync, so remove the instance completely if exists.
if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) {
foreach ($instances as $instance) {
$plugin->delete_instance($instance);
}
}
return;
}
// First find out if any parent category context contains interesting role assignments.
$coursecontext = context_course::instance($course->id);
$contextids = $coursecontext->get_parent_context_ids();
array_pop($contextids); // Remove system context, we are interested in categories only.
list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r');
list($contextids, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'c');
$params = array_merge($params, $contextparams);
$params['courseid'] = $course->id;
$sql = "SELECT 'x'
FROM {role_assignments}
WHERE roleid $roleids AND contextid $contextids";
if (!$DB->record_exists_sql($sql, $params)) {
if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) {
// Should be max one instance, but anyway.
foreach ($instances as $instance) {
$plugin->delete_instance($instance);
}
}
return;
}
// Make sure the enrol instance exists - there should be always only one instance.
$delinstances = array();
if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) {
$instance = array_shift($instances);
$delinstances = $instances;
} else {
$i = $plugin->add_instance($course);
$instance = $DB->get_record('enrol', array('id'=>$i));
}
// Add new enrolments.
$sql = "SELECT ra.userid, ra.estart
FROM (SELECT xra.userid, MIN(xra.timemodified) AS estart
FROM {role_assignments} xra
JOIN {user} xu ON (xu.id = xra.userid AND xu.deleted = 0)
WHERE xra.roleid $roleids AND xra.contextid $contextids
GROUP BY xra.userid
) ra
LEFT JOIN {user_enrolments} ue ON (ue.enrolid = :instanceid AND ue.userid = ra.userid)
WHERE ue.id IS NULL";
$params['instanceid'] = $instance->id;
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $ra) {
$plugin->enrol_user($instance, $ra->userid, null, $ra->estart);
}
$rs->close();
// Remove unwanted enrolments.
$sql = "SELECT DISTINCT ue.userid
FROM {user_enrolments} ue
LEFT JOIN {role_assignments} ra ON (ra.roleid $roleids AND ra.contextid $contextids AND ra.userid = ue.userid)
WHERE ue.enrolid = :instanceid AND ra.id IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $ra) {
$plugin->unenrol_user($instance, $ra->userid);
}
$rs->close();
if ($delinstances) {
// We have to do this as the last step in order to prevent temporary unenrolment.
foreach ($delinstances as $delinstance) {
$plugin->delete_instance($delinstance);
}
}
}
/**
* Synchronise courses in all categories.
*
* It gets out-of-sync if:
* - you move course to different category
* - reorder categories
* - disable enrol_category and enable it again
*
* @param progress_trace $trace
* @return int exit code - 0 is ok, 1 means error, 2 if plugin disabled
*/
function enrol_category_sync_full(progress_trace $trace) {
global $DB;
if (!enrol_is_enabled('category')) {
$trace->finished();
return 2;
}
// We may need a lot of time here.
core_php_time_limit::raise();
$plugin = enrol_get_plugin('category');
$syscontext = context_system::instance();
// Any interesting roles worth synchronising?
if (!$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext)) {
// yay, nothing to do, so let's remove all leftovers
$trace->output("No roles with 'enrol/category:synchronised' capability found.");
if ($instances = $DB->get_records('enrol', array('enrol'=>'category'))) {
$trace->output("Deleting all category enrol instances...");
foreach ($instances as $instance) {
$trace->output("deleting category enrol instance from course {$instance->courseid}", 1);
$plugin->delete_instance($instance);
}
$trace->output("...all instances deleted.");
}
$trace->finished();
return 0;
}
$rolenames = role_fix_names($roles, null, ROLENAME_SHORT, true);
$trace->output('Synchronising category enrolments for roles: '.implode(', ', $rolenames).'...');
list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r');
$params['courselevel'] = CONTEXT_COURSE;
$params['catlevel'] = CONTEXT_COURSECAT;
// First of all add necessary enrol instances to all courses.
$parentcat = $DB->sql_concat("cat.path", "'/%'");
$parentcctx = $DB->sql_concat("cctx.path", "'/%'");
// Need whole course records to be used by add_instance(), use inner view (ci) to
// get distinct records only.
// TODO: Moodle 2.1. Improve enrol API to accept courseid / courserec
$sql = "SELECT c.*
FROM {course} c
JOIN (
SELECT DISTINCT c.id
FROM {course} c
JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel)
JOIN (SELECT DISTINCT cctx.path
FROM {course_categories} cc
JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
) cat ON (ctx.path LIKE $parentcat)
LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
WHERE e.id IS NULL) ci ON (c.id = ci.id)";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $course) {
$plugin->add_instance($course);
}
$rs->close();
// Now look for courses that do not have any interesting roles in parent contexts,
// but still have the instance and delete them.
$sql = "SELECT e.*
FROM {enrol} e
JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
LEFT JOIN ({course_categories} cc
JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
) ON (ctx.path LIKE $parentcctx)
WHERE e.enrol = 'category' AND cc.id IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $instance) {
$plugin->delete_instance($instance);
}
$rs->close();
// Add missing enrolments.
$sql = "SELECT e.*, cat.userid, cat.estart
FROM {enrol} e
JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
JOIN (SELECT cctx.path, ra.userid, MIN(ra.timemodified) AS estart
FROM {course_categories} cc
JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
GROUP BY cctx.path, ra.userid
) cat ON (ctx.path LIKE $parentcat)
LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = cat.userid)
WHERE e.enrol = 'category' AND ue.id IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $instance) {
$userid = $instance->userid;
$estart = $instance->estart;
unset($instance->userid);
unset($instance->estart);
$plugin->enrol_user($instance, $userid, null, $estart);
$trace->output("enrolling: user $userid ==> course $instance->courseid", 1);
}
$rs->close();
// Remove stale enrolments.
$sql = "SELECT e.*, ue.userid
FROM {enrol} e
JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
JOIN {user_enrolments} ue ON (ue.enrolid = e.id)
LEFT JOIN ({course_categories} cc
JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
) ON (ctx.path LIKE $parentcctx AND ra.userid = ue.userid)
WHERE e.enrol = 'category' AND cc.id IS NULL";
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $instance) {
$userid = $instance->userid;
unset($instance->userid);
$plugin->unenrol_user($instance, $userid);
$trace->output("unenrolling: user $userid ==> course $instance->courseid", 1);
}
$rs->close();
$trace->output('...user enrolment synchronisation finished.');
$trace->finished();
return 0;
}
+35
View File
@@ -0,0 +1,35 @@
<?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/>.
/**
* Category enrolment plugin settings and presets.
*
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
if ($ADMIN->fulltree) {
//--- general settings -----------------------------------------------------------------------------------
$settings->add(new admin_setting_heading('enrol_category_settings', '', get_string('pluginname_desc', 'enrol_category')));
//--- enrol instance defaults ----------------------------------------------------------------------------
}
+348
View File
@@ -0,0 +1,348 @@
<?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 enrol_category;
/**
* Category enrolment sync functional test.
*
* @package enrol_category
* @category phpunit
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class plugin_test extends \advanced_testcase {
protected function enable_plugin() {
$enabled = enrol_get_plugins(true);
$enabled['category'] = true;
$enabled = array_keys($enabled);
set_config('enrol_plugins_enabled', implode(',', $enabled));
}
protected function disable_plugin() {
$enabled = enrol_get_plugins(true);
unset($enabled['category']);
$enabled = array_keys($enabled);
set_config('enrol_plugins_enabled', implode(',', $enabled));
}
protected function enable_role_sync($roleid) {
$syscontext = \context_system::instance();
assign_capability('enrol/category:synchronised', CAP_ALLOW, $roleid, $syscontext, true);
}
protected function disable_role_sync($roleid) {
$syscontext = \context_system::instance();
unassign_capability('enrol/category:synchronised', $roleid, $syscontext);
}
/**
* Test utility methods used in syn test, fail here means something
* in core accesslib was changed, but it is possible that only this test
* is affected, nto the plugin itself...
*/
public function test_utils(): void {
global $DB;
$this->resetAfterTest();
$syscontext = \context_system::instance();
$this->assertFalse(enrol_is_enabled('category'));
$this->enable_plugin();
$this->assertTrue(enrol_is_enabled('category'));
$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext);
$this->assertEmpty($roles);
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$this->enable_role_sync($studentrole->id);
$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext);
$this->assertEquals(1, count($roles));
$this->assertEquals($studentrole, reset($roles));
$this->disable_role_sync($studentrole->id);
$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext);
$this->assertEmpty($roles);
}
public function test_handler_sync(): void {
global $DB, $CFG;
require_once($CFG->dirroot.'/enrol/category/locallib.php');
$this->resetAfterTest();
// Setup a few courses and categories.
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$managerrole = $DB->get_record('role', array('shortname'=>'manager'));
$this->assertNotEmpty($managerrole);
$cat1 = $this->getDataGenerator()->create_category();
$cat2 = $this->getDataGenerator()->create_category();
$cat3 = $this->getDataGenerator()->create_category(array('parent'=>$cat2->id));
$course1 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course2 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$course3 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$course4 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$this->enable_role_sync($studentrole->id);
$this->enable_role_sync($teacherrole->id);
$this->enable_plugin();
$this->assertEquals(0, $DB->count_records('role_assignments', array()));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
// Test assign event.
role_assign($managerrole->id, $user1->id, \context_coursecat::instance($cat1->id));
role_assign($managerrole->id, $user3->id, \context_course::instance($course1->id));
role_assign($managerrole->id, $user3->id, \context_course::instance($course2->id));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
role_assign($studentrole->id, $user1->id, \context_coursecat::instance($cat2->id));
$this->assertTrue(is_enrolled(\context_course::instance($course2->id), $user1->id));
$this->assertTrue(is_enrolled(\context_course::instance($course3->id), $user1->id));
$this->assertTrue(is_enrolled(\context_course::instance($course4->id), $user1->id));
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
role_assign($managerrole->id, $user2->id, \context_coursecat::instance($cat3->id));
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
role_assign($teacherrole->id, $user4->id, \context_coursecat::instance($cat1->id));
$this->assertTrue(is_enrolled(\context_course::instance($course1->id), $user4->id));
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
// Test role unassigned event.
role_unassign($teacherrole->id, $user4->id, \context_coursecat::instance($cat1->id)->id);
$this->assertFalse(is_enrolled(\context_course::instance($course1->id), $user4->id));
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
// Make sure handlers are disabled when plugin disabled.
$this->disable_plugin();
role_unassign($studentrole->id, $user1->id, \context_coursecat::instance($cat2->id)->id);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
role_assign($studentrole->id, $user3->id, \context_coursecat::instance($cat1->id));
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
}
public function test_sync_course(): void {
global $DB, $CFG;
require_once($CFG->dirroot.'/enrol/category/locallib.php');
$this->resetAfterTest();
// Setup a few courses and categories.
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$managerrole = $DB->get_record('role', array('shortname'=>'manager'));
$this->assertNotEmpty($managerrole);
$cat1 = $this->getDataGenerator()->create_category();
$cat2 = $this->getDataGenerator()->create_category();
$cat3 = $this->getDataGenerator()->create_category(array('parent'=>$cat2->id));
$course1 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course2 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$course3 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$course4 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$this->enable_role_sync($studentrole->id);
$this->enable_role_sync($teacherrole->id);
$this->enable_plugin();
$this->assertEquals(0, $DB->count_records('role_assignments', array()));
role_assign($managerrole->id, $user1->id, \context_coursecat::instance($cat1->id));
role_assign($managerrole->id, $user3->id, \context_course::instance($course1->id));
role_assign($managerrole->id, $user3->id, \context_course::instance($course2->id));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->disable_plugin(); // Stops the event handlers.
role_assign($studentrole->id, $user1->id, \context_coursecat::instance($cat2->id));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->enable_plugin();
enrol_category_sync_course($course2);
$this->assertTrue(is_enrolled(\context_course::instance($course2->id), $user1->id));
$this->assertFalse(is_enrolled(\context_course::instance($course3->id), $user1->id));
$this->assertFalse(is_enrolled(\context_course::instance($course4->id), $user1->id));
$this->assertEquals(1, $DB->count_records('user_enrolments', array()));
enrol_category_sync_course($course2);
enrol_category_sync_course($course3);
enrol_category_sync_course($course4);
$this->assertFalse(is_enrolled(\context_course::instance($course1->id), $user1->id));
$this->assertTrue(is_enrolled(\context_course::instance($course2->id), $user1->id));
$this->assertTrue(is_enrolled(\context_course::instance($course3->id), $user1->id));
$this->assertTrue(is_enrolled(\context_course::instance($course4->id), $user1->id));
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->disable_plugin(); // Stops the event handlers.
role_assign($studentrole->id, $user2->id, \context_coursecat::instance($cat1->id));
role_assign($teacherrole->id, $user4->id, \context_coursecat::instance($cat1->id));
role_unassign($studentrole->id, $user1->id, \context_coursecat::instance($cat2->id)->id);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->enable_plugin();
enrol_category_sync_course($course2);
$this->assertFalse(is_enrolled(\context_course::instance($course2->id), $user1->id));
$this->assertFalse(is_enrolled(\context_course::instance($course2->id), $user2->id));
$this->assertFalse(is_enrolled(\context_course::instance($course2->id), $user4->id));
enrol_category_sync_course($course1);
enrol_category_sync_course($course3);
enrol_category_sync_course($course4);
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->assertTrue(is_enrolled(\context_course::instance($course1->id), $user2->id));
$this->assertTrue(is_enrolled(\context_course::instance($course1->id), $user4->id));
$this->disable_role_sync($studentrole->id);
enrol_category_sync_course($course1);
enrol_category_sync_course($course2);
enrol_category_sync_course($course3);
enrol_category_sync_course($course4);
$this->assertEquals(1, $DB->count_records('user_enrolments', array()));
$this->assertTrue(is_enrolled(\context_course::instance($course1->id), $user4->id));
$this->assertEquals(1, $DB->count_records('enrol', array('enrol'=>'category')));
$this->disable_role_sync($teacherrole->id);
enrol_category_sync_course($course1);
enrol_category_sync_course($course2);
enrol_category_sync_course($course3);
enrol_category_sync_course($course4);
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->assertEquals(0, $DB->count_records('enrol', array('enrol'=>'category')));
}
public function test_sync_full(): void {
global $DB, $CFG;
require_once($CFG->dirroot.'/enrol/category/locallib.php');
$this->resetAfterTest();
$trace = new \null_progress_trace();
// Setup a few courses and categories.
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$managerrole = $DB->get_record('role', array('shortname'=>'manager'));
$this->assertNotEmpty($managerrole);
$cat1 = $this->getDataGenerator()->create_category();
$cat2 = $this->getDataGenerator()->create_category();
$cat3 = $this->getDataGenerator()->create_category(array('parent'=>$cat2->id));
$course1 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course2 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$course3 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$course4 = $this->getDataGenerator()->create_course(array('category'=>$cat3->id));
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$this->enable_role_sync($studentrole->id);
$this->enable_role_sync($teacherrole->id);
$this->enable_plugin();
$this->assertEquals(0, $DB->count_records('role_assignments', array()));
role_assign($managerrole->id, $user1->id, \context_coursecat::instance($cat1->id));
role_assign($managerrole->id, $user3->id, \context_course::instance($course1->id));
role_assign($managerrole->id, $user3->id, \context_course::instance($course2->id));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$result = enrol_category_sync_full($trace);
$this->assertSame(0, $result);
$this->disable_plugin();
role_assign($studentrole->id, $user1->id, \context_coursecat::instance($cat2->id));
$this->enable_plugin();
$result = enrol_category_sync_full($trace);
$this->assertSame(0, $result);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertTrue(is_enrolled(\context_course::instance($course2->id), $user1->id));
$this->assertTrue(is_enrolled(\context_course::instance($course3->id), $user1->id));
$this->assertTrue(is_enrolled(\context_course::instance($course4->id), $user1->id));
$this->disable_plugin();
role_unassign($studentrole->id, $user1->id, \context_coursecat::instance($cat2->id)->id);
role_assign($studentrole->id, $user2->id, \context_coursecat::instance($cat1->id));
role_assign($teacherrole->id, $user4->id, \context_coursecat::instance($cat1->id));
role_assign($teacherrole->id, $user3->id, \context_coursecat::instance($cat2->id));
role_assign($managerrole->id, $user3->id, \context_course::instance($course3->id));
$this->enable_plugin();
$result = enrol_category_sync_full($trace);
$this->assertSame(0, $result);
$this->assertEquals(5, $DB->count_records('user_enrolments', array()));
$this->assertTrue(is_enrolled(\context_course::instance($course1->id), $user2->id));
$this->assertTrue(is_enrolled(\context_course::instance($course1->id), $user4->id));
$this->assertTrue(is_enrolled(\context_course::instance($course2->id), $user3->id));
$this->assertTrue(is_enrolled(\context_course::instance($course3->id), $user3->id));
$this->assertTrue(is_enrolled(\context_course::instance($course4->id), $user3->id));
// Cleanup everything.
$this->assertNotEmpty($DB->count_records('role_assignments', array()));
$this->assertNotEmpty($DB->count_records('user_enrolments', array()));
$this->disable_plugin();
role_unassign_all(array('roleid'=>$studentrole->id));
role_unassign_all(array('roleid'=>$managerrole->id));
role_unassign_all(array('roleid'=>$teacherrole->id));
$result = enrol_category_sync_full($trace);
$this->assertSame(2, $result);
$this->assertEquals(0, $DB->count_records('role_assignments', array()));
$this->assertNotEmpty($DB->count_records('user_enrolments', array()));
$this->disable_role_sync($studentrole->id);
$this->disable_role_sync($teacherrole->id);
$this->enable_plugin();
$result = enrol_category_sync_full($trace);
$this->assertSame(0, $result);
$this->assertEquals(0, $DB->count_records('role_assignments', array()));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->assertEquals(0, $DB->count_records('enrol', array('enrol'=>'category')));
}
}
+29
View File
@@ -0,0 +1,29 @@
<?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/>.
/**
* Category enrolment plugin version specification.
*
* @package enrol_category
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'enrol_category'; // Full name of the plugin (used for diagnostics)
+53
View File
@@ -0,0 +1,53 @@
<?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 core_enrol\form;
/**
* Form to customise the course role names.
*
* @package core_enrol
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renameroles extends \moodleform {
/**
* Form definition.
*/
public function definition() {
$mform = $this->_form;
$courseid = $this->_customdata['id'];
$roles = $this->_customdata['roles'] ?? [];
$formdata = new \stdClass();
$mform->addElement('hidden', 'id', $courseid);
$mform->setType('id', PARAM_INT);
foreach ($roles as $role) {
$settingname = 'role_' . $role->id;
$mform->addElement('text', $settingname, get_string('yourwordforx', '', $role->localname));
$mform->setType($settingname, PARAM_TEXT);
$formdata->{$settingname} = $role->coursealias;
}
$mform->addElement('submit', 'submit', get_string('save'));
$this->set_data($formdata);
}
}
@@ -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 core_enrol\hook;
use stdClass;
/**
* Hook after enrolment status is changed.
*
* @package core_enrol
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions after the enrolment instance status is changed.')]
#[\core\attribute\tags('enrol')]
class after_enrol_instance_status_updated {
/**
* Constructor for the hook.
*
* @param stdClass $enrolinstance The enrol instance.
* @param int $newstatus The new status.
*/
public function __construct(
/** @var stdClass The enrol instance */
public readonly stdClass $enrolinstance,
/** @var int The new status */
public readonly int $newstatus,
) {
}
}
@@ -0,0 +1,62 @@
<?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 core_enrol\hook;
use stdClass;
/**
* Hook after a user is enrolled in a course for an enrolment instance.
*
* @package core_enrol
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions after a user is enrolled in a course.')]
#[\core\attribute\tags('enrol', 'user')]
class after_user_enrolled {
/**
* Constructor for the hook.
*
* @param stdClass $enrolinstance The enrol instance.
* @param stdClass $userenrolmentinstance The user enrolment instance.
*/
public function __construct(
/** @var stdClass The enrol instance */
public readonly stdClass $enrolinstance,
/** @var stdClass The user enrolment instance */
public readonly stdClass $userenrolmentinstance,
) {
}
/**
* Get the user id.
*
* @return int
*/
public function get_userid(): int {
return $this->userenrolmentinstance->userid;
}
/**
* Get the enrol instance.
*
* @return stdClass The enrol instance.
*/
public function get_enrolinstance(): stdClass {
return $this->enrolinstance;
}
}
@@ -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/>.
namespace core_enrol\hook;
use stdClass;
use Psr\EventDispatcher\StoppableEventInterface;
/**
* Hook before enrolment instance is deleted.
*
* @package core_enrol
* @copyright 20234 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions before the enrolment instance is deleted.')]
#[\core\attribute\tags('enrol', 'user')]
class before_enrol_instance_deleted implements
StoppableEventInterface
{
use \core\hook\stoppable_trait;
/**
* Constructor for the hook.
*
* @param stdClass $enrolinstance The enrol instance.
*/
public function __construct(
/** @var stdClass The enrol instance */
public readonly stdClass $enrolinstance,
) {
}
}
@@ -0,0 +1,53 @@
<?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 core_enrol\hook;
use stdClass;
/**
* Hook before a user is un-enrolled from a course for an enrolment instance.
*
* @package core_enrol
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions before a user enrolment is removed.')]
#[\core\attribute\tags('enrol', 'user')]
class before_user_enrolment_removed {
/**
* Constructor for the hook.
*
* @param stdClass $enrolinstance The enrol instance.
* @param stdClass $userenrolmentinstance The user enrolment instance.
*/
public function __construct(
/** @var stdClass The enrol instance */
public readonly stdClass $enrolinstance,
/** @var stdClass The user enrolment instance */
public readonly stdClass $userenrolmentinstance,
) {
}
/**
* Get the user id.
*
* @return int
*/
public function get_userid(): int {
return $this->userenrolmentinstance->userid;
}
}
@@ -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 core_enrol\hook;
use stdClass;
/**
* Hook before a user enrolment is updated.
*
* @package core_enrol
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions before a user enrolment is updated.')]
#[\core\attribute\tags('enrol', 'user')]
class before_user_enrolment_updated {
/**
* Constructor for the hook.
*
* @param stdClass $enrolinstance The enrol instance.
* @param stdClass $userenrolmentinstance The user enrolment instance.
* @param bool $statusmodified Whether the status of the enrolment has been modified.
* @param bool $timeendmodified Whether the time end of the enrolment has been modified.
*/
public function __construct(
/** @var stdClass The enrol instance */
public readonly stdClass $enrolinstance,
/** @var stdClass The user enrolment instance */
public readonly stdClass $userenrolmentinstance,
/** @var bool Whether the status of the enrolment has been modified */
public readonly bool $statusmodified,
/** @var bool Whether the time end of the enrolment has been modified */
public readonly bool $timeendmodified,
) {
}
/**
* Get the user id.
*
* @return int
*/
public function get_userid(): int {
return $this->userenrolmentinstance->userid;
}
}
+295
View File
@@ -0,0 +1,295 @@
<?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 core_enrol.
*
* @package core_enrol
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_enrol\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\context;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\transform;
use core_privacy\local\request\writer;
use core_privacy\local\request\userlist;
use \core_privacy\local\request\approved_userlist;
/**
* Privacy Subsystem for core_enrol implementing metadata and plugin providers.
*
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\core_userlist_provider,
\core_privacy\local\request\subsystem\provider {
/**
* Returns meta data about this system.
*
* @param collection $collection The initialised collection to add items to.
* @return collection A listing of user data stored through this system.
*/
public static function get_metadata(collection $collection): collection {
$collection->add_database_table(
'user_enrolments',
[
'status' => 'privacy:metadata:user_enrolments:status',
'enrolid' => 'privacy:metadata:user_enrolments:enrolid',
'userid' => 'privacy:metadata:user_enrolments:userid',
'timestart' => 'privacy:metadata:user_enrolments:timestart',
'timeend' => 'privacy:metadata:user_enrolments:timeend',
'modifierid' => 'privacy:metadata:user_enrolments:modifierid',
'timecreated' => 'privacy:metadata:user_enrolments:timecreated',
'timemodified' => 'privacy:metadata:user_enrolments:timemodified'
],
'privacy:metadata:user_enrolments:tableexplanation'
);
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid The user to search.
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$sql = "SELECT ctx.id
FROM {user_enrolments} ue
JOIN {enrol} e
ON e.id = ue.enrolid
AND ue.userid = :userid
JOIN {context} ctx
ON ctx.instanceid = e.courseid
AND ctx.contextlevel = :contextlevel";
$params = [
'contextlevel' => CONTEXT_COURSE,
'userid' => $userid
];
$contextlist = new contextlist();
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users within a specific context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if (!$context instanceof \context_course) {
return;
}
$sql = "SELECT ue.userid as userid
FROM {user_enrolments} ue
JOIN {enrol} e ON e.id = ue.enrolid
WHERE e.courseid = ?";
$params = [$context->instanceid];
$userlist->add_from_sql('userid', $sql, $params);
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
if (empty($contextlist->count())) {
return;
}
$userid = $contextlist->get_user()->id;
list($insql, $inparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
$params = [
'contextlevel' => CONTEXT_COURSE,
'userid' => $userid
];
$params += $inparams;
$sql = "SELECT ue.id,
ue.status,
ue.timestart,
ue.timeend,
ue.timecreated,
ue.timemodified,
e.enrol,
ctx.id as contextid
FROM {user_enrolments} ue
JOIN {enrol} e
ON e.id = ue.enrolid
AND ue.userid = :userid
JOIN {context} ctx
ON ctx.instanceid = e.courseid
AND ctx.contextlevel = :contextlevel
WHERE ctx.id $insql
ORDER BY ctx.id, e.enrol";
$data = [];
$lastcontextid = null;
$lastenrol = null;
$path = [get_string('privacy:metadata:user_enrolments', 'core_enrol')];
$flush = function($lastcontextid, $lastenrol, $data) use ($path) {
$context = \context::instance_by_id($lastcontextid);
writer::with_context($context)->export_related_data(
$path,
$lastenrol,
(object)$data
);
};
$userenrolments = $DB->get_recordset_sql($sql, $params);
foreach ($userenrolments as $userenrolment) {
if (($lastcontextid && $lastcontextid != $userenrolment->contextid) ||
($lastenrol && $lastenrol != $userenrolment->enrol)) {
$flush($lastcontextid, $lastenrol, $data);
$data = [];
}
$data[] = (object) [
'status' => $userenrolment->status,
'timecreated' => transform::datetime($userenrolment->timecreated),
'timemodified' => transform::datetime($userenrolment->timemodified),
'timestart' => transform::datetime($userenrolment->timestart),
'timeend' => transform::datetime($userenrolment->timeend)
];
$lastcontextid = $userenrolment->contextid;
$lastenrol = $userenrolment->enrol;
}
if (!empty($data)) {
$flush($lastcontextid, $lastenrol, $data);
}
$userenrolments->close();
}
/**
* Delete all data for all users in the specified context.
*
* @param \context $context The specific context to delete data for.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
global $DB;
// Sanity check that context is at the User context level.
if ($context->contextlevel == CONTEXT_COURSE) {
$sql = "SELECT ue.id
FROM {user_enrolments} ue
JOIN {enrol} e ON e.id = ue.enrolid
WHERE e.courseid = :courseid";
$params = ['courseid' => $context->instanceid];
$enrolsids = $DB->get_fieldset_sql($sql, $params);
if (!empty($enrolsids)) {
list($insql, $inparams) = $DB->get_in_or_equal($enrolsids, SQL_PARAMS_NAMED);
static::delete_user_data($insql, $inparams);
}
}
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
global $DB;
$context = $userlist->get_context();
if ($context instanceof \context_course) {
list($usersql, $userparams) = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
$sql = "SELECT ue.id
FROM {user_enrolments} ue
JOIN {enrol} e ON e.id = ue.enrolid
WHERE e.courseid = :courseid
AND ue.userid {$usersql}";
$params = ['courseid' => $context->instanceid] + $userparams;
$enrolsids = $DB->get_fieldset_sql($sql, $params);
if (!empty($enrolsids)) {
list($insql, $inparams) = $DB->get_in_or_equal($enrolsids, SQL_PARAMS_NAMED);
static::delete_user_data($insql, $inparams);
}
}
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
global $DB;
if (empty($contextlist->count())) {
return;
}
$userid = $contextlist->get_user()->id;
list($insql, $inparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
$params = [
'contextlevel' => CONTEXT_COURSE,
'userid' => $userid
];
$params += $inparams;
$sql = "SELECT ue.id
FROM {user_enrolments} ue
JOIN {enrol} e
ON e.id = ue.enrolid
AND ue.userid = :userid
JOIN {context} ctx
ON ctx.instanceid = e.courseid
AND ctx.contextlevel = :contextlevel
WHERE ctx.id $insql";
$enrolsids = $DB->get_fieldset_sql($sql, $params);
if (!empty($enrolsids)) {
list($insql, $inparams) = $DB->get_in_or_equal($enrolsids, SQL_PARAMS_NAMED);
static::delete_user_data($insql, $inparams);
}
}
/**
* Delete data from $tablename with the IDs returned by $sql query.
*
* @param string $sql SQL query for getting the IDs of the uer enrolments entries to delete.
* @param array $params SQL params for the query.
*/
protected static function delete_user_data(string $sql, array $params) {
global $DB;
$DB->delete_records_select('user_enrolments', "id $sql", $params);
}
/**
* Get the subcontext for export.
*
* @param array $subcontext Any additional subcontext to use.
* @return array The array containing the full subcontext, i.e. [enrolments, subcontext]
*/
public static function get_subcontext(array $subcontext) {
return array_merge(
[get_string('privacy:metadata:user_enrolments', 'core_enrol')],
$subcontext
);
}
}
@@ -0,0 +1,260 @@
<?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 core_enrol\reportbuilder\local\entities;
use enrol_plugin;
use lang_string;
use stdClass;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\{boolean_select, date, duration, select, text};
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\report\{column, filter};
/**
* Enrolment method entity
*
* @package core_enrol
* @copyright 2023 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol extends base {
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'enrol',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('enrolmentmethod', 'core_enrol');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this
->add_filter($filter)
->add_condition($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
global $DB;
$enrolalias = $this->get_table_alias('enrol');
// Plugin column.
$columns[] = (new column(
'plugin',
new lang_string('plugin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$enrolalias}.enrol")
->set_is_sortable(true)
->set_callback(static function(?string $enrol): string {
if ($enrol === null || !$plugin = enrol_get_plugin($enrol)) {
return '';
}
return $plugin->get_instance_name(null);
});
// Name column.
$columns[] = (new column(
'name',
new lang_string('name'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_fields("{$enrolalias}.enrol, {$enrolalias}.name, {$enrolalias}.courseid, " .
"{$enrolalias}.roleid, {$enrolalias}.customint1")
->set_is_sortable(true)
->set_callback(static function(?string $enrol, stdClass $instance): string {
if ($enrol === null || !$plugin = enrol_get_plugin($enrol)) {
return '';
}
return $plugin->get_instance_name($instance);
});
// Enabled column.
$columns[] = (new column(
'enabled',
new lang_string('enabled', 'core_admin'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_BOOLEAN)
// For accurate aggregation, we need to return boolean enabled = true by xor'ing the field value.
->add_field($DB->sql_bitxor("{$enrolalias}.status", 1), 'status')
->set_is_sortable(true)
->set_callback([format::class, 'boolean_as_text']);
// Period column.
$columns[] = (new column(
'period',
new lang_string('enrolperiod', 'core_enrol'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$enrolalias}.enrolperiod")
->set_is_sortable(true)
->set_callback(static function(?int $enrolperiod): string {
if (!$enrolperiod) {
return '';
}
return format_time($enrolperiod);
});
// Start date column.
$columns[] = (new column(
'startdate',
new lang_string('enroltimestart', 'core_enrol'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$enrolalias}.enrolstartdate")
->set_is_sortable(true)
->set_callback([format::class, 'userdate']);
// End date column.
$columns[] = (new column(
'enddate',
new lang_string('enroltimeend', 'core_enrol'),
$this->get_entity_name()
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_fields("{$enrolalias}.enrolenddate")
->set_is_sortable(true)
->set_callback([format::class, 'userdate']);
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
global $DB;
$enrolalias = $this->get_table_alias('enrol');
// Plugin filter.
$filters[] = (new filter(
select::class,
'plugin',
new lang_string('plugin'),
$this->get_entity_name(),
"{$enrolalias}.enrol"
))
->add_joins($this->get_joins())
->set_options_callback(static function(): array {
return array_map(static function(enrol_plugin $plugin): string {
return $plugin->get_instance_name(null);
}, enrol_get_plugins(true));
});
// Custom name filter.
$filters[] = (new filter(
text::class,
'customname',
new lang_string('custominstancename', 'core_enrol'),
$this->get_entity_name(),
"{$enrolalias}.name"
))
->add_joins($this->get_joins());
// Enabled filter.
$filters[] = (new filter(
boolean_select::class,
'enabled',
new lang_string('enabled', 'core_admin'),
$this->get_entity_name(),
$DB->sql_bitxor("{$enrolalias}.status", 1)
))
->add_joins($this->get_joins());
// Period filter.
$filters[] = (new filter(
duration::class,
'period',
new lang_string('enrolperiod', 'core_enrol'),
$this->get_entity_name(),
"{$enrolalias}.enrolperiod"
))
->add_joins($this->get_joins());
// Start date filter.
$filters[] = (new filter(
date::class,
'startdate',
new lang_string('enroltimestart', 'core_enrol'),
$this->get_entity_name(),
"{$enrolalias}.enrolstartdate"
))
->add_joins($this->get_joins());
// End date filter.
$filters[] = (new filter(
date::class,
'enddate',
new lang_string('enroltimeend', 'core_enrol'),
$this->get_entity_name(),
"{$enrolalias}.enrolenddate"
))
->add_joins($this->get_joins());
return $filters;
}
}
+145
View File
@@ -0,0 +1,145 @@
<?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 enrol_cohort.
*
* @package enrol_cohort
* @category privacy
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_cohort\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\userlist;
/**
* Privacy provider for enrol_cohort.
*
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
// This plugin stores user data.
\core_privacy\local\metadata\provider,
// This plugin contains user's enrolments.
\core_privacy\local\request\plugin\provider,
// This plugin is capable of determining which users have data within it.
\core_privacy\local\request\core_userlist_provider {
/**
* Returns meta data about this system.
*
* @param collection $collection The initialised item collection to add items to.
* @return collection A listing of user data stored through this system.
*/
public static function get_metadata(collection $collection): collection {
$collection->add_subsystem_link('core_group', [], 'privacy:metadata:core_group');
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid The user to search.
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
return \core_group\privacy\provider::get_contexts_for_group_member($userid, 'enrol_cohort');
}
/**
* Get the list of users who have data within a context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if (!$context instanceof \context_course) {
return;
}
\core_group\privacy\provider::get_group_members_in_context($userlist, 'enrol_cohort');
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
if (empty($contextlist)) {
return;
}
foreach ($contextlist as $context) {
if ($context->contextlevel == CONTEXT_COURSE) {
\core_group\privacy\provider::export_groups(
$context,
'enrol_cohort',
[get_string('pluginname', 'enrol_cohort')]
);
}
}
}
/**
* Delete all use data which matches the specified deletion_criteria.
*
* @param \context $context A user context.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
if (empty($context)) {
return;
}
if ($context->contextlevel == CONTEXT_COURSE) {
// Delete all the associated groups.
\core_group\privacy\provider::delete_groups_for_all_users($context, 'enrol_cohort');
}
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
if (empty($contextlist->count())) {
return;
}
\core_group\privacy\provider::delete_groups_for_user($contextlist, 'enrol_cohort');
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
\core_group\privacy\provider::delete_groups_for_users($userlist, 'enrol_cohort');
}
}
@@ -0,0 +1,60 @@
<?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/>.
/**
* Syncing enrolments task.
*
* @package enrol_cohort
* @author Farhan Karmali <farhan6318@gmail.com>
* @copyright Farhan Karmali
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_cohort\task;
defined('MOODLE_INTERNAL') || die();
/**
* Syncing enrolments task.
*
* @package enrol_cohort
* @author Farhan Karmali <farhan6318@gmail.com>
* @copyright Farhan Karmali
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol_cohort_sync extends \core\task\scheduled_task {
/**
* Name for this task.
*
* @return string
*/
public function get_name() {
return get_string('enrolcohortsynctask', 'enrol_cohort');
}
/**
* Run task for syncing cohort enrolments.
*/
public function execute() {
global $CFG;
require_once("$CFG->dirroot/enrol/cohort/locallib.php");
$trace = new \null_progress_trace();
enrol_cohort_sync($trace);
$trace->finished();
}
}
+70
View File
@@ -0,0 +1,70 @@
<?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/>.
/**
* CLI sync for cohort enrolments, use for debugging or immediate sync
* of all courses.
*
* Notes:
* - it is required to use the web server account when executing PHP CLI scripts
* - you need to change the "www-data" to match the apache user account
* - use "su" if "sudo" not available
*
* @package enrol_cohort
* @copyright 2011 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__.'/../../../config.php');
require_once("$CFG->libdir/clilib.php");
require_once("$CFG->dirroot/enrol/cohort/locallib.php");
// Now get cli options.
list($options, $unrecognized) = cli_get_params(array('verbose'=>false, 'help'=>false), array('v'=>'verbose', 'h'=>'help'));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
if ($options['help']) {
$help =
"Execute cohort course enrol sync.
Options:
-v, --verbose Print verbose progess information
-h, --help Print out this help
Example:
\$ sudo -u www-data /usr/bin/php enrol/cohort/cli/sync.php
";
echo $help;
die;
}
if (empty($options['verbose'])) {
$trace = new null_progress_trace();
} else {
$trace = new text_progress_trace();
}
$result = enrol_cohort_sync($trace, null);
$trace->finished();
exit($result);
+49
View File
@@ -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/>.
/**
* Capabilities for cohort access plugin.
*
* @package enrol_cohort
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
'enrol/cohort:config' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW,
)
),
/* This is used only when sync suspends users instead of full unenrolment. */
'enrol/cohort:unenrol' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'manager' => CAP_ALLOW,
)
),
);
+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/>.
/**
* Cohort enrolment plugin event handler definition.
*
* @package enrol_cohort
* @category event
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$observers = array(
array(
'eventname' => '\core\event\cohort_member_added',
'callback' => 'enrol_cohort_handler::member_added',
'includefile' => '/enrol/cohort/locallib.php'
),
array(
'eventname' => '\core\event\cohort_member_removed',
'callback' => 'enrol_cohort_handler::member_removed',
'includefile' => '/enrol/cohort/locallib.php'
),
array(
'eventname' => '\core\event\cohort_deleted',
'callback' => 'enrol_cohort_handler::deleted',
'includefile' => '/enrol/cohort/locallib.php'
),
);
+39
View File
@@ -0,0 +1,39 @@
<?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/>.
/**
* Task definition for enrol_cohort.
* @author Farhan Karmali <farhan6318@gmail.com>
* @copyright Farhan Karmali
* @package enrol_cohort
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$tasks = array(
array(
'classname' => '\enrol_cohort\task\enrol_cohort_sync',
'blocking' => 0,
'minute' => 'R',
'hour' => '*',
'day' => '*',
'month' => '*',
'dayofweek' => '*',
'disabled' => 0
)
);
+40
View File
@@ -0,0 +1,40 @@
<?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/>.
/**
* Meta link enrolment plugin uninstallation.
*
* @package enrol_cohort
* @copyright 2011 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
function xmldb_enrol_cohort_uninstall() {
global $CFG, $DB;
$cohort = enrol_get_plugin('cohort');
$rs = $DB->get_recordset('enrol', array('enrol'=>'cohort'));
foreach ($rs as $instance) {
$cohort->delete_instance($instance);
}
$rs->close();
role_unassign_all(array('component'=>'enrol_cohort'));
return true;
}
+1
View File
@@ -0,0 +1 @@
plugindisabled,enrol_cohort
+39
View File
@@ -0,0 +1,39 @@
<?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 component 'enrol_cohort', language 'en'.
*
* @package enrol_cohort
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['addgroup'] = 'Add to group';
$string['assignrole'] = 'Assign role';
$string['cohort:config'] = 'Configure cohort instances';
$string['cohort:unenrol'] = 'Unenrol suspended users';
$string['defaultgroupnametext'] = '{$a->name} cohort {$a->increment}';
$string['enrolcohortsynctask'] = 'Cohort enrolment sync task';
$string['instanceexists'] = 'Cohort is already synchronised with selected role';
$string['pluginname'] = 'Cohort sync';
$string['pluginname_desc'] = 'Cohort enrolment plugin synchronises cohort members with course participants.';
$string['status'] = 'Active';
$string['creategroup'] = 'Create new group';
$string['privacy:metadata:core_group'] = 'Enrol cohort plugin can create a new group or use an existing group to add all the members of the cohort.';
// Deprecated since Moodle 4.5.
$string['plugindisabled'] = 'Cohort sync plugin is disabled';
+725
View File
@@ -0,0 +1,725 @@
<?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/>.
/**
* Cohort enrolment plugin.
*
* @package enrol_cohort
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* COHORT_CREATEGROUP constant for automatically creating a group for a cohort.
*/
define('COHORT_CREATE_GROUP', -1);
/**
* COHORT_NOGROUP constant for using no group for a cohort.
*/
define('COHORT_NOGROUP', 0);
/**
* Cohort enrolment plugin implementation.
* @author Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol_cohort_plugin extends enrol_plugin {
/**
* Is it possible to delete enrol instance via standard UI?
*
* @param stdClass $instance
* @return bool
*/
public function can_delete_instance($instance) {
$context = context_course::instance($instance->courseid);
return has_capability('enrol/cohort:config', $context);
}
/**
* Returns localised name of enrol instance.
*
* @param stdClass $instance (null is accepted too)
* @return string
*/
public function get_instance_name($instance) {
global $DB;
if (empty($instance)) {
$enrol = $this->get_name();
return get_string('pluginname', 'enrol_'.$enrol);
} else if (empty($instance->name)) {
$enrol = $this->get_name();
$cohort = $DB->get_record('cohort', array('id'=>$instance->customint1));
if (!$cohort) {
return get_string('pluginname', 'enrol_'.$enrol);
}
$cohortname = format_string($cohort->name, true, array('context'=>context::instance_by_id($cohort->contextid)));
if ($role = $DB->get_record('role', array('id'=>$instance->roleid))) {
$role = role_get_name($role, context_course::instance($instance->courseid, IGNORE_MISSING), ROLENAME_BOTH);
return get_string('pluginname', 'enrol_'.$enrol) . ' (' . $cohortname . ' - ' . $role .')';
} else {
return get_string('pluginname', 'enrol_'.$enrol) . ' (' . $cohortname . ')';
}
} else {
return format_string($instance->name, true, array('context'=>context_course::instance($instance->courseid)));
}
}
/**
* Given a courseid this function returns true if the user is able to enrol or configure cohorts.
* AND there are cohorts that the user can view.
*
* @param int $courseid
* @return bool
*/
public function can_add_instance($courseid) {
global $CFG;
require_once($CFG->dirroot . '/cohort/lib.php');
$coursecontext = context_course::instance($courseid);
if (!has_capability('moodle/course:enrolconfig', $coursecontext) or !has_capability('enrol/cohort:config', $coursecontext)) {
return false;
}
return cohort_get_available_cohorts($coursecontext, 0, 0, 1) ? true : false;
}
/**
* Add new instance of enrol plugin.
* @param object $course
* @param array $fields instance fields
* @return int id of new instance, null if can not be created
*/
public function add_instance($course, array $fields = null) {
global $CFG;
// Allows multiple cohorts to be set on creation.
if (!empty($fields['customint1'])) {
$fields2 = $fields;
if (!is_array($fields['customint1'])) {
$fields['customint1'] = array($fields['customint1']);
}
foreach ($fields['customint1'] as $cid) {
$fields2['customint1'] = $cid;
if (!empty($fields['customint2']) && $fields['customint2'] == COHORT_CREATE_GROUP) {
// Create a new group for the cohort if requested.
$context = context_course::instance($course->id);
require_capability('moodle/course:managegroups', $context);
$groupid = enrol_cohort_create_new_group($course->id, $cid);
$fields2['customint2'] = $groupid;
}
$result = parent::add_instance($course, $fields2);
}
} else {
$result = parent::add_instance($course, $fields);
}
require_once("$CFG->dirroot/enrol/cohort/locallib.php");
$trace = new null_progress_trace();
enrol_cohort_sync($trace, $course->id);
$trace->finished();
return $result;
}
/**
* Update instance of enrol plugin.
* @param stdClass $instance
* @param stdClass $data modified instance fields
* @return boolean
*/
public function update_instance($instance, $data) {
global $CFG;
// NOTE: no cohort changes here!!!
$context = context_course::instance($instance->courseid);
if ($data->roleid != $instance->roleid) {
// The sync script can only add roles, for perf reasons it does not modify them.
$params = array(
'contextid' => $context->id,
'roleid' => $instance->roleid,
'component' => 'enrol_cohort',
'itemid' => $instance->id
);
role_unassign_all($params);
}
// Create a new group for the cohort if requested.
if ($data->customint2 == COHORT_CREATE_GROUP) {
require_capability('moodle/course:managegroups', $context);
$groupid = enrol_cohort_create_new_group($instance->courseid, $data->customint1);
$data->customint2 = $groupid;
}
$result = parent::update_instance($instance, $data);
require_once("$CFG->dirroot/enrol/cohort/locallib.php");
$trace = new null_progress_trace();
enrol_cohort_sync($trace, $instance->courseid);
$trace->finished();
return $result;
}
/**
* Called after updating/inserting course.
*
* @param bool $inserted true if course just inserted
* @param stdClass $course
* @param stdClass $data form data
* @return void
*/
public function course_updated($inserted, $course, $data) {
// It turns out there is no need for cohorts to deal with this hook, see MDL-34870.
}
/**
* Update instance status
*
* @param stdClass $instance
* @param int $newstatus ENROL_INSTANCE_ENABLED, ENROL_INSTANCE_DISABLED
* @return void
*/
public function update_status($instance, $newstatus) {
global $CFG;
parent::update_status($instance, $newstatus);
require_once("$CFG->dirroot/enrol/cohort/locallib.php");
$trace = new null_progress_trace();
enrol_cohort_sync($trace, $instance->courseid);
$trace->finished();
}
/**
* Does this plugin allow manual unenrolment of a specific user?
* Yes, but only if user suspended...
*
* @param stdClass $instance course enrol instance
* @param stdClass $ue record from user_enrolments table
*
* @return bool - true means user with 'enrol/xxx:unenrol' may unenrol this user, false means nobody may touch this user enrolment
*/
public function allow_unenrol_user(stdClass $instance, stdClass $ue) {
if ($ue->status == ENROL_USER_SUSPENDED) {
return true;
}
return false;
}
/**
* Restore instance and map settings.
*
* @param restore_enrolments_structure_step $step
* @param stdClass $data
* @param stdClass $course
* @param int $oldid
*/
public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
global $DB, $CFG;
if (!$step->get_task()->is_samesite()) {
// No cohort restore from other sites.
$step->set_mapping('enrol', $oldid, 0);
return;
}
if (!empty($data->customint2)) {
$data->customint2 = $step->get_mappingid('group', $data->customint2);
}
if ($data->roleid and $DB->record_exists('cohort', array('id'=>$data->customint1))) {
$instance = $DB->get_record('enrol', array('roleid'=>$data->roleid, 'customint1'=>$data->customint1, 'courseid'=>$course->id, 'enrol'=>$this->get_name()));
if ($instance) {
$instanceid = $instance->id;
} else {
$instanceid = $this->add_instance($course, (array)$data);
}
$step->set_mapping('enrol', $oldid, $instanceid);
require_once("$CFG->dirroot/enrol/cohort/locallib.php");
$trace = new null_progress_trace();
enrol_cohort_sync($trace, $course->id);
$trace->finished();
} else if ($this->get_config('unenrolaction') == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
$data->customint1 = 0;
$instance = $DB->get_record('enrol', array('roleid'=>$data->roleid, 'customint1'=>$data->customint1, 'courseid'=>$course->id, 'enrol'=>$this->get_name()));
if ($instance) {
$instanceid = $instance->id;
} else {
$data->status = ENROL_INSTANCE_DISABLED;
$instanceid = $this->add_instance($course, (array)$data);
}
$step->set_mapping('enrol', $oldid, $instanceid);
require_once("$CFG->dirroot/enrol/cohort/locallib.php");
$trace = new null_progress_trace();
enrol_cohort_sync($trace, $course->id);
$trace->finished();
} else {
$step->set_mapping('enrol', $oldid, 0);
}
}
/**
* Restore user enrolment.
*
* @param restore_enrolments_structure_step $step
* @param stdClass $data
* @param stdClass $instance
* @param int $oldinstancestatus
* @param int $userid
*/
public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
global $DB;
if ($this->get_config('unenrolaction') != ENROL_EXT_REMOVED_SUSPENDNOROLES) {
// Enrolments were already synchronised in restore_instance(), we do not want any suspended leftovers.
return;
}
// ENROL_EXT_REMOVED_SUSPENDNOROLES means all previous enrolments are restored
// but without roles and suspended.
if (!$DB->record_exists('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
$this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, ENROL_USER_SUSPENDED);
}
}
/**
* Restore user group membership.
* @param stdClass $instance
* @param int $groupid
* @param int $userid
*/
public function restore_group_member($instance, $groupid, $userid) {
// Nothing to do here, the group members are added in $this->restore_group_restored()
return;
}
/**
* Is it possible to hide/show enrol instance via standard UI?
*
* @param stdClass $instance
* @return bool
*/
public function can_hide_show_instance($instance) {
$context = context_course::instance($instance->courseid);
return has_capability('enrol/cohort:config', $context);
}
/**
* Return an array of valid options for the status.
*
* @return array
*/
protected function get_status_options() {
$options = array(ENROL_INSTANCE_ENABLED => get_string('yes'),
ENROL_INSTANCE_DISABLED => get_string('no'));
return $options;
}
/**
* Return an array of valid options for the cohorts.
*
* @param stdClass $instance
* @param context $context
* @return array
*/
protected function get_cohort_options($instance, $context) {
global $DB, $CFG;
require_once($CFG->dirroot . '/cohort/lib.php');
$cohorts = array();
if ($instance->id) {
if ($cohort = $DB->get_record('cohort', array('id' => $instance->customint1))) {
$name = format_string($cohort->name, true, array('context' => context::instance_by_id($cohort->contextid)));
$cohorts = array($instance->customint1 => $name);
} else {
$cohorts = array($instance->customint1 => get_string('error'));
}
} else {
$cohorts = array('' => get_string('choosedots'));
$allcohorts = cohort_get_available_cohorts($context, 0, 0, 0);
foreach ($allcohorts as $c) {
$cohorts[$c->id] = format_string($c->name);
}
}
return $cohorts;
}
/**
* Return an array of valid options for the roles.
*
* @param stdClass $instance
* @param context $coursecontext
* @return array
*/
protected function get_role_options($instance, $coursecontext) {
global $DB;
$roles = get_assignable_roles($coursecontext, ROLENAME_BOTH);
$roles[0] = get_string('none');
$roles = array_reverse($roles, true); // Descending default sortorder.
// If the instance is already configured, but the configured role is no longer assignable in the course then add it back.
if ($instance->id and !isset($roles[$instance->roleid])) {
if ($role = $DB->get_record('role', array('id' => $instance->roleid))) {
$roles[$instance->roleid] = role_get_name($role, $coursecontext, ROLENAME_BOTH);
} else {
$roles[$instance->roleid] = get_string('error');
}
}
return $roles;
}
/**
* Return an array of valid options for the groups.
*
* @param context $coursecontext
* @return array
*/
protected function get_group_options($coursecontext) {
$groups = array(0 => get_string('none'));
if (has_capability('moodle/course:managegroups', $coursecontext)) {
$groups[COHORT_CREATE_GROUP] = get_string('creategroup', 'enrol_cohort');
}
foreach (groups_get_all_groups($coursecontext->instanceid) as $group) {
$groups[$group->id] = format_string($group->name, true, array('context' => $coursecontext));
}
return $groups;
}
/**
* We are a good plugin and don't invent our own UI/validation code path.
*
* @return boolean
*/
public function use_standard_editing_ui() {
return true;
}
/**
* Add elements to the edit instance form.
*
* @param stdClass $instance
* @param MoodleQuickForm $mform
* @param context $coursecontext
* @return bool
*/
public function edit_instance_form($instance, MoodleQuickForm $mform, $coursecontext) {
$options = $this->get_status_options();
$mform->addElement('select', 'status', get_string('status', 'enrol_cohort'), $options);
$options = ['contextid' => $coursecontext->id, 'multiple' => true];
$mform->addElement('cohort', 'customint1', get_string('cohort', 'cohort'), $options);
if ($instance->id) {
$mform->setConstant('customint1', $instance->customint1);
$mform->hardFreeze('customint1', $instance->customint1);
} else {
$mform->addRule('customint1', get_string('required'), 'required', null, 'client');
}
$roles = $this->get_role_options($instance, $coursecontext);
$mform->addElement('select', 'roleid', get_string('assignrole', 'enrol_cohort'), $roles);
$mform->setDefault('roleid', $this->get_config('roleid'));
$groups = $this->get_group_options($coursecontext);
$mform->addElement('select', 'customint2', get_string('addgroup', 'enrol_cohort'), $groups);
}
/**
* Perform custom validation of the data used to edit the instance.
*
* @param array $data array of ("fieldname" => value) of submitted data
* @param array $files array of uploaded files "element_name" => tmp_file_path
* @param object $instance The instance loaded from the DB
* @param context $context The context of the instance we are editing
* @return array of "element_name" => "error_description" if there are errors,
* or an empty array if everything is OK.
* @return void
*/
public function edit_instance_validation($data, $files, $instance, $context) {
global $DB;
$errors = array();
// Allows multiple cohorts to be selected.
list($sql1, $params1) = $DB->get_in_or_equal($data['customint1'], SQL_PARAMS_NAMED);
$params = array(
'roleid' => $data['roleid'],
'courseid' => $data['courseid'],
'id' => $data['id']
);
$params = array_merge($params, $params1);
$sql = "roleid = :roleid AND customint1 $sql1 AND courseid = :courseid AND enrol = 'cohort' AND id <> :id";
if ($DB->record_exists_select('enrol', $sql, $params)) {
$errors['customint1'] = get_string('instanceexists', 'enrol_cohort');
}
$validstatus = array_keys($this->get_status_options());
$validcohorts = array_keys($this->get_cohort_options($instance, $context));
$validroles = array_keys($this->get_role_options($instance, $context));
$validgroups = array_keys($this->get_group_options($context));
$tovalidate = array(
'status' => $validstatus,
'roleid' => $validroles,
'customint2' => $validgroups
);
$typeerrors = $this->validate_param_types($data, $tovalidate);
// When creating a new cohort enrolment, we allow multiple cohorts in just one go.
// When editing an existing enrolment, changing the cohort is no allowed, so cohort is a single value.
if (is_array($data['customint1'])) {
$cohorts = $data['customint1'];
} else {
$cohorts = [$data['customint1']];
}
$errors = array_merge($errors, $typeerrors);
// Check that the cohorts passed are valid.
if (!empty(array_diff($cohorts, $validcohorts))) {
$errors['customint1'] = get_string('invaliddata', 'error');
}
return $errors;
}
/**
* Check if data is valid for a given enrolment plugin
*
* @param array $enrolmentdata enrolment data to validate.
* @param int|null $courseid Course ID.
* @return array Errors
*/
public function validate_enrol_plugin_data(array $enrolmentdata, ?int $courseid = null): array {
global $DB;
$errors = parent::validate_enrol_plugin_data($enrolmentdata, $courseid);
if (isset($enrolmentdata['addtogroup'])) {
$addtogroup = $enrolmentdata['addtogroup'];
if (($addtogroup == - COHORT_CREATE_GROUP) || $addtogroup == COHORT_NOGROUP) {
if (isset($enrolmentdata['groupname'])) {
$errors['erroraddtogroupgroupname'] =
new lang_string('erroraddtogroupgroupname', 'group');
}
} else {
$errors['erroraddtogroup'] =
new lang_string('erroraddtogroup', 'group');
}
}
if ($courseid) {
$enrolmentdata = $this->fill_enrol_custom_fields($enrolmentdata, $courseid);
$error = $this->validate_plugin_data_context($enrolmentdata, $courseid);
if ($error) {
$errors['contextnotallowed'] = $error;
}
if (isset($enrolmentdata['groupname']) && $enrolmentdata['groupname']) {
$groupname = $enrolmentdata['groupname'];
if (!groups_get_group_by_name($courseid, $groupname)) {
$errors['errorinvalidgroup'] =
new lang_string('errorinvalidgroup', 'group', $groupname);
}
}
}
if (!isset($enrolmentdata['cohortidnumber'])) {
$missingmandatoryfields = 'cohortidnumber';
} else {
$cohortidnumber = $enrolmentdata['cohortidnumber'];
// Cohort idnumber is unique.
$cohortid = $DB->get_field('cohort', 'id', ['idnumber' => $cohortidnumber]);
if (!$cohortid) {
$errors['unknowncohort'] =
new lang_string('unknowncohort', 'cohort', $cohortidnumber);
}
}
if (!isset($enrolmentdata['role'])) {
// We require role since we need it to identify enrol instance.
if (isset($missingmandatoryfields)) {
$missingmandatoryfields .= ', role';
} else {
$missingmandatoryfields = 'role';
}
$errors['missingmandatoryfields'] =
new lang_string('missingmandatoryfields', 'tool_uploadcourse',
$missingmandatoryfields);
} else {
$roleid = $DB->get_field('role', 'id', ['shortname' => $enrolmentdata['role']]);
if (!$roleid) {
$errors['unknownrole'] =
new lang_string('unknownrole', 'error', s($enrolmentdata['role']));
}
}
return $errors;
}
/**
* Fill custom fields data for a given enrolment plugin.
*
* @param array $enrolmentdata enrolment data.
* @param int $courseid Course ID.
* @return array Updated enrolment data with custom fields info.
*/
public function fill_enrol_custom_fields(array $enrolmentdata, int $courseid): array {
global $DB;
if (isset($enrolmentdata['cohortidnumber'])) {
// Cohort idnumber is unique.
$enrolmentdata['customint1'] =
$DB->get_field('cohort', 'id', ['idnumber' => $enrolmentdata['cohortidnumber']]);
}
if (isset($enrolmentdata['addtogroup'])) {
if ($enrolmentdata['addtogroup'] == COHORT_NOGROUP) {
$enrolmentdata['customint2'] = COHORT_NOGROUP;
} else if ($enrolmentdata['addtogroup'] == - COHORT_CREATE_GROUP) {
$enrolmentdata['customint2'] = COHORT_CREATE_GROUP;
}
} else if (isset($enrolmentdata['groupname'])) {
$enrolmentdata['customint2'] = groups_get_group_by_name($courseid, $enrolmentdata['groupname']);
}
return $enrolmentdata;
}
/**
* Check if plugin custom data is allowed in relevant context.
*
* @param array $enrolmentdata enrolment data to validate.
* @param int|null $courseid Course ID.
* @return lang_string|null Error
*/
public function validate_plugin_data_context(array $enrolmentdata, ?int $courseid = null): ?lang_string {
$error = null;
if (isset($enrolmentdata['customint1'])) {
$cohortid = $enrolmentdata['customint1'];
$coursecontext = \context_course::instance($courseid);
if (!cohort_get_cohort($cohortid, $coursecontext)) {
$error = new lang_string('contextcohortnotallowed', 'cohort', $enrolmentdata['cohortidnumber']);
}
}
return $error;
}
/**
* Add new instance of enrol plugin with custom settings,
* called when adding new instance manually or when adding new course.
* Used for example on course upload.
*
* Not all plugins support this.
*
* @param stdClass $course Course object
* @param array|null $fields instance fields
* @return int|null id of new instance or null if not supported
*/
public function add_custom_instance(stdClass $course, ?array $fields = null): ?int {
return $this->add_instance($course, $fields);
}
/**
* Check if enrolment plugin is supported in csv course upload.
*
* @return bool
*/
public function is_csv_upload_supported(): bool {
return true;
}
/**
* Finds matching instances for a given course.
*
* @param array $enrolmentdata enrolment data.
* @param int $courseid Course ID.
* @return stdClass|null Matching instance
*/
public function find_instance(array $enrolmentdata, int $courseid): ?stdClass {
global $DB;
$instances = enrol_get_instances($courseid, false);
$instance = null;
if (isset($enrolmentdata['cohortidnumber']) && isset($enrolmentdata['role'])) {
$cohortid = $DB->get_field('cohort', 'id', ['idnumber' => $enrolmentdata['cohortidnumber']]);
$roleid = $DB->get_field('role', 'id', ['shortname' => $enrolmentdata['role']]);
if ($cohortid && $roleid) {
foreach ($instances as $i) {
if ($i->enrol == 'cohort' && $i->customint1 == $cohortid && $i->roleid == $roleid) {
$instance = $i;
break;
}
}
}
}
return $instance;
}
}
/**
* Prevent removal of enrol roles.
* @param int $itemid
* @param int $groupid
* @param int $userid
* @return bool
*/
function enrol_cohort_allow_group_member_remove($itemid, $groupid, $userid) {
return false;
}
/**
* Create a new group with the cohorts name.
*
* @param int $courseid
* @param int $cohortid
* @return int $groupid Group ID for this cohort.
*/
function enrol_cohort_create_new_group($courseid, $cohortid) {
global $DB, $CFG;
require_once($CFG->dirroot . '/group/lib.php');
$groupname = $DB->get_field('cohort', 'name', array('id' => $cohortid), MUST_EXIST);
$a = new stdClass();
$a->name = $groupname;
$a->increment = '';
$groupname = trim(get_string('defaultgroupnametext', 'enrol_cohort', $a));
$inc = 1;
// Check to see if the cohort group name already exists. Add an incremented number if it does.
while ($DB->record_exists('groups', array('name' => $groupname, 'courseid' => $courseid))) {
$a->increment = '(' . (++$inc) . ')';
$newshortname = trim(get_string('defaultgroupnametext', 'enrol_cohort', $a));
$groupname = $newshortname;
}
// Create a new group for the cohort.
$groupdata = new stdClass();
$groupdata->courseid = $courseid;
$groupdata->name = $groupname;
$groupid = groups_create_group($groupdata);
return $groupid;
}
+313
View File
@@ -0,0 +1,313 @@
<?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/>.
/**
* Local stuff for cohort enrolment plugin.
*
* @package enrol_cohort
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/enrol/locallib.php');
require_once($CFG->dirroot . '/cohort/lib.php');
/**
* Event handler for cohort enrolment plugin.
*
* We try to keep everything in sync via listening to events,
* it may fail sometimes, so we always do a full sync in cron too.
*/
class enrol_cohort_handler {
/**
* Event processor - cohort member added.
* @param \core\event\cohort_member_added $event
* @return bool
*/
public static function member_added(\core\event\cohort_member_added $event) {
global $DB, $CFG;
require_once("$CFG->dirroot/group/lib.php");
if (!enrol_is_enabled('cohort')) {
return true;
}
// Does any enabled cohort instance want to sync with this cohort?
$sql = "SELECT e.*, r.id as roleexists
FROM {enrol} e
LEFT JOIN {role} r ON (r.id = e.roleid)
WHERE e.customint1 = :cohortid AND e.enrol = 'cohort' AND e.status = :enrolstatus
ORDER BY e.id ASC";
$params['cohortid'] = $event->objectid;
$params['enrolstatus'] = ENROL_INSTANCE_ENABLED;
if (!$instances = $DB->get_records_sql($sql, $params)) {
return true;
}
$plugin = enrol_get_plugin('cohort');
foreach ($instances as $instance) {
if ($instance->status != ENROL_INSTANCE_ENABLED ) {
// No roles for disabled instances.
$instance->roleid = 0;
} else if ($instance->roleid and !$instance->roleexists) {
// Invalid role - let's just enrol, they will have to create new sync and delete this one.
$instance->roleid = 0;
}
unset($instance->roleexists);
// No problem if already enrolled.
$plugin->enrol_user($instance, $event->relateduserid, $instance->roleid, 0, 0, ENROL_USER_ACTIVE);
// Sync groups.
if ($instance->customint2) {
if (!groups_is_member($instance->customint2, $event->relateduserid)) {
if ($group = $DB->get_record('groups', array('id'=>$instance->customint2, 'courseid'=>$instance->courseid))) {
groups_add_member($group->id, $event->relateduserid, 'enrol_cohort', $instance->id);
}
}
}
}
return true;
}
/**
* Event processor - cohort member removed.
* @param \core\event\cohort_member_removed $event
* @return bool
*/
public static function member_removed(\core\event\cohort_member_removed $event) {
global $DB;
// Does anything want to sync with this cohort?
if (!$instances = $DB->get_records('enrol', array('customint1'=>$event->objectid, 'enrol'=>'cohort'), 'id ASC')) {
return true;
}
$plugin = enrol_get_plugin('cohort');
$unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
foreach ($instances as $instance) {
if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$event->relateduserid))) {
continue;
}
if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
$plugin->unenrol_user($instance, $event->relateduserid);
} else {
if ($ue->status != ENROL_USER_SUSPENDED) {
$plugin->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
$context = context_course::instance($instance->courseid);
if ($unenrolaction != ENROL_EXT_REMOVED_SUSPEND) {
role_unassign_all(array('userid' => $ue->userid, 'contextid' => $context->id,
'component' => 'enrol_cohort', 'itemid' => $instance->id));
}
}
}
}
return true;
}
/**
* Event processor - cohort deleted.
* @param \core\event\cohort_deleted $event
* @return bool
*/
public static function deleted(\core\event\cohort_deleted $event) {
global $DB;
// Does anything want to sync with this cohort?
if (!$instances = $DB->get_records('enrol', array('customint1'=>$event->objectid, 'enrol'=>'cohort'), 'id ASC')) {
return true;
}
$plugin = enrol_get_plugin('cohort');
$unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
foreach ($instances as $instance) {
if ($unenrolaction != ENROL_EXT_REMOVED_UNENROL) {
$context = context_course::instance($instance->courseid);
if ($unenrolaction != ENROL_EXT_REMOVED_SUSPEND) {
role_unassign_all(array('contextid' => $context->id, 'component' => 'enrol_cohort',
'itemid' => $instance->id));
}
$plugin->update_status($instance, ENROL_INSTANCE_DISABLED);
} else {
$plugin->delete_instance($instance);
}
}
return true;
}
}
/**
* Sync all cohort course links.
* @param progress_trace $trace
* @param int $courseid one course, empty mean all
* @return int 0 means ok, 1 means error, 2 means plugin disabled
*/
function enrol_cohort_sync(progress_trace $trace, $courseid = NULL) {
global $CFG, $DB;
require_once("$CFG->dirroot/group/lib.php");
// Purge all roles if cohort sync disabled, those can be recreated later here by cron or CLI.
if (!enrol_is_enabled('cohort')) {
$trace->output('Cohort sync plugin is disabled, unassigning all plugin roles and stopping.');
role_unassign_all(array('component'=>'enrol_cohort'));
return 2;
}
// Unfortunately this may take a long time, this script can be interrupted without problems.
core_php_time_limit::raise();
raise_memory_limit(MEMORY_HUGE);
$trace->output('Starting user enrolment synchronisation...');
$allroles = get_all_roles();
$instances = array(); //cache
$plugin = enrol_get_plugin('cohort');
$unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
// Iterate through all not enrolled yet users.
$onecourse = $courseid ? "AND e.courseid = :courseid" : "";
$sql = "SELECT cm.userid, e.id AS enrolid, ue.status
FROM {cohort_members} cm
JOIN {enrol} e ON (e.customint1 = cm.cohortid AND e.enrol = 'cohort' AND e.status = :enrolstatus $onecourse)
JOIN {user} u ON (u.id = cm.userid AND u.deleted = 0)
LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = cm.userid)
WHERE ue.id IS NULL OR ue.status = :suspended";
$params = array();
$params['courseid'] = $courseid;
$params['suspended'] = ENROL_USER_SUSPENDED;
$params['enrolstatus'] = ENROL_INSTANCE_ENABLED;
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $ue) {
if (!isset($instances[$ue->enrolid])) {
$instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
}
$instance = $instances[$ue->enrolid];
if ($ue->status == ENROL_USER_SUSPENDED) {
$plugin->update_user_enrol($instance, $ue->userid, ENROL_USER_ACTIVE);
$trace->output("unsuspending: $ue->userid ==> $instance->courseid via cohort $instance->customint1", 1);
} else {
$plugin->enrol_user($instance, $ue->userid);
$trace->output("enrolling: $ue->userid ==> $instance->courseid via cohort $instance->customint1", 1);
}
}
$rs->close();
// Unenrol as necessary.
$sql = "SELECT ue.*, e.courseid
FROM {user_enrolments} ue
JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'cohort' $onecourse)
LEFT JOIN {cohort_members} cm ON (cm.cohortid = e.customint1 AND cm.userid = ue.userid)
WHERE cm.id IS NULL";
$rs = $DB->get_recordset_sql($sql, array('courseid'=>$courseid));
foreach($rs as $ue) {
if (!isset($instances[$ue->enrolid])) {
$instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
}
$instance = $instances[$ue->enrolid];
if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
// Remove enrolment together with group membership, grades, preferences, etc.
$plugin->unenrol_user($instance, $ue->userid);
$trace->output("unenrolling: $ue->userid ==> $instance->courseid via cohort $instance->customint1", 1);
} else { // ENROL_EXT_REMOVED_SUSPENDNOROLES
// Just disable and ignore any changes.
if ($ue->status != ENROL_USER_SUSPENDED) {
$plugin->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
$context = context_course::instance($instance->courseid);
if ($unenrolaction != ENROL_EXT_REMOVED_SUSPEND) {
role_unassign_all(array('userid' => $ue->userid, 'contextid' => $context->id,
'component' => 'enrol_cohort', 'itemid' => $instance->id));
$trace->output("unsassigning all roles: $ue->userid ==> $instance->courseid", 1);
}
$trace->output("suspending: $ue->userid ==> $instance->courseid", 1);
}
}
}
$rs->close();
unset($instances);
// Now assign all necessary roles to enrolled users - skip suspended instances and users.
$onecourse = $courseid ? "AND e.courseid = :courseid" : "";
$sql = "SELECT e.roleid, ue.userid, c.id AS contextid, e.id AS itemid, e.courseid
FROM {user_enrolments} ue
JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'cohort' AND e.status = :statusenabled $onecourse)
JOIN {role} r ON (r.id = e.roleid)
JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :coursecontext)
JOIN {user} u ON (u.id = ue.userid AND u.deleted = 0)
LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.userid = ue.userid AND ra.itemid = e.id AND ra.component = 'enrol_cohort' AND e.roleid = ra.roleid)
WHERE ue.status = :useractive AND ra.id IS NULL";
$params = array();
$params['statusenabled'] = ENROL_INSTANCE_ENABLED;
$params['useractive'] = ENROL_USER_ACTIVE;
$params['coursecontext'] = CONTEXT_COURSE;
$params['courseid'] = $courseid;
$rs = $DB->get_recordset_sql($sql, $params);
foreach($rs as $ra) {
role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_cohort', $ra->itemid);
$trace->output("assigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname, 1);
}
$rs->close();
if ($unenrolaction != ENROL_EXT_REMOVED_SUSPEND) {
// Remove unwanted roles - sync role can not be changed, we only remove role when unenrolled.
$onecourse = $courseid ? "AND e.courseid = :courseid" : "";
$sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid, e.courseid
FROM {role_assignments} ra
JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = :coursecontext)
JOIN {enrol} e ON (e.id = ra.itemid AND e.enrol = 'cohort' $onecourse)
LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = ra.userid AND ue.status = :useractive)
WHERE ra.component = 'enrol_cohort' AND (ue.id IS NULL OR e.status <> :statusenabled)";
$params = array();
$params['statusenabled'] = ENROL_INSTANCE_ENABLED;
$params['useractive'] = ENROL_USER_ACTIVE;
$params['coursecontext'] = CONTEXT_COURSE;
$params['courseid'] = $courseid;
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $ra) {
role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_cohort', $ra->itemid);
$trace->output("unassigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname, 1);
}
$rs->close();
}
// Finally sync groups.
$affectedusers = groups_sync_with_enrolment('cohort', $courseid);
foreach ($affectedusers['removed'] as $gm) {
$trace->output("removing user from group: $gm->userid ==> $gm->courseid - $gm->groupname", 1);
}
foreach ($affectedusers['added'] as $ue) {
$trace->output("adding user to group: $ue->userid ==> $ue->courseid - $ue->groupname", 1);
}
$trace->output('...user enrolment synchronisation finished.');
return 0;
}
+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/>.
/**
* Cohort enrolment plugin settings and presets.
*
* @package enrol_cohort
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
if ($ADMIN->fulltree) {
//--- general settings -----------------------------------------------------------------------------------
$settings->add(new admin_setting_heading('enrol_cohort_settings', '', get_string('pluginname_desc', 'enrol_cohort')));
//--- enrol instance defaults ----------------------------------------------------------------------------
if (!during_initial_install()) {
$options = get_default_enrol_roles(context_system::instance());
$student = get_archetype_roles('student');
$student = reset($student);
$settings->add(new admin_setting_configselect('enrol_cohort/roleid',
get_string('defaultrole', 'role'), '', $student->id ?? null, $options));
$options = array(
ENROL_EXT_REMOVED_UNENROL => get_string('extremovedunenrol', 'enrol'),
ENROL_EXT_REMOVED_SUSPEND => get_string('extremovedsuspend', 'enrol'),
ENROL_EXT_REMOVED_SUSPENDNOROLES => get_string('extremovedsuspendnoroles', 'enrol'));
$settings->add(new admin_setting_configselect('enrol_cohort/unenrolaction', get_string('extremovedaction', 'enrol'), get_string('extremovedaction_help', 'enrol'), ENROL_EXT_REMOVED_UNENROL, $options));
}
}
@@ -0,0 +1,112 @@
@enrol @enrol_cohort
Feature: Cohort enrolment management
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher001 | Teacher | 001 | teacher001@example.com |
And the following "cohorts" exist:
| name | idnumber | visible |
| Alpha1 | A1 | 1 |
| Beta2 | B1 | 1 |
And the following "courses" exist:
| fullname | shortname | format | startdate |
| Course 001 | C001 | weeks | ##1 month ago## |
And the following "course enrolments" exist:
| user | course | role | timestart |
| teacher001 | C001 | editingteacher | ##1 month ago## |
@javascript
Scenario: Add multiple cohorts to the course
When I log in as "teacher001"
And I am on the "Course 001" "enrolment methods" page
And I select "Cohort sync" from the "Add method" singleselect
And I open the autocomplete suggestions list
And I click on "Alpha1" item in the autocomplete list
And "Alpha1" "autocomplete_selection" should exist
And I click on "Beta2" item in the autocomplete list
And "Alpha1" "autocomplete_selection" should exist
And "Beta2" "autocomplete_selection" should exist
And I press "Add method"
Then I should see "Cohort sync (Beta2 - Student)"
And I should see "Cohort sync (Alpha1 - Student)"
@javascript
Scenario: Edit cohort enrolment
When I log in as "teacher001"
And I add "Cohort sync" enrolment method in "Course 001" with:
| Cohort | Alpha1 |
And I should see "Cohort sync (Alpha1 - Student)"
And I click on "Edit" "link" in the "Alpha1" "table_row"
And I set the field "Assign role" to "Non-editing teacher"
And I click on "Save" "button"
And I should see "Cohort sync (Alpha1 - Non-editing teacher)"
@javascript
Scenario: Course cohort enrolment sync cohorts members
Given the following "users" exist:
| username | firstname | lastname | email |
| s1 | Sandra | Cole | s1@example.com |
| s2 | John | Smith | s2@example.com |
| s4 | Jane | Doe | s4@example.com |
And the following "cohort members" exist:
| user | cohort |
| s1 | A1 |
| s2 | A1 |
When I log in as "teacher001"
And I add "Cohort sync" enrolment method in "Course 001" with:
| Cohort | A1 |
| customint2 | -1 |
Then I should see "Cohort sync (Alpha1 - Student)"
And I set the field "Participants tertiary navigation" to "Groups"
# Confirm that group was created and corresponding group members are present
And I set the field "groups[]" to "Alpha1 cohort (2)"
And the "members" select box should contain "Sandra Cole (s1@example.com)"
And the "members" select box should contain "John Smith (s2@example.com)"
And I log in as "admin"
And I navigate to "Users > Accounts > Cohorts" in site administration
And I press "Assign" action in the "Alpha1" report row
And I should see "Cohort 'Alpha1' members"
And I should see "Removing users from a cohort may result in unenrolling of users from multiple courses which includes deleting of user settings, grades, group membership and other user information from affected courses."
# Remove user s4 from cohort
And I set the field "removeselect[]" to "John Smith (s2@example.com)"
And I click on "Remove" "button"
# Add user s4 to the cohort.
And I set the field "addselect_searchtext" to "s4"
And I set the field "addselect[]" to "Jane Doe (s4@example.com)"
And I click on "Add" "button"
And the "removeselect[]" select box should contain "Sandra Cole (s1@example.com)"
And the "removeselect[]" select box should contain "Jane Doe (s4@example.com)"
And the "removeselect[]" select box should not contain "John Smith (s2@example.com)"
And I trigger cron
And I am on "Course 001" course homepage
And I navigate to course participants
# Verifies students 1 and 4 are in the cohort and student 2 is not any more.
And the following should exist in the "participants" table:
| First name | Email address | Roles | Groups |
| Sandra Cole | s1@example.com | Student | Alpha1 cohort |
| Jane Doe | s4@example.com | Student | Alpha1 cohort |
And the following should not exist in the "participants" table:
| First name | Email address | Roles | Groups |
| John Smith | s2@example.com | Student | Alpha1 cohort |
@javascript
Scenario: Course cohort enrolment creates a new group
Given the following "users" exist:
| username | firstname | lastname | email |
| s3 | Bianca | McAfee | s3@example.com |
| s5 | Abigail | Wyatt | s5@example.com |
And the following "cohort members" exist:
| user | cohort |
| s3 | B1 |
| s5 | B1 |
When I log in as "teacher001"
And I add "Cohort sync" enrolment method in "Course 001" with:
| Cohort | B1 |
And I click on "Edit" "link" in the "Beta2" "table_row"
And I set the field "Add to group" to "Create new group"
And I click on "Save changes" "button"
And I set the field "Participants tertiary navigation" to "Groups"
And I set the field "groups[]" to "Beta2 cohort (2)"
Then the "members" select box should contain "Bianca McAfee (s3@example.com)"
And the "members" select box should contain "Abigail Wyatt (s5@example.com)"
@@ -0,0 +1,87 @@
@enrol @enrol_cohort
Feature: Unenrol action to disable course enrolment
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher001 | Teacher | 001 | teacher001@example.com |
| student001 | Student | 001 | student001@example.com |
| student002 | Student | 002 | student002@example.com |
| student003 | Student | 003 | student003@example.com |
| student004 | Student | 004 | student004@example.com |
And the following "cohorts" exist:
| name | idnumber | visible |
| System cohort | CVO | 1 |
And the following "cohort members" exist:
| user | cohort |
| student001 | CVO |
| student002 | CVO |
| student003 | CVO |
| student004 | CVO |
And the following "courses" exist:
| fullname | shortname | format | startdate |
| Course 001 | C001 | weeks | ##1 month ago## |
And the following "course enrolments" exist:
| user | course | role | timestart |
| teacher001 | C001 | editingteacher | ##1 month ago## |
@javascript @skip_chrome_zerosize
Scenario: Removing the user from the cohort will suspend the enrolment but keep the role
When I log in as "teacher001"
And I am on the "Course 001" "enrolment methods" page
And I select "Cohort sync" from the "Add method" singleselect
And I open the autocomplete suggestions list
Then "System cohort" "autocomplete_suggestions" should exist
And I set the field "Cohort" to "System cohort"
And I press "Add method"
And I am on the "Course 001" "enrolled users" page
And I should see "student001@example.com"
And I should see "student002@example.com"
And I should see "student003@example.com"
And I should see "student004@example.com"
And I log out
When I log in as "admin"
Then I navigate to "Plugins > Enrolments > Cohort sync" in site administration
And I select "Disable course enrolment" from the "External unenrol action" singleselect
And I press "Save changes"
And I navigate to "Users > Accounts > Cohorts" in site administration
When I press "Assign" action in the "System cohort" report row
And I set the field "removeselect_searchtext" to "Student 001"
And I set the field "Current users" to "Student 001 (student001@example.com)"
And I wait "1" seconds
And I press "Remove"
And I am on "Course 001" course homepage
And I navigate to course participants
And I should see "Suspended" in the "Student 001" "table_row"
And I should see "Active" in the "Student 002" "table_row"
And I should see "Active" in the "Student 003" "table_row"
And I should see "Active" in the "Student 004" "table_row"
@javascript @skip_chrome_zerosize
Scenario: Deleting non-empty cohort will suspend the enrolment but keep the role
When I log in as "teacher001"
And I am on the "Course 001" "enrolment methods" page
And I select "Cohort sync" from the "Add method" singleselect
And I open the autocomplete suggestions list
Then "System cohort" "autocomplete_suggestions" should exist
And I set the field "Cohort" to "System cohort"
And I press "Add method"
And I am on the "Course 001" "enrolled users" page
And I should see "student001@example.com"
And I should see "student002@example.com"
And I should see "student003@example.com"
And I should see "student004@example.com"
And I log out
When I log in as "admin"
Then I navigate to "Plugins > Enrolments > Cohort sync" in site administration
And I select "Disable course enrolment" from the "External unenrol action" singleselect
And I press "Save changes"
And I navigate to "Users > Accounts > Cohorts" in site administration
When I press "Delete" action in the "System cohort" report row
And I press "Continue"
And I am on "Course 001" course homepage
And I navigate to course participants
And I should see "Suspended" in the "Student 001" "table_row"
And I should see "Suspended" in the "Student 002" "table_row"
And I should see "Suspended" in the "Student 003" "table_row"
And I should see "Suspended" in the "Student 004" "table_row"
+481
View File
@@ -0,0 +1,481 @@
<?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 enrol_cohort;
use core\plugininfo\enrol;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot.'/cohort/lib.php');
require_once($CFG->dirroot.'/group/lib.php');
/**
* Contains tests for the cohort library.
*
* @package enrol_cohort
* @category test
* @copyright 2015 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class lib_test extends \advanced_testcase {
/**
* Test that a new group with the name of the cohort is created.
*/
public function test_enrol_cohort_create_new_group(): void {
global $DB;
$this->resetAfterTest();
// Create a category.
$category = $this->getDataGenerator()->create_category();
// Create two courses.
$course = $this->getDataGenerator()->create_course(array('category' => $category->id));
$course2 = $this->getDataGenerator()->create_course(array('category' => $category->id));
// Create a cohort.
$cohort = $this->getDataGenerator()->create_cohort(array('context' => \context_coursecat::instance($category->id)->id));
// Run the function.
$groupid = enrol_cohort_create_new_group($course->id, $cohort->id);
// Check the results.
$group = $DB->get_record('groups', array('id' => $groupid));
// The group name should match the cohort name.
$this->assertEquals($cohort->name . ' cohort', $group->name);
// Group course id should match the course id.
$this->assertEquals($course->id, $group->courseid);
// Create a group that will have the same name as the cohort.
$groupdata = new \stdClass();
$groupdata->courseid = $course2->id;
$groupdata->name = $cohort->name . ' cohort';
groups_create_group($groupdata);
// Create a group for the cohort in course 2.
$groupid = enrol_cohort_create_new_group($course2->id, $cohort->id);
$groupinfo = $DB->get_record('groups', array('id' => $groupid));
// Check that the group name has been changed.
$this->assertEquals($cohort->name . ' cohort (2)', $groupinfo->name);
// Create another group that will have the same name as a generated cohort.
$groupdata = new \stdClass();
$groupdata->courseid = $course2->id;
$groupdata->name = $cohort->name . ' cohort (2)';
groups_create_group($groupdata);
// Create a group for the cohort in course 2.
$groupid = enrol_cohort_create_new_group($course2->id, $cohort->id);
$groupinfo = $DB->get_record('groups', array('id' => $groupid));
// Check that the group name has been changed.
$this->assertEquals($cohort->name . ' cohort (3)', $groupinfo->name);
}
/**
* Test for getting user enrolment actions.
*/
public function test_get_user_enrolment_actions(): void {
global $CFG, $PAGE;
$this->resetAfterTest();
// Set page URL to prevent debugging messages.
$PAGE->set_url('/enrol/editinstance.php');
$pluginname = 'cohort';
// Only enable the cohort enrol plugin.
$CFG->enrol_plugins_enabled = $pluginname;
$generator = $this->getDataGenerator();
// Get the enrol plugin.
$plugin = enrol_get_plugin($pluginname);
// Create a course.
$course = $generator->create_course();
// Enable this enrol plugin for the course.
$plugin->add_instance($course);
// Create a student.
$student = $generator->create_user();
// Enrol the student to the course.
$generator->enrol_user($student->id, $course->id, 'student', $pluginname);
// Teachers don't have enrol/cohort:unenrol capability by default. Login as admin for simplicity.
$this->setAdminUser();
require_once($CFG->dirroot . '/enrol/locallib.php');
$manager = new \course_enrolment_manager($PAGE, $course);
$userenrolments = $manager->get_user_enrolments($student->id);
$this->assertCount(1, $userenrolments);
$ue = reset($userenrolments);
$actions = $plugin->get_user_enrolment_actions($manager, $ue);
// Cohort-sync has no enrol actions for active students.
$this->assertCount(0, $actions);
// Enrol actions for a suspended student.
// Suspend the student.
$ue->status = ENROL_USER_SUSPENDED;
$actions = $plugin->get_user_enrolment_actions($manager, $ue);
// Cohort-sync has enrol actions for suspended students -- unenrol.
$this->assertCount(1, $actions);
}
public function test_enrol_cohort_unenrolaction_suspend_only(): void {
global $CFG, $DB, $PAGE;
$this->resetAfterTest();
$trace = new \null_progress_trace();
$cohortplugin = enrol_get_plugin('cohort');
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND);
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$this->assertNotEmpty($studentrole);
// Setup a test course.
$course = $this->getDataGenerator()->create_course();
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$cohort = $this->getDataGenerator()->create_cohort();
$cohortplugin->add_instance($course, ['customint1' => $cohort->id,
'roleid' => $studentrole->id]
);
cohort_add_member($cohort->id, $user1->id);
cohort_add_member($cohort->id, $user2->id);
cohort_add_member($cohort->id, $user3->id);
cohort_add_member($cohort->id, $user4->id);
// Test sync.
enrol_cohort_sync($trace, $course->id);
// All users should be enrolled.
$this->assertTrue(is_enrolled(\context_course::instance($course->id), $user1));
$this->assertTrue(is_enrolled(\context_course::instance($course->id), $user2));
$this->assertTrue(is_enrolled(\context_course::instance($course->id), $user3));
$this->assertTrue(is_enrolled(\context_course::instance($course->id), $user4));
// Remove cohort member.
cohort_remove_member($cohort->id, $user1->id);
$this->assertTrue(is_enrolled(\context_course::instance($course->id), $user1));
// Run the sync again.
enrol_cohort_sync($trace, $course->id);
$enrolid = $DB->get_field('enrol', 'id', ['enrol' => 'cohort', 'customint1' => $cohort->id]);
$ue = $DB->get_record('user_enrolments', ['enrolid' => $enrolid, 'userid' => $user1->id]);
// Check user is suspended.
$this->assertEquals($ue->status, ENROL_USER_SUSPENDED);
// Check that user4 still have student role.
$userrole = $DB->get_record('role_assignments', ['userid' => $user1->id]);
$this->assertNotEmpty($userrole);
$this->assertEquals($studentrole->id, $userrole->roleid);
// Delete the cohort.
cohort_delete_cohort($cohort);
// Run the sync again.
enrol_cohort_sync($trace, $course->id);
$ue = $DB->get_records('user_enrolments', ['enrolid' => $enrolid], '', 'userid, status, enrolid');
// Check users are suspended.
$this->assertEquals($ue[$user2->id]->status, ENROL_USER_SUSPENDED);
$this->assertEquals($ue[$user3->id]->status, ENROL_USER_SUSPENDED);
$this->assertEquals($ue[$user4->id]->status, ENROL_USER_SUSPENDED);
// Check that users still have student role.
$usersrole = $DB->get_records('role_assignments', ['itemid' => $enrolid], '', 'userid, roleid');
$this->assertNotEmpty($usersrole);
$this->assertEquals($studentrole->id, $usersrole[$user2->id]->roleid);
$this->assertEquals($studentrole->id, $usersrole[$user3->id]->roleid);
$this->assertEquals($studentrole->id, $usersrole[$user4->id]->roleid);
}
/**
* Test the behaviour of validate_plugin_data_context().
*
* @covers ::validate_plugin_data_context
*/
public function test_validate_plugin_data_context(): void {
$this->resetAfterTest();
$cohortplugin = enrol_get_plugin('cohort');
$cat = $this->getDataGenerator()->create_category();
$cat1 = $this->getDataGenerator()->create_category(['parent' => $cat->id]);
$cat2 = $this->getDataGenerator()->create_category(['parent' => $cat->id]);
$course = $this->getDataGenerator()->create_course(['category' => $cat1->id, 'shortname' => 'ANON']);
$cohort1 = $this->getDataGenerator()->create_cohort([
'contextid' => \context_coursecat::instance($cat1->id)->id,
'idnumber' => 'one',
]);
$cohort2 = $this->getDataGenerator()->create_cohort([
'contextid' => \context_coursecat::instance($cat2->id)->id,
'idnumber' => 'two',
]);
$enrolmentdata = [
'customint1' => $cohort2->id,
'cohortidnumber' => $cohort2->idnumber,
];
$error = $cohortplugin->validate_plugin_data_context($enrolmentdata, $course->id);
$this->assertInstanceOf('lang_string', $error);
$this->assertEquals('contextcohortnotallowed', $error->get_identifier());
$enrolmentdata = [
'customint1' => $cohort1->id,
'cohortidnumber' => $cohort1->idnumber,
'courseid' => $course->id,
'id' => null,
'status' => ENROL_INSTANCE_ENABLED,
];
$enrolmentdata = $cohortplugin->fill_enrol_custom_fields($enrolmentdata, $course->id);
$error = $cohortplugin->validate_plugin_data_context($enrolmentdata, $course->id);
$this->assertNull($error);
}
/**
* Test the behaviour of fill_enrol_custom_fields().
*
* @covers ::fill_enrol_custom_fields
*/
public function test_fill_enrol_custom_fields(): void {
$this->resetAfterTest();
$cohortplugin = enrol_get_plugin('cohort');
$cat = $this->getDataGenerator()->create_category();
$course = $this->getDataGenerator()->create_course(['category' => $cat->id, 'shortname' => 'ANON']);
$cohort = $this->getDataGenerator()->create_cohort([
'contextid' => \context_coursecat::instance($cat->id)->id,
'idnumber' => 'one',
]);
$group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
$enrolmentdata['cohortidnumber'] = $cohort->idnumber;
$enrolmentdata = $cohortplugin->fill_enrol_custom_fields($enrolmentdata, $course->id);
$this->assertArrayHasKey('customint1', $enrolmentdata);
$this->assertEquals($cohort->id, $enrolmentdata['customint1']);
$this->assertArrayNotHasKey('customint2', $enrolmentdata);
$enrolmentdata['cohortidnumber'] = 'notexist';
$enrolmentdata = $cohortplugin->fill_enrol_custom_fields($enrolmentdata, $course->id);
$this->assertArrayHasKey('customint1', $enrolmentdata);
$this->assertFalse($enrolmentdata['customint1']);
$this->assertArrayNotHasKey('customint2', $enrolmentdata);
$enrolmentdata['cohortidnumber'] = $cohort->idnumber;
$enrolmentdata['addtogroup'] = COHORT_NOGROUP;
$enrolmentdata = $cohortplugin->fill_enrol_custom_fields($enrolmentdata, $course->id);
$this->assertArrayHasKey('customint1', $enrolmentdata);
$this->assertEquals($cohort->id, $enrolmentdata['customint1']);
$this->assertArrayHasKey('customint2', $enrolmentdata);
$this->assertEquals(COHORT_NOGROUP, $enrolmentdata['customint2']);
unset($enrolmentdata['addtogroup']);
$enrolmentdata['groupname'] = $group->name;
$enrolmentdata = $cohortplugin->fill_enrol_custom_fields($enrolmentdata, $course->id);
$this->assertArrayHasKey('customint1', $enrolmentdata);
$this->assertEquals($cohort->id, $enrolmentdata['customint1']);
$this->assertArrayHasKey('customint2', $enrolmentdata);
$this->assertEquals($group->id, $enrolmentdata['customint2']);
$enrolmentdata['groupname'] = 'notexist';
$enrolmentdata = $cohortplugin->fill_enrol_custom_fields($enrolmentdata, $course->id);
$this->assertArrayHasKey('customint1', $enrolmentdata);
$this->assertEquals($cohort->id, $enrolmentdata['customint1']);
$this->assertArrayHasKey('customint2', $enrolmentdata);
$this->assertFalse($enrolmentdata['customint2']);
}
/**
* Test the behaviour of validate_enrol_plugin_data().
*
* @covers ::validate_enrol_plugin_data
*/
public function test_validate_enrol_plugin_data(): void {
$this->resetAfterTest();
$this->setAdminUser();
$cat = $this->getDataGenerator()->create_category();
$cat1 = $this->getDataGenerator()->create_category(['parent' => $cat->id]);
$cat2 = $this->getDataGenerator()->create_category(['parent' => $cat->id]);
$course = $this->getDataGenerator()->create_course(['category' => $cat1->id, 'shortname' => 'ANON']);
$group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'name' => 'Group 1']);
$cohort1 = $this->getDataGenerator()->create_cohort([
'contextid' => \context_coursecat::instance($cat1->id)->id,
'idnumber' => 'one',
]);
$cohort2 = $this->getDataGenerator()->create_cohort([
'contextid' => \context_coursecat::instance($cat2->id)->id,
'idnumber' => 'two',
]);
enrol::enable_plugin('cohort', false);
$cohortplugin = enrol_get_plugin('cohort');
// Plugin is disabled in system and cohort name is missing in csv.
$enrolmentdata = [];
$errors = $cohortplugin->validate_enrol_plugin_data($enrolmentdata);
$this->assertArrayHasKey('plugindisabled', $errors);
$this->assertArrayHasKey('missingmandatoryfields', $errors);
enrol::enable_plugin('cohort', true);
// Unknown cohort idnumber and missing role.
$enrolmentdata['cohortidnumber'] = 'test';
$errors = $cohortplugin->validate_enrol_plugin_data($enrolmentdata);
$this->assertArrayHasKey('missingmandatoryfields', $errors);
$this->assertArrayHasKey('unknowncohort', $errors);
// Non-valid 'addtogroup' option.
$enrolmentdata['cohortidnumber'] = $cohort1->idnumber;
$enrolmentdata['addtogroup'] = 2;
$errors = $cohortplugin->validate_enrol_plugin_data($enrolmentdata, $course->id);
$this->assertArrayHasKey('erroraddtogroup', $errors);
// Options 'addtogroup' and 'groupname' are not allowed together.
$enrolmentdata['addtogroup'] = 0;
$enrolmentdata['groupname'] = 'test';
$errors = $cohortplugin->validate_enrol_plugin_data($enrolmentdata, $course->id);
$this->assertArrayHasKey('erroraddtogroupgroupname', $errors);
// Cohort is not allowed on a given category context.
$enrolmentdata['cohortidnumber'] = $cohort2->idnumber;
$errors = $cohortplugin->validate_enrol_plugin_data($enrolmentdata, $course->id);
$this->assertArrayHasKey('contextnotallowed', $errors);
// Group does not exist.
unset($enrolmentdata['addtogroup']);
$errors = $cohortplugin->validate_enrol_plugin_data($enrolmentdata, $course->id);
$this->assertArrayHasKey('errorinvalidgroup', $errors);
// Unknown role.
$enrolmentdata['role'] = 'test';
$errors = $cohortplugin->validate_enrol_plugin_data($enrolmentdata, $course->id);
$this->assertArrayHasKey('unknownrole', $errors);
// Valid data when trying to create a group.
$enrolmentdata['cohortidnumber'] = $cohort1->idnumber;
$enrolmentdata['role'] = 'student';
$enrolmentdata['addtogroup'] = 1;
unset($enrolmentdata['groupname']);
$errors = $cohortplugin->validate_enrol_plugin_data($enrolmentdata, $course->id);
$this->assertEmpty($errors);
// Valid data when trying to add to existing group.
$enrolmentdata['groupname'] = $group1->name;
unset($enrolmentdata['addtogroup']);
$errors = $cohortplugin->validate_enrol_plugin_data($enrolmentdata, $course->id);
$this->assertEmpty($errors);
// Valid data when trying without group mode.
$enrolmentdata['addtogroup'] = 0;
unset($enrolmentdata['groupname']);
$errors = $cohortplugin->validate_enrol_plugin_data($enrolmentdata, $course->id);
$this->assertEmpty($errors);
}
/**
* Test the behaviour of find_instance().
*
* @covers ::find_instance
*/
public function test_find_instance(): void {
global $DB;
$this->resetAfterTest();
$cat = $this->getDataGenerator()->create_category();
$course = $this->getDataGenerator()->create_course(['category' => $cat->id, 'shortname' => 'ANON']);
$cohort1 = $this->getDataGenerator()->create_cohort([
'contextid' => \context_coursecat::instance($cat->id)->id,
'idnumber' => 'one',
]);
$cohort2 = $this->getDataGenerator()->create_cohort([
'contextid' => \context_coursecat::instance($cat->id)->id,
'idnumber' => 'two',
]);
$cohort3 = $this->getDataGenerator()->create_cohort([
'contextid' => \context_coursecat::instance($cat->id)->id,
'idnumber' => 'three',
]);
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
$teacherrole = $DB->get_record('role', ['shortname' => 'teacher']);
$managerrole = $DB->get_record('role', ['shortname' => 'manager']);
$cohortplugin = enrol_get_plugin('cohort');
// Add three cohort enrol instances.
$instanceid1 = $cohortplugin->add_instance($course, ['customint1' => $cohort1->id, 'roleid' => $teacherrole->id]);
$instanceid2 = $cohortplugin->add_instance($course, ['customint1' => $cohort2->id, 'roleid' => $managerrole->id]);
$instanceid3 = $cohortplugin->add_instance($course, ['customint1' => $cohort2->id, 'roleid' => $studentrole->id]);
$instance1 = $DB->get_record('enrol', ['id' => $instanceid1]);
$instance2 = $DB->get_record('enrol', ['id' => $instanceid2]);
$enrolmentdata = [];
$instance = $cohortplugin->find_instance($enrolmentdata, $course->id);
$this->assertNull($instance);
// Unknown idnumber.
$enrolmentdata['cohortidnumber'] = 'test';
$instance = $cohortplugin->find_instance($enrolmentdata, $course->id);
$this->assertNull($instance);
// Unknown role.
$enrolmentdata['role'] = 'test';
$enrolmentdata['cohortidnumber'] = $cohort1->idnumber;
$instance = $cohortplugin->find_instance($enrolmentdata, $course->id);
$this->assertNull($instance);
// Cohort3 instance has not matching role and cohort.
$enrolmentdata['role'] = $teacherrole->shortname;
$enrolmentdata['cohortidnumber'] = $cohort3->idnumber;
$instance = $cohortplugin->find_instance($enrolmentdata, $course->id);
$this->assertNull($instance);
// Cohort2 instance has matching cohort, but not matching role.
$enrolmentdata['role'] = $teacherrole->shortname;
$enrolmentdata['cohortidnumber'] = $cohort2->idnumber;
$instance = $cohortplugin->find_instance($enrolmentdata, $course->id);
$this->assertNull($instance);
$enrolmentdata['role'] = $teacherrole->shortname;
$enrolmentdata['cohortidnumber'] = $cohort1->idnumber;
$instance = $cohortplugin->find_instance($enrolmentdata, $course->id);
$this->assertEquals($instance1->id, $instance->id);
$enrolmentdata['role'] = $managerrole->shortname;
$enrolmentdata['cohortidnumber'] = $cohort2->idnumber;
$instance = $cohortplugin->find_instance($enrolmentdata, $course->id);
$this->assertEquals($instance2->id, $instance->id);
}
}
@@ -0,0 +1,395 @@
<?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/>.
/**
* Base class for unit tests for enrol_cohort.
*
* @package enrol_cohort
* @category test
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_cohort\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\request\writer;
use core_privacy\local\request\approved_contextlist;
use enrol_cohort\privacy\provider;
/**
* Unit tests for the enrol_cohort implementation of the privacy API.
*
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider_test extends \core_privacy\tests\provider_testcase {
/**
* Test getting the context for the user ID related to this plugin.
*/
public function test_get_contexts_for_userid(): void {
global $DB;
$this->resetAfterTest();
$trace = new \null_progress_trace();
$cohortplugin = enrol_get_plugin('cohort');
$user1 = $this->getDataGenerator()->create_user();
$cat1 = $this->getDataGenerator()->create_category();
$course1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id));
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id));
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$cohort1 = $this->getDataGenerator()->create_cohort(
array('contextid' => \context_coursecat::instance($cat1->id)->id));
$cohortplugin->add_instance($course1, array(
'customint1' => $cohort1->id,
'roleid' => $studentrole->id,
'customint2' => $group1->id)
);
cohort_add_member($cohort1->id, $user1->id);
enrol_cohort_sync($trace, $course1->id);
// Check if user1 is enrolled into course1 in group 1.
$this->assertEquals(1, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('groups_members', array(
'groupid' => $group1->id,
'userid' => $user1->id,
'component' => 'enrol_cohort')
));
// Check context course fro provider to user1.
$context = \context_course::instance($course1->id);
$contextlist = provider::get_contexts_for_userid($user1->id);
$this->assertEquals($context->id, $contextlist->current()->id);
}
/**
* Test that user data is exported correctly.
*/
public function test_export_user_data(): void {
global $DB;
$this->resetAfterTest();
$trace = new \null_progress_trace();
$cohortplugin = enrol_get_plugin('cohort');
$user1 = $this->getDataGenerator()->create_user();
$cat1 = $this->getDataGenerator()->create_category();
$course1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id));
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id));
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$cohort1 = $this->getDataGenerator()->create_cohort(
array('contextid' => \context_coursecat::instance($cat1->id)->id));
$cohortplugin->add_instance($course1, array(
'customint1' => $cohort1->id,
'roleid' => $studentrole->id,
'customint2' => $group1->id)
);
cohort_add_member($cohort1->id, $user1->id);
enrol_cohort_sync($trace, $course1->id);
// Check if user1 is enrolled into course1 in group 1.
$this->assertEquals(1, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('groups_members', array(
'groupid' => $group1->id,
'userid' => $user1->id,
'component' => 'enrol_cohort')
));
$this->setUser($user1);
$contextlist = provider::get_contexts_for_userid($user1->id);
$approvedcontextlist = new approved_contextlist($user1, 'enrol_cohort', $contextlist->get_contextids());
provider::export_user_data($approvedcontextlist);
foreach ($contextlist as $context) {
/** @var \core_privacy\tests\request\content_writer $writer */
$writer = writer::with_context($context);
$data = $writer->get_data([
get_string('pluginname', 'enrol_cohort'),
get_string('groups', 'core_group')
]);
$this->assertTrue($writer->has_any_data());
if ($context->contextlevel == CONTEXT_COURSE) {
$exportedgroups = $data->groups;
// User1 only belongs to group1 via enrol_cohort.
$this->assertCount(1, $exportedgroups);
$exportedgroup = reset($exportedgroups);
$this->assertEquals($group1->name, $exportedgroup->name);
}
}
}
/**
* Test for provider::delete_data_for_all_users_in_context().
*/
public function test_delete_data_for_all_users_in_context(): void {
global $DB;
$this->resetAfterTest();
$trace = new \null_progress_trace();
$cohortplugin = enrol_get_plugin('cohort');
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$cat1 = $this->getDataGenerator()->create_category();
$course1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id));
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id));
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$cohort1 = $this->getDataGenerator()->create_cohort(
array('contextid' => \context_coursecat::instance($cat1->id)->id));
$cohortplugin->add_instance($course1, array(
'customint1' => $cohort1->id,
'roleid' => $studentrole->id,
'customint2' => $group1->id)
);
cohort_add_member($cohort1->id, $user1->id);
cohort_add_member($cohort1->id, $user2->id);
cohort_add_member($cohort1->id, $user3->id);
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(
3,
$DB->count_records_sql("SELECT COUNT(gm.id)
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = ?", [$course1->id])
);
$coursecontext1 = \context_course::instance($course1->id);
provider::delete_data_for_all_users_in_context($coursecontext1);
$this->assertEquals(
0,
$DB->count_records_sql("SELECT COUNT(gm.id)
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = ?", [$course1->id])
);
}
/**
* Test for provider::delete_data_for_user().
*/
public function test_delete_data_for_user(): void {
global $DB;
$this->resetAfterTest();
$trace = new \null_progress_trace();
$cohortplugin = enrol_get_plugin('cohort');
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$cat1 = $this->getDataGenerator()->create_category();
$course1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id));
$course2 = $this->getDataGenerator()->create_course(array('category' => $cat1->id));
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course2->id));
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$cohort1 = $this->getDataGenerator()->create_cohort(
array('contextid' => \context_coursecat::instance($cat1->id)->id));
$cohortplugin->add_instance($course1, array(
'customint1' => $cohort1->id,
'roleid' => $studentrole->id,
'customint2' => $group1->id)
);
$cohortplugin->add_instance($course2, array(
'customint1' => $cohort1->id,
'roleid' => $studentrole->id,
'customint2' => $group2->id)
);
$this->getDataGenerator()->enrol_user($user2->id, $course1->id);
$this->getDataGenerator()->enrol_user($user3->id, $course1->id);
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user3->id));
cohort_add_member($cohort1->id, $user1->id);
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(
3,
$DB->count_records_sql("SELECT COUNT(gm.id)
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = ?", [$course1->id])
);
$this->assertEquals(
1,
$DB->count_records_sql("SELECT COUNT(gm.id)
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = ?", [$course2->id])
);
$this->setUser($user1);
$coursecontext1 = \context_course::instance($course1->id);
$coursecontext2 = \context_course::instance($course2->id);
$approvedcontextlist = new \core_privacy\tests\request\approved_contextlist($user1, 'enrol_cohort',
[$coursecontext1->id, $coursecontext2->id]);
provider::delete_data_for_user($approvedcontextlist);
// Check we have 2 users in groups because we are deleted user1.
$this->assertEquals(
2,
$DB->count_records_sql("SELECT COUNT(gm.id)
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = ?", [$course1->id])
);
// Check we have not users in groups.
$this->assertEquals(
0,
$DB->count_records_sql("SELECT COUNT(gm.id)
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = ?", [$course2->id])
);
}
/**
* Test for provider::delete_data_for_users().
*/
public function test_delete_data_for_users(): void {
global $DB;
$this->resetAfterTest();
$trace = new \null_progress_trace();
$cohortplugin = enrol_get_plugin('cohort');
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$cat1 = $this->getDataGenerator()->create_category();
$course1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id));
$course2 = $this->getDataGenerator()->create_course(array('category' => $cat1->id));
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course2->id));
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$cohort1 = $this->getDataGenerator()->create_cohort(
array('contextid' => \context_coursecat::instance($cat1->id)->id));
$cohortplugin->add_instance($course1, array(
'customint1' => $cohort1->id,
'roleid' => $studentrole->id,
'customint2' => $group1->id)
);
$cohortplugin->add_instance($course2, array(
'customint1' => $cohort1->id,
'roleid' => $studentrole->id,
'customint2' => $group2->id)
);
$this->getDataGenerator()->enrol_user($user2->id, $course1->id);
$this->getDataGenerator()->enrol_user($user3->id, $course1->id);
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user3->id));
cohort_add_member($cohort1->id, $user1->id);
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(
3,
$DB->count_records_sql("SELECT COUNT(gm.id)
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = ?", [$course1->id])
);
$this->assertEquals(
1,
$DB->count_records_sql("SELECT COUNT(gm.id)
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = ?", [$course2->id])
);
$coursecontext1 = \context_course::instance($course1->id);
$approveduserlist = new \core_privacy\local\request\approved_userlist($coursecontext1, 'enrol_cohort',
[$user1->id, $user2->id]);
provider::delete_data_for_users($approveduserlist);
// Check we have 2 users in groups because we have deleted user1.
// User2's membership is manual and is not as the result of a cohort enrolment.
$this->assertEquals(
2,
$DB->count_records_sql("SELECT COUNT(gm.id)
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = ?", [$course1->id])
);
// Check that course2 is not touched.
$this->assertEquals(
1,
$DB->count_records_sql("SELECT COUNT(gm.id)
FROM {groups_members} gm
JOIN {groups} g ON gm.groupid = g.id
WHERE g.courseid = ?", [$course2->id])
);
}
/**
* Test for provider::get_users_in_context().
*/
public function test_get_users_in_context(): void {
global $DB;
$this->resetAfterTest();
$trace = new \null_progress_trace();
$cohortplugin = enrol_get_plugin('cohort');
$cat1 = $this->getDataGenerator()->create_category();
$course1 = $this->getDataGenerator()->create_course(array('category' => $cat1->id));
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course1->id));
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$cohort1 = $this->getDataGenerator()->create_cohort(
array('contextid' => \context_coursecat::instance($cat1->id)->id));
$cohortplugin->add_instance($course1, array(
'customint1' => $cohort1->id,
'roleid' => $studentrole->id,
'customint2' => $group1->id)
);
$user1 = $this->getDataGenerator()->create_user();
cohort_add_member($cohort1->id, $user1->id);
enrol_cohort_sync($trace, $course1->id);
// Check if user1 is enrolled into course1 in group 1.
$this->assertEquals(1, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('groups_members', array(
'groupid' => $group1->id,
'userid' => $user1->id,
'component' => 'enrol_cohort')
));
$context = \context_course::instance($course1->id);
$userlist = new \core_privacy\local\request\userlist($context, 'enrol_cohort');
\enrol_cohort\privacy\provider::get_users_in_context($userlist);
$this->assertEquals([$user1->id], $userlist->get_userids());
}
}
+749
View File
@@ -0,0 +1,749 @@
<?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 enrol_cohort;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot.'/enrol/cohort/locallib.php');
require_once($CFG->dirroot.'/cohort/lib.php');
require_once($CFG->dirroot.'/group/lib.php');
/**
* Cohort enrolment sync functional test.
*
* @package enrol_cohort
* @category test
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sync_test extends \advanced_testcase {
protected function enable_plugin() {
$enabled = enrol_get_plugins(true);
$enabled['cohort'] = true;
$enabled = array_keys($enabled);
set_config('enrol_plugins_enabled', implode(',', $enabled));
}
protected function disable_plugin() {
$enabled = enrol_get_plugins(true);
unset($enabled['cohort']);
$enabled = array_keys($enabled);
set_config('enrol_plugins_enabled', implode(',', $enabled));
}
public function test_handler_sync(): void {
global $DB;
$this->resetAfterTest();
$trace = new \null_progress_trace();
// Setup a few courses and categories.
$cohortplugin = enrol_get_plugin('cohort');
$manualplugin = enrol_get_plugin('manual');
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$managerrole = $DB->get_record('role', array('shortname'=>'manager'));
$this->assertNotEmpty($managerrole);
$cat1 = $this->getDataGenerator()->create_category();
$cat2 = $this->getDataGenerator()->create_category();
$course1 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course2 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course3 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$course4 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$user5 = $this->getDataGenerator()->create_user();
$cohort1 = $this->getDataGenerator()->create_cohort(array('contextid'=>\context_coursecat::instance($cat1->id)->id));
$cohort2 = $this->getDataGenerator()->create_cohort(array('contextid'=>\context_coursecat::instance($cat2->id)->id));
$cohort3 = $this->getDataGenerator()->create_cohort();
$cohort4 = $this->getDataGenerator()->create_cohort();
$this->enable_plugin();
$manualplugin->enrol_user($maninstance1, $user4->id, $teacherrole->id);
$manualplugin->enrol_user($maninstance1, $user3->id, $managerrole->id);
$this->assertEquals(2, $DB->count_records('role_assignments', array()));
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$id = $cohortplugin->add_instance($course1, array('customint1'=>$cohort1->id, 'roleid'=>$studentrole->id));
$cohortinstance1 = $DB->get_record('enrol', array('id'=>$id));
$id = $cohortplugin->add_instance($course1, array('customint1'=>$cohort2->id, 'roleid'=>$teacherrole->id));
$cohortinstance2 = $DB->get_record('enrol', array('id'=>$id));
$id = $cohortplugin->add_instance($course2, array('customint1'=>$cohort2->id, 'roleid'=>$studentrole->id));
$cohortinstance3 = $DB->get_record('enrol', array('id'=>$id));
$id = $cohortplugin->add_instance($course2, array('customint1' => $cohort2->id, 'roleid' => $studentrole->id, 'status' => ENROL_INSTANCE_DISABLED));
$cohortinstance4 = $DB->get_record('enrol', array('id' => $id));
$id = $cohortplugin->add_instance($course3, array('customint1' => $cohort4->id, 'roleid' => $studentrole->id));
$cohortinstance5 = $DB->get_record('enrol', array('id' => $id));
// Test cohort member add event.
cohort_add_member($cohort1->id, $user1->id);
cohort_add_member($cohort1->id, $user2->id);
cohort_add_member($cohort1->id, $user4->id);
$this->assertEquals(5, $DB->count_records('user_enrolments', array()));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user1->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user2->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user4->id)));
$this->assertEquals(5, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user4->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
cohort_add_member($cohort2->id, $user3->id);
$this->assertEquals(7, $DB->count_records('user_enrolments', array()));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance2->id, 'userid'=>$user3->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance3->id, 'userid'=>$user3->id)));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid' => $cohortinstance4->id, 'userid' => $user3->id)));
$this->assertEquals(7, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance2->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course2->id)->id, 'userid'=>$user3->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance3->id)));
cohort_add_member($cohort3->id, $user3->id);
cohort_add_member($cohort4->id, $user5->id);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertEquals(8, $DB->count_records('role_assignments', array()));
// Test cohort remove action.
$this->assertEquals(ENROL_EXT_REMOVED_UNENROL, $cohortplugin->get_config('unenrolaction'));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
cohort_remove_member($cohort1->id, $user2->id);
cohort_remove_member($cohort1->id, $user4->id);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertEquals(6, $DB->count_records('role_assignments', array()));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user4->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertEquals(ENROL_USER_SUSPENDED, $DB->get_field('user_enrolments', 'status',
array('userid' => $user2->id, 'enrolid' => $cohortinstance1->id)));
$this->assertEquals(ENROL_USER_SUSPENDED, $DB->get_field('user_enrolments', 'status',
array('userid' => $user4->id, 'enrolid' => $cohortinstance1->id)));
cohort_add_member($cohort1->id, $user2->id);
cohort_add_member($cohort1->id, $user4->id);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertEquals(8, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('role_assignments', array(
'contextid' => \context_course::instance($course1->id)->id,
'userid' => $user2->id, 'roleid' => $studentrole->id,
'component' => 'enrol_cohort', 'itemid' => $cohortinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array(
'contextid' => \context_course::instance($course1->id)->id,
'userid' => $user4->id, 'roleid' => $studentrole->id,
'component' => 'enrol_cohort', 'itemid' => $cohortinstance1->id)));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND);
cohort_remove_member($cohort1->id, $user2->id);
cohort_remove_member($cohort1->id, $user4->id);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertEquals(8, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('role_assignments', array(
'contextid' => \context_course::instance($course1->id)->id,
'userid' => $user2->id, 'roleid' => $studentrole->id,
'component' => 'enrol_cohort', 'itemid' => $cohortinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array(
'contextid' => \context_course::instance($course1->id)->id,
'userid' => $user4->id, 'roleid' => $studentrole->id,
'component' => 'enrol_cohort', 'itemid' => $cohortinstance1->id)));
$this->assertEquals(ENROL_USER_SUSPENDED, $DB->get_field('user_enrolments', 'status',
array('userid' => $user2->id, 'enrolid' => $cohortinstance1->id)));
$this->assertEquals(ENROL_USER_SUSPENDED, $DB->get_field('user_enrolments', 'status',
array('userid' => $user4->id, 'enrolid' => $cohortinstance1->id)));
cohort_add_member($cohort1->id, $user2->id);
cohort_add_member($cohort1->id, $user4->id);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertEquals(8, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('role_assignments', array(
'contextid' => \context_course::instance($course1->id)->id,
'userid' => $user2->id, 'roleid' => $studentrole->id,
'component' => 'enrol_cohort', 'itemid' => $cohortinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array(
'contextid' => \context_course::instance($course1->id)->id,
'userid' => $user4->id, 'roleid' => $studentrole->id,
'component' => 'enrol_cohort', 'itemid' => $cohortinstance1->id)));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
cohort_remove_member($cohort1->id, $user2->id);
cohort_remove_member($cohort1->id, $user4->id);
$this->assertEquals(6, $DB->count_records('user_enrolments', array()));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user2->id)));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user4->id)));
$this->assertEquals(6, $DB->count_records('role_assignments', array()));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user4->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
cohort_remove_member($cohort2->id, $user3->id);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance2->id, 'userid'=>$user3->id)));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance3->id, 'userid'=>$user3->id)));
$this->assertEquals(4, $DB->count_records('role_assignments', array()));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance2->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course2->id)->id, 'userid'=>$user3->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance3->id)));
// Test cohort deleting.
cohort_add_member($cohort1->id, $user2->id);
cohort_add_member($cohort1->id, $user4->id);
cohort_add_member($cohort2->id, $user3->id);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertEquals(8, $DB->count_records('role_assignments', array()));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND);
cohort_delete_cohort($cohort4);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertEquals(8, $DB->count_records('role_assignments', array()));
$cohortinstance5 = $DB->get_record('enrol', array('id' => $cohortinstance5->id), '*', MUST_EXIST);
$this->assertEquals(ENROL_INSTANCE_DISABLED, $cohortinstance5->status);
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
cohort_delete_cohort($cohort2);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertEquals(6, $DB->count_records('role_assignments', array()));
$cohortinstance2 = $DB->get_record('enrol', array('id'=>$cohortinstance2->id), '*', MUST_EXIST);
$cohortinstance3 = $DB->get_record('enrol', array('id'=>$cohortinstance3->id), '*', MUST_EXIST);
$this->assertEquals(ENROL_INSTANCE_DISABLED, $cohortinstance2->status);
$this->assertEquals(ENROL_INSTANCE_DISABLED, $cohortinstance3->status);
$this->assertFalse($DB->record_exists('role_assignments', array('component'=>'enrol_cohort', 'itemid'=>$cohortinstance2->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('component'=>'enrol_cohort', 'itemid'=>$cohortinstance3->id)));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
cohort_delete_cohort($cohort1);
$this->assertEquals(5, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('role_assignments', array()));
$this->assertFalse($DB->record_exists('enrol', array('id'=>$cohortinstance1->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
// Cleanup after previous test (remove the extra user_enrolment).
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
// Test group sync.
$id = groups_create_group((object)array('name'=>'Group 1', 'courseid'=>$course1->id));
$group1 = $DB->get_record('groups', array('id'=>$id), '*', MUST_EXIST);
$id = groups_create_group((object)array('name'=>'Group 2', 'courseid'=>$course1->id));
$group2 = $DB->get_record('groups', array('id'=>$id), '*', MUST_EXIST);
$cohort1 = $this->getDataGenerator()->create_cohort(array('contextid'=>\context_coursecat::instance($cat1->id)->id));
$id = $cohortplugin->add_instance($course1, array('customint1'=>$cohort1->id, 'roleid'=>$studentrole->id, 'customint2'=>$group1->id));
$cohortinstance1 = $DB->get_record('enrol', array('id'=>$id));
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('role_assignments', array()));
$this->assertTrue(is_enrolled(\context_course::instance($course1->id), $user4));
$this->assertTrue(groups_add_member($group1, $user4));
$this->assertTrue(groups_add_member($group2, $user4));
$this->assertFalse(groups_is_member($group1->id, $user1->id));
cohort_add_member($cohort1->id, $user1->id);
$this->assertTrue(groups_is_member($group1->id, $user1->id));
$this->assertTrue($DB->record_exists('groups_members', array('groupid'=>$group1->id, 'userid'=>$user1->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
cohort_add_member($cohort1->id, $user4->id);
$this->assertTrue(groups_is_member($group1->id, $user4->id));
$this->assertFalse($DB->record_exists('groups_members', array('groupid'=>$group1->id, 'userid'=>$user4->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
cohort_remove_member($cohort1->id, $user1->id);
$this->assertFalse(groups_is_member($group1->id, $user1->id));
cohort_remove_member($cohort1->id, $user4->id);
$this->assertTrue(groups_is_member($group1->id, $user4->id));
$this->assertTrue(groups_is_member($group2->id, $user4->id));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
cohort_add_member($cohort1->id, $user1->id);
cohort_remove_member($cohort1->id, $user1->id);
$this->assertTrue(groups_is_member($group1->id, $user1->id));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND);
cohort_add_member($cohort1->id, $user1->id);
cohort_remove_member($cohort1->id, $user1->id);
$this->assertTrue(groups_is_member($group1->id, $user1->id));
// Test deleting of instances.
cohort_add_member($cohort1->id, $user1->id);
cohort_add_member($cohort1->id, $user2->id);
cohort_add_member($cohort1->id, $user3->id);
$this->assertEquals(7, $DB->count_records('user_enrolments', array()));
$this->assertEquals(6, $DB->count_records('role_assignments', array()));
$this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertEquals(5, $DB->count_records('groups_members', array()));
$this->assertEquals(3, $DB->count_records('groups_members', array('component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$cohortplugin->delete_instance($cohortinstance1);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('role_assignments', array()));
$this->assertEquals(0, $DB->count_records('role_assignments', array('component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertEquals(2, $DB->count_records('groups_members', array()));
$this->assertEquals(0, $DB->count_records('groups_members', array('component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
}
public function test_sync_course(): void {
global $DB;
$this->resetAfterTest();
$trace = new \null_progress_trace();
// Setup a few courses and categories.
$cohortplugin = enrol_get_plugin('cohort');
$manualplugin = enrol_get_plugin('manual');
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$managerrole = $DB->get_record('role', array('shortname'=>'manager'));
$this->assertNotEmpty($managerrole);
$cat1 = $this->getDataGenerator()->create_category();
$cat2 = $this->getDataGenerator()->create_category();
$course1 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course2 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course3 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$course4 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$user5 = $this->getDataGenerator()->create_user();
$cohort1 = $this->getDataGenerator()->create_cohort(array('contextid'=>\context_coursecat::instance($cat1->id)->id));
$cohort2 = $this->getDataGenerator()->create_cohort(array('contextid'=>\context_coursecat::instance($cat2->id)->id));
$cohort3 = $this->getDataGenerator()->create_cohort();
$cohort4 = $this->getDataGenerator()->create_cohort();
$this->disable_plugin(); // Prevents event sync.
$manualplugin->enrol_user($maninstance1, $user4->id, $teacherrole->id);
$manualplugin->enrol_user($maninstance1, $user3->id, $managerrole->id);
$this->assertEquals(2, $DB->count_records('role_assignments', array()));
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$id = $cohortplugin->add_instance($course1, array('customint1'=>$cohort1->id, 'roleid'=>$studentrole->id));
$cohortinstance1 = $DB->get_record('enrol', array('id'=>$id));
$id = $cohortplugin->add_instance($course1, array('customint1'=>$cohort2->id, 'roleid'=>$teacherrole->id));
$cohortinstance2 = $DB->get_record('enrol', array('id'=>$id));
$id = $cohortplugin->add_instance($course2, array('customint1'=>$cohort2->id, 'roleid'=>$studentrole->id));
$cohortinstance3 = $DB->get_record('enrol', array('id'=>$id));
$id = $cohortplugin->add_instance($course2, array('customint1' => $cohort2->id, 'roleid' => $studentrole->id, 'status' => ENROL_INSTANCE_DISABLED));
$cohortinstance4 = $DB->get_record('enrol', array('id' => $id));
$id = $cohortplugin->add_instance($course3, array('customint1' => $cohort4->id, 'roleid' => $studentrole->id));
$cohortinstance5 = $DB->get_record('enrol', array('id' => $id));
cohort_add_member($cohort1->id, $user1->id);
cohort_add_member($cohort1->id, $user2->id);
cohort_add_member($cohort1->id, $user4->id);
cohort_add_member($cohort2->id, $user3->id);
cohort_add_member($cohort3->id, $user3->id);
cohort_add_member($cohort4->id, $user5->id);
$this->assertEquals(2, $DB->count_records('role_assignments', array()));
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
// Test sync of one course only.
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(2, $DB->count_records('role_assignments', array()));
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->enable_plugin();
enrol_cohort_sync($trace, $course2->id);
$this->assertEquals(3, $DB->count_records('role_assignments', array()));
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$DB->delete_records('cohort_members', array('cohortid'=>$cohort3->id)); // Use low level DB api to prevent events!
$DB->delete_records('cohort', array('id'=>$cohort3->id)); // Use low level DB api to prevent events!
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(7, $DB->count_records('user_enrolments', array()));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user1->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user2->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user4->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance2->id, 'userid'=>$user3->id)));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid' => $cohortinstance4->id, 'userid' => $user3->id)));
$this->assertEquals(7, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user4->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance2->id)));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND);
// Use low level DB api to prevent events!
$DB->delete_records('cohort_members', array('cohortid' => $cohort2->id, 'userid' => $user3->id));
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(7, $DB->count_records('user_enrolments', array()));
$this->assertEquals(7, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('role_assignments', array(
'contextid' => \context_course::instance($course1->id)->id,
'userid' => $user3->id, 'roleid' => $teacherrole->id,
'component' => 'enrol_cohort', 'itemid' => $cohortinstance2->id)));
cohort_add_member($cohort2->id, $user3->id);
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
$DB->delete_records('cohort_members', array('cohortid'=>$cohort2->id, 'userid'=>$user3->id)); // Use low level DB api to prevent events!
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(7, $DB->count_records('user_enrolments', array()));
$this->assertEquals(6, $DB->count_records('role_assignments', array()));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance2->id)));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
$DB->delete_records('cohort_members', array('cohortid'=>$cohort1->id, 'userid'=>$user1->id)); // Use low level DB api to prevent events!
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(5, $DB->count_records('user_enrolments', array()));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance2->id, 'userid'=>$user3->id)));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user1->id)));
$this->assertEquals(5, $DB->count_records('role_assignments', array()));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance2->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND);
$DB->delete_records('cohort_members', array('cohortid' => $cohort4->id));
$DB->delete_records('cohort', array('id' => $cohort4->id));
enrol_cohort_sync($trace, $course3->id);
$this->assertEquals(5, $DB->count_records('user_enrolments', array()));
$this->assertEquals(5, $DB->count_records('role_assignments', array()));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
$DB->delete_records('cohort_members', array('cohortid'=>$cohort1->id)); // Use low level DB api to prevent events!
$DB->delete_records('cohort', array('id'=>$cohort1->id)); // Use low level DB api to prevent events!
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(5, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('role_assignments', array()));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('role_assignments', array()));
// Test group sync.
$this->disable_plugin(); // No event sync.
// Trigger sync to remove left over role assignments.
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(2, $DB->count_records('role_assignments', array()));
$id = groups_create_group((object)array('name'=>'Group 1', 'courseid'=>$course1->id));
$group1 = $DB->get_record('groups', array('id'=>$id), '*', MUST_EXIST);
$id = groups_create_group((object)array('name'=>'Group 2', 'courseid'=>$course1->id));
$group2 = $DB->get_record('groups', array('id'=>$id), '*', MUST_EXIST);
$cohort1 = $this->getDataGenerator()->create_cohort(array('contextid'=>\context_coursecat::instance($cat1->id)->id));
$id = $cohortplugin->add_instance($course1, array('customint1'=>$cohort1->id, 'roleid'=>$studentrole->id, 'customint2'=>$group1->id));
$cohortinstance1 = $DB->get_record('enrol', array('id'=>$id));
$this->assertTrue(is_enrolled(\context_course::instance($course1->id), $user4));
$this->assertTrue(groups_add_member($group1, $user4));
$this->assertTrue(groups_add_member($group2, $user4));
$this->enable_plugin(); // No event sync.
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('role_assignments', array()));
$this->assertFalse(groups_is_member($group1->id, $user1->id));
cohort_add_member($cohort1->id, $user1->id);
cohort_add_member($cohort1->id, $user4->id);
cohort_add_member($cohort2->id, $user4->id);
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(7, $DB->count_records('user_enrolments', array()));
// This used to be 7 - but now add_instance triggers an immediate sync.
$this->assertEquals(6, $DB->count_records('role_assignments', array()));
$this->assertTrue(groups_is_member($group1->id, $user1->id));
$this->assertTrue($DB->record_exists('groups_members', array('groupid'=>$group1->id, 'userid'=>$user1->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue(groups_is_member($group1->id, $user4->id));
$this->assertFalse($DB->record_exists('groups_members', array('groupid'=>$group1->id, 'userid'=>$user4->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$cohortinstance1->customint2 = $group2->id;
$DB->update_record('enrol', $cohortinstance1);
enrol_cohort_sync($trace, $course1->id);
$this->assertFalse(groups_is_member($group1->id, $user1->id));
$this->assertTrue(groups_is_member($group2->id, $user1->id));
$this->assertTrue($DB->record_exists('groups_members', array('groupid'=>$group2->id, 'userid'=>$user1->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue(groups_is_member($group1->id, $user4->id));
$this->assertTrue(groups_is_member($group2->id, $user4->id));
$this->assertFalse($DB->record_exists('groups_members', array('groupid'=>$group1->id, 'userid'=>$user4->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertFalse($DB->record_exists('groups_members', array('groupid'=>$group2->id, 'userid'=>$user4->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
cohort_remove_member($cohort1->id, $user1->id);
$this->assertFalse(groups_is_member($group1->id, $user1->id));
cohort_remove_member($cohort1->id, $user4->id);
$this->assertTrue(groups_is_member($group1->id, $user4->id));
$this->assertTrue(groups_is_member($group2->id, $user4->id));
}
public function test_sync_all_courses(): void {
global $DB;
$this->resetAfterTest();
$trace = new \null_progress_trace();
// Setup a few courses and categories.
$cohortplugin = enrol_get_plugin('cohort');
$manualplugin = enrol_get_plugin('manual');
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$managerrole = $DB->get_record('role', array('shortname'=>'manager'));
$this->assertNotEmpty($managerrole);
$cat1 = $this->getDataGenerator()->create_category();
$cat2 = $this->getDataGenerator()->create_category();
$course1 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course2 = $this->getDataGenerator()->create_course(array('category'=>$cat1->id));
$course3 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$course4 = $this->getDataGenerator()->create_course(array('category'=>$cat2->id));
$maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$user5 = $this->getDataGenerator()->create_user();
$cohort1 = $this->getDataGenerator()->create_cohort(array('contextid'=>\context_coursecat::instance($cat1->id)->id));
$cohort2 = $this->getDataGenerator()->create_cohort(array('contextid'=>\context_coursecat::instance($cat2->id)->id));
$cohort3 = $this->getDataGenerator()->create_cohort();
$cohort4 = $this->getDataGenerator()->create_cohort();
$this->disable_plugin(); // Prevents event sync.
$manualplugin->enrol_user($maninstance1, $user4->id, $teacherrole->id);
$manualplugin->enrol_user($maninstance1, $user3->id, $managerrole->id);
$this->assertEquals(2, $DB->count_records('role_assignments', array()));
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$id = $cohortplugin->add_instance($course1, array('customint1'=>$cohort1->id, 'roleid'=>$studentrole->id));
$cohortinstance1 = $DB->get_record('enrol', array('id'=>$id));
$id = $cohortplugin->add_instance($course1, array('customint1'=>$cohort2->id, 'roleid'=>$teacherrole->id));
$cohortinstance2 = $DB->get_record('enrol', array('id'=>$id));
$id = $cohortplugin->add_instance($course2, array('customint1'=>$cohort2->id, 'roleid'=>$studentrole->id));
$cohortinstance3 = $DB->get_record('enrol', array('id'=>$id));
$id = $cohortplugin->add_instance($course3, array('customint1' => $cohort4->id, 'roleid' => $studentrole->id));
$cohortinstance4 = $DB->get_record('enrol', array('id' => $id));
cohort_add_member($cohort1->id, $user1->id);
cohort_add_member($cohort1->id, $user2->id);
cohort_add_member($cohort1->id, $user4->id);
cohort_add_member($cohort2->id, $user3->id);
cohort_add_member($cohort3->id, $user3->id);
cohort_add_member($cohort4->id, $user5->id);
$this->assertEquals(2, $DB->count_records('role_assignments', array()));
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
// Test sync of one course only.
enrol_cohort_sync($trace, null);
$this->assertEquals(2, $DB->count_records('role_assignments', array()));
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->enable_plugin();
enrol_cohort_sync($trace, null);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user1->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user2->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user4->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance2->id, 'userid'=>$user3->id)));
$this->assertEquals(8, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user4->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance2->id)));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND);
$DB->delete_records('cohort_members', array('cohortid' => $cohort2->id, 'userid' => $user3->id));
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertEquals(8, $DB->count_records('role_assignments', array()));
$this->assertTrue($DB->record_exists('role_assignments', array(
'contextid' => \context_course::instance($course1->id)->id,
'userid' => $user3->id, 'roleid' => $teacherrole->id,
'component' => 'enrol_cohort', 'itemid' => $cohortinstance2->id)));
cohort_add_member($cohort2->id, $user3->id);
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
$DB->delete_records('cohort_members', array('cohortid'=>$cohort2->id, 'userid'=>$user3->id)); // Use low level DB api to prevent events!
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertEquals(7, $DB->count_records('role_assignments', array()));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance2->id)));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
$DB->delete_records('cohort_members', array('cohortid'=>$cohort1->id, 'userid'=>$user1->id)); // Use low level DB api to prevent events!
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(6, $DB->count_records('user_enrolments', array()));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance2->id, 'userid'=>$user3->id)));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user1->id)));
$this->assertEquals(6, $DB->count_records('role_assignments', array()));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance2->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>\context_course::instance($course1->id)->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND);
$DB->delete_records('cohort_members', array('cohortid' => $cohort4->id)); // Use low level DB api to prevent events!
$DB->delete_records('cohort', array('id' => $cohort4->id)); // Use low level DB api to prevent events!
enrol_cohort_sync($trace, $course3->id);
$this->assertEquals(6, $DB->count_records('user_enrolments', array()));
$this->assertEquals(6, $DB->count_records('role_assignments', array()));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
$DB->delete_records('cohort_members', array('cohortid' => $cohort1->id)); // Use low level DB api to prevent events!
$DB->delete_records('cohort', array('id' => $cohort1->id)); // Use low level DB api to prevent events!
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(6, $DB->count_records('user_enrolments', array()));
$this->assertEquals(4, $DB->count_records('role_assignments', array()));
$cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(4, $DB->count_records('role_assignments', array()));
// Test group sync.
$this->disable_plugin(); // No event sync
// Trigger sync to remove extra role assignments.
enrol_cohort_sync($trace, $course1->id);
$this->assertEquals(2, $DB->count_records('role_assignments', array()));
$id = groups_create_group((object)array('name'=>'Group 1', 'courseid'=>$course1->id));
$group1 = $DB->get_record('groups', array('id'=>$id), '*', MUST_EXIST);
$id = groups_create_group((object)array('name'=>'Group 2', 'courseid'=>$course1->id));
$group2 = $DB->get_record('groups', array('id'=>$id), '*', MUST_EXIST);
$id = groups_create_group((object)array('name'=>'Group 2', 'courseid'=>$course2->id));
$group3 = $DB->get_record('groups', array('id'=>$id), '*', MUST_EXIST);
$cohort1 = $this->getDataGenerator()->create_cohort(array('contextid'=>\context_coursecat::instance($cat1->id)->id));
$id = $cohortplugin->add_instance($course1, array('customint1'=>$cohort1->id, 'roleid'=>$studentrole->id, 'customint2'=>$group1->id));
$cohortinstance1 = $DB->get_record('enrol', array('id'=>$id));
$this->assertTrue(groups_add_member($group1, $user4));
$this->assertTrue(groups_add_member($group2, $user4));
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('role_assignments', array()));
$this->assertFalse(groups_is_member($group1->id, $user1->id));
cohort_add_member($cohort1->id, $user1->id);
cohort_add_member($cohort1->id, $user4->id);
cohort_add_member($cohort2->id, $user4->id);
cohort_add_member($cohort2->id, $user3->id);
$this->enable_plugin();
enrol_cohort_sync($trace, null);
$this->assertEquals(8, $DB->count_records('user_enrolments', array()));
$this->assertEquals(8, $DB->count_records('role_assignments', array()));
$this->assertTrue(groups_is_member($group1->id, $user1->id));
$this->assertTrue($DB->record_exists('groups_members', array('groupid'=>$group1->id, 'userid'=>$user1->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue(is_enrolled(\context_course::instance($course1->id), $user4));
$this->assertTrue(groups_is_member($group1->id, $user4->id));
$this->assertFalse($DB->record_exists('groups_members', array('groupid'=>$group1->id, 'userid'=>$user4->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue(is_enrolled(\context_course::instance($course2->id), $user3));
$this->assertFalse(groups_is_member($group3->id, $user3->id));
$cohortinstance1->customint2 = $group2->id;
$DB->update_record('enrol', $cohortinstance1);
$cohortinstance3->customint2 = $group3->id;
$DB->update_record('enrol', $cohortinstance3);
enrol_cohort_sync($trace, null);
$this->assertFalse(groups_is_member($group1->id, $user1->id));
$this->assertTrue(groups_is_member($group2->id, $user1->id));
$this->assertTrue($DB->record_exists('groups_members', array('groupid'=>$group2->id, 'userid'=>$user1->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue(groups_is_member($group1->id, $user4->id));
$this->assertTrue(groups_is_member($group2->id, $user4->id));
$this->assertFalse($DB->record_exists('groups_members', array('groupid'=>$group1->id, 'userid'=>$user4->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertFalse($DB->record_exists('groups_members', array('groupid'=>$group2->id, 'userid'=>$user4->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
$this->assertTrue(groups_is_member($group3->id, $user3->id));
$this->assertTrue($DB->record_exists('groups_members', array('groupid'=>$group3->id, 'userid'=>$user3->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance3->id)));
cohort_remove_member($cohort1->id, $user1->id);
$this->assertFalse(groups_is_member($group1->id, $user1->id));
cohort_remove_member($cohort1->id, $user4->id);
$this->assertTrue(groups_is_member($group1->id, $user4->id));
$this->assertTrue(groups_is_member($group2->id, $user4->id));
}
}
+29
View File
@@ -0,0 +1,29 @@
<?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/>.
/**
* Cohort enrolment plugin version specification.
*
* @package enrol_cohort
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'enrol_cohort'; // Full name of the plugin (used for diagnostics)
@@ -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/>.
/**
* Privacy Subsystem implementation for enrol_database.
*
* @package enrol_database
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_database\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for enrol_database implementing null_provider.
*
* @copyright 2018 Carlos Escobedo <carlos@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,63 @@
<?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/>.
/**
* Sync enrolments task
* @package enrol_database
* @copyright 2018 Daniel Neis Araujo <danielneis@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_database\task;
defined('MOODLE_INTERNAL') || die();
/**
* Class sync_enrolments
* @package enrol_database
* @copyright 2018 Daniel Neis Araujo <danielneis@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sync_enrolments extends \core\task\scheduled_task {
/**
* Name for this task.
*
* @return string
*/
public function get_name() {
return get_string('syncenrolmentstask', 'enrol_database');
}
/**
* Run task for synchronising users.
*/
public function execute() {
$trace = new \text_progress_trace();
if (!enrol_is_enabled('database')) {
$trace->output('Plugin not enabled');
return;
}
$enrol = enrol_get_plugin('database');
// Update enrolments -- these handlers should autocreate courses if required.
$enrol->sync_courses($trace);
$enrol->sync_enrolments($trace);
}
}
+45
View File
@@ -0,0 +1,45 @@
<?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/>.
/**
* Capabilities for database enrolment plugin.
*
* @package enrol_database
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
/* This is used only when sync suspends users instead of full unenrolment. */
'enrol/database:unenrol' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'manager' => CAP_ALLOW,
)
),
'enrol/database:config' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'manager' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
)
),
);
+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/>.
/**
* Database enrolment plugin installation.
*
* @package enrol_database
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
function xmldb_enrol_database_install() {
global $CFG, $DB;
}
+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/>.
/**
* Task definition for enrol_database.
* @package enrol_database
* @copyright 2018 Daniel Neis Araujo <danielneis@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$tasks = array(
array(
'classname' => '\enrol_database\task\sync_enrolments',
'blocking' => 0,
'minute' => 'R',
'hour' => 'R',
'day' => '*',
'month' => '*',
'dayofweek' => '*',
'disabled' => 1
)
);
+39
View File
@@ -0,0 +1,39 @@
<?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/>.
/**
* Database enrolment plugin upgrade.
*
* @package enrol_database
* @copyright 2011 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function xmldb_enrol_database_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+77
View File
@@ -0,0 +1,77 @@
<?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 component 'enrol_database', language 'en'.
*
* @package enrol_database
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['database:config'] = 'Configure database enrol instances';
$string['database:unenrol'] = 'Unenrol suspended users';
$string['dbencoding'] = 'Database encoding';
$string['dbhost'] = 'Database host';
$string['dbhost_desc'] = 'Type database server IP address or host name. Use a system DSN name if using ODBC. Use a PDO DSN if using PDO.';
$string['dbname'] = 'Database name';
$string['dbname_desc'] = 'Leave empty if using a DSN name in database host.';
$string['dbpass'] = 'Database password';
$string['dbsetupsql'] = 'Database setup command';
$string['dbsetupsql_desc'] = 'SQL command for special database setup, often used to setup communication encoding - example for MySQL and PostgreSQL: <em>SET NAMES \'utf8\'</em>';
$string['dbsybasequoting'] = 'Use sybase quotes';
$string['dbsybasequoting_desc'] = 'Sybase style single quote escaping - needed for Oracle, MS SQL and some other databases. Do not use for MySQL!';
$string['dbtype'] = 'Database driver';
$string['dbtype_desc'] = 'ADOdb database driver name, type of the external database engine.';
$string['dbuser'] = 'Database user';
$string['debugdb'] = 'Debug ADOdb';
$string['debugdb_desc'] = 'Debug ADOdb connection to external database - use when getting empty page during login. Not suitable for production sites!';
$string['defaultcategory'] = 'Default new course category';
$string['defaultcategory_desc'] = 'The default category for auto-created courses. Used when no new category id specified or not found.';
$string['defaultrole'] = 'Default role';
$string['defaultrole_desc'] = 'The role that will be assigned by default if no other role is specified in external table.';
$string['ignorehiddencourses'] = 'Ignore hidden courses';
$string['ignorehiddencourses_desc'] = 'If enabled users will not be enrolled on courses that are set to be unavailable to students.';
$string['localcategoryfield'] = 'Local category field';
$string['localcoursefield'] = 'Local course field';
$string['localrolefield'] = 'Local role field';
$string['localuserfield'] = 'Local user field';
$string['newcoursetable'] = 'Remote new courses table';
$string['newcoursetable_desc'] = 'Specify of the name of the table that contains list of courses that should be created automatically. Empty means no courses are created.';
$string['newcoursecategory'] = 'New course category field';
$string['newcoursefullname'] = 'New course full name field';
$string['newcourseidnumber'] = 'New course ID number field';
$string['newcourseshortname'] = 'New course short name field';
$string['pluginname'] = 'External database';
$string['pluginname_desc'] = 'You can use an external database (of nearly any kind) to control your enrolments. It is assumed your external database contains at least a field containing a course ID, and a field containing a user ID. These are compared against fields that you choose in the local course and user tables.';
$string['remotecoursefield'] = 'Remote course field';
$string['remotecoursefield_desc'] = 'The name of the field in the remote table that we are using to match entries in the course table.';
$string['remoteenroltable'] = 'Remote user enrolment table';
$string['remoteenroltable_desc'] = 'Specify the name of the table that contains list of user enrolments. Empty means no user enrolment sync.';
$string['remoteotheruserfield'] = 'Remote Other User field';
$string['remoteotheruserfield_desc'] = 'The name of the field in the remote table that we are using to flag "Other User" role assignments.';
$string['remoterolefield'] = 'Remote role field';
$string['remoterolefield_desc'] = 'The name of the field in the remote table that we are using to match entries in the roles table.';
$string['remoteuserfield'] = 'Remote user field';
$string['settingsheaderdb'] = 'External database connection';
$string['settingsheaderlocal'] = 'Local field mapping';
$string['settingsheaderremote'] = 'Remote enrolment sync';
$string['settingsheadernewcourses'] = 'Creation of new courses';
$string['syncenrolmentstask'] = 'Synchronise external database enrolments task';
$string['remoteuserfield_desc'] = 'The name of the field in the remote table that we are using to match entries in the user table.';
$string['templatecourse'] = 'New course template';
$string['templatecourse_desc'] = 'Optional: auto-created courses can copy their settings from a template course. Type here the shortname of the template course.';
$string['privacy:metadata'] = 'The External database enrolment plugin does not store any personal data.';
File diff suppressed because it is too large Load Diff
+123
View File
@@ -0,0 +1,123 @@
<?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/>.
/**
* Database enrolment plugin settings and presets.
*
* @package enrol_database
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
if ($ADMIN->fulltree) {
//--- general settings -----------------------------------------------------------------------------------
$settings->add(new admin_setting_heading('enrol_database_settings', '', get_string('pluginname_desc', 'enrol_database')));
$settings->add(new admin_setting_heading('enrol_database_exdbheader', get_string('settingsheaderdb', 'enrol_database'), ''));
$options = array('', "access", "ado_access", "ado", "ado_mssql", "borland_ibase", "csv", "db2", "fbsql", "firebird", "ibase", "informix72", "informix", "mssql", "mssql_n", "mssqlnative", "mysql", "mysqli", "mysqlt", "oci805", "oci8", "oci8po", "odbc", "odbc_mssql", "odbc_oracle", "oracle", "pdo", "postgres64", "postgres7", "postgres", "proxy", "sqlanywhere", "sybase", "vfp");
$options = array_combine($options, $options);
$settings->add(new admin_setting_configselect('enrol_database/dbtype', get_string('dbtype', 'enrol_database'), get_string('dbtype_desc', 'enrol_database'), '', $options));
$settings->add(new admin_setting_configtext('enrol_database/dbhost', get_string('dbhost', 'enrol_database'), get_string('dbhost_desc', 'enrol_database'), 'localhost'));
$settings->add(new admin_setting_configtext('enrol_database/dbuser', get_string('dbuser', 'enrol_database'), '', ''));
$settings->add(new admin_setting_configpasswordunmask('enrol_database/dbpass', get_string('dbpass', 'enrol_database'), '', ''));
$settings->add(new admin_setting_configtext('enrol_database/dbname', get_string('dbname', 'enrol_database'), get_string('dbname_desc', 'enrol_database'), ''));
$settings->add(new admin_setting_configtext('enrol_database/dbencoding', get_string('dbencoding', 'enrol_database'), '', 'utf-8'));
$settings->add(new admin_setting_configtext('enrol_database/dbsetupsql', get_string('dbsetupsql', 'enrol_database'), get_string('dbsetupsql_desc', 'enrol_database'), ''));
$settings->add(new admin_setting_configcheckbox('enrol_database/dbsybasequoting', get_string('dbsybasequoting', 'enrol_database'), get_string('dbsybasequoting_desc', 'enrol_database'), 0));
$settings->add(new admin_setting_configcheckbox('enrol_database/debugdb', get_string('debugdb', 'enrol_database'), get_string('debugdb_desc', 'enrol_database'), 0));
$settings->add(new admin_setting_heading('enrol_database_localheader', get_string('settingsheaderlocal', 'enrol_database'), ''));
$options = array('id'=>'id', 'idnumber'=>'idnumber', 'shortname'=>'shortname');
$settings->add(new admin_setting_configselect('enrol_database/localcoursefield', get_string('localcoursefield', 'enrol_database'), '', 'idnumber', $options));
$options = array('id'=>'id', 'idnumber'=>'idnumber', 'email'=>'email', 'username'=>'username'); // only local users if username selected, no mnet users!
$settings->add(new admin_setting_configselect('enrol_database/localuserfield', get_string('localuserfield', 'enrol_database'), '', 'idnumber', $options));
$options = array('id'=>'id', 'shortname'=>'shortname');
$settings->add(new admin_setting_configselect('enrol_database/localrolefield', get_string('localrolefield', 'enrol_database'), '', 'shortname', $options));
$options = array('id'=>'id', 'idnumber'=>'idnumber');
$settings->add(new admin_setting_configselect('enrol_database/localcategoryfield', get_string('localcategoryfield', 'enrol_database'), '', 'id', $options));
$settings->add(new admin_setting_heading('enrol_database_remoteheader', get_string('settingsheaderremote', 'enrol_database'), ''));
$settings->add(new admin_setting_configtext('enrol_database/remoteenroltable', get_string('remoteenroltable', 'enrol_database'), get_string('remoteenroltable_desc', 'enrol_database'), ''));
$settings->add(new admin_setting_configtext('enrol_database/remotecoursefield', get_string('remotecoursefield', 'enrol_database'), get_string('remotecoursefield_desc', 'enrol_database'), ''));
$settings->add(new admin_setting_configtext('enrol_database/remoteuserfield', get_string('remoteuserfield', 'enrol_database'), get_string('remoteuserfield_desc', 'enrol_database'), ''));
$settings->add(new admin_setting_configtext('enrol_database/remoterolefield', get_string('remoterolefield', 'enrol_database'), get_string('remoterolefield_desc', 'enrol_database'), ''));
$otheruserfieldlabel = get_string('remoteotheruserfield', 'enrol_database');
$otheruserfielddesc = get_string('remoteotheruserfield_desc', 'enrol_database');
$settings->add(new admin_setting_configtext('enrol_database/remoteotheruserfield', $otheruserfieldlabel, $otheruserfielddesc, ''));
if (!during_initial_install()) {
$options = get_default_enrol_roles(context_system::instance());
$student = get_archetype_roles('student');
$student = reset($student);
$settings->add(new admin_setting_configselect('enrol_database/defaultrole',
get_string('defaultrole', 'enrol_database'),
get_string('defaultrole_desc', 'enrol_database'),
$student->id ?? null,
$options));
}
$settings->add(new admin_setting_configcheckbox('enrol_database/ignorehiddencourses', get_string('ignorehiddencourses', 'enrol_database'), get_string('ignorehiddencourses_desc', 'enrol_database'), 0));
$options = array(ENROL_EXT_REMOVED_UNENROL => get_string('extremovedunenrol', 'enrol'),
ENROL_EXT_REMOVED_KEEP => get_string('extremovedkeep', 'enrol'),
ENROL_EXT_REMOVED_SUSPEND => get_string('extremovedsuspend', 'enrol'),
ENROL_EXT_REMOVED_SUSPENDNOROLES => get_string('extremovedsuspendnoroles', 'enrol'));
$settings->add(new admin_setting_configselect('enrol_database/unenrolaction', get_string('extremovedaction', 'enrol'), get_string('extremovedaction_help', 'enrol'), ENROL_EXT_REMOVED_UNENROL, $options));
$settings->add(new admin_setting_heading('enrol_database_newcoursesheader', get_string('settingsheadernewcourses', 'enrol_database'), ''));
$settings->add(new admin_setting_configtext('enrol_database/newcoursetable', get_string('newcoursetable', 'enrol_database'), get_string('newcoursetable_desc', 'enrol_database'), ''));
$settings->add(new admin_setting_configtext('enrol_database/newcoursefullname', get_string('newcoursefullname', 'enrol_database'), '', 'fullname'));
$settings->add(new admin_setting_configtext('enrol_database/newcourseshortname', get_string('newcourseshortname', 'enrol_database'), '', 'shortname'));
$settings->add(new admin_setting_configtext('enrol_database/newcourseidnumber', get_string('newcourseidnumber', 'enrol_database'), '', 'idnumber'));
$settings->add(new admin_setting_configtext('enrol_database/newcoursecategory', get_string('newcoursecategory', 'enrol_database'), '', ''));
$settings->add(new admin_settings_coursecat_select('enrol_database/defaultcategory',
get_string('defaultcategory', 'enrol_database'),
get_string('defaultcategory_desc', 'enrol_database'), 1));
$settings->add(new admin_setting_configtext('enrol_database/templatecourse', get_string('templatecourse', 'enrol_database'), get_string('templatecourse_desc', 'enrol_database'), ''));
}
+101
View File
@@ -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/>.
/**
* Database enrolment tests.
*
* @package enrol_database
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_database;
use course_enrolment_manager;
defined('MOODLE_INTERNAL') || die();
/**
* Database enrolment tests.
*
* @package enrol_database
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class lib_test extends \advanced_testcase {
public static function tearDownAfterClass(): void {
global $DB;
// Apply sqlsrv native driver error and logging default
// settings while finishing the AdoDB tests.
if ($DB->get_dbfamily() === 'mssql') {
sqlsrv_configure("WarningsReturnAsErrors", false);
sqlsrv_configure("LogSubsystems", SQLSRV_LOG_SYSTEM_OFF);
sqlsrv_configure("LogSeverity", SQLSRV_LOG_SEVERITY_ERROR);
}
}
/**
* Test for getting user enrolment actions.
*/
public function test_get_user_enrolment_actions(): void {
global $CFG, $PAGE;
$this->resetAfterTest();
// Set page URL to prevent debugging messages.
$PAGE->set_url('/enrol/editinstance.php');
$pluginname = 'database';
// Only enable the database enrol plugin.
$CFG->enrol_plugins_enabled = $pluginname;
$generator = $this->getDataGenerator();
// Get the enrol plugin.
$plugin = enrol_get_plugin($pluginname);
// Create a course.
$course = $generator->create_course();
// Enable this enrol plugin for the course.
$plugin->add_instance($course);
// Create a student.
$student = $generator->create_user();
// Enrol the student to the course.
$generator->enrol_user($student->id, $course->id, 'student', $pluginname);
// Teachers don't have enrol/database:unenrol capability by default. Login as admin for simplicity.
$this->setAdminUser();
require_once($CFG->dirroot . '/enrol/locallib.php');
$manager = new course_enrolment_manager($PAGE, $course);
$userenrolments = $manager->get_user_enrolments($student->id);
$this->assertCount(1, $userenrolments);
$ue = reset($userenrolments);
$actions = $plugin->get_user_enrolment_actions($manager, $ue);
// Database enrol has 0 enrol actions for active users.
$this->assertCount(0, $actions);
// Enrol actions for a suspended student.
// Suspend the student.
$ue->status = ENROL_USER_SUSPENDED;
$actions = $plugin->get_user_enrolment_actions($manager, $ue);
// Database enrol has enrol actions for suspended students -- unenrol.
$this->assertCount(1, $actions);
}
}
+805
View File
@@ -0,0 +1,805 @@
<?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 enrol_database;
/**
* External database enrolment sync tests
*
* This also tests adodb drivers that are matching
* our four supported Moodle database drivers.
*
* @package enrol_database
* @category test
* @copyright 2011 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sync_test extends \advanced_testcase {
protected static $courses = array();
protected static $users = array();
protected static $roles = array();
/** @var string Original error log */
protected $oldlog;
public static function tearDownAfterClass(): void {
global $DB;
// Apply sqlsrv native driver error and logging default
// settings while finishing the AdoDB tests.
if ($DB->get_dbfamily() === 'mssql') {
sqlsrv_configure("WarningsReturnAsErrors", false);
sqlsrv_configure("LogSubsystems", SQLSRV_LOG_SYSTEM_OFF);
sqlsrv_configure("LogSeverity", SQLSRV_LOG_SEVERITY_ERROR);
}
}
protected function init_enrol_database() {
global $DB, $CFG;
// Discard error logs from AdoDB.
$this->oldlog = ini_get('error_log');
ini_set('error_log', "$CFG->dataroot/testlog.log");
$dbman = $DB->get_manager();
set_config('dbencoding', 'utf-8', 'enrol_database');
set_config('dbhost', $CFG->dbhost, 'enrol_database');
set_config('dbuser', $CFG->dbuser, 'enrol_database');
set_config('dbpass', $CFG->dbpass, 'enrol_database');
set_config('dbname', $CFG->dbname, 'enrol_database');
if (!empty($CFG->dboptions['dbport'])) {
set_config('dbhost', $CFG->dbhost.':'.$CFG->dboptions['dbport'], 'enrol_database');
}
switch ($DB->get_dbfamily()) {
case 'mysql':
set_config('dbtype', 'mysqli', 'enrol_database');
set_config('dbsetupsql', "SET NAMES 'UTF-8'", 'enrol_database');
set_config('dbsybasequoting', '0', 'enrol_database');
if (!empty($CFG->dboptions['dbsocket'])) {
$dbsocket = $CFG->dboptions['dbsocket'];
if ((strpos($dbsocket, '/') === false and strpos($dbsocket, '\\') === false)) {
$dbsocket = ini_get('mysqli.default_socket');
}
set_config('dbtype', 'mysqli://'.rawurlencode($CFG->dbuser).':'.rawurlencode($CFG->dbpass).'@'.rawurlencode($CFG->dbhost).'/'.rawurlencode($CFG->dbname).'?socket='.rawurlencode($dbsocket), 'enrol_database');
}
break;
case 'oracle':
set_config('dbtype', 'oci8po', 'enrol_database');
set_config('dbsybasequoting', '1', 'enrol_database');
break;
case 'postgres':
set_config('dbtype', 'postgres7', 'enrol_database');
$setupsql = "SET NAMES 'UTF-8'";
if (!empty($CFG->dboptions['dbschema'])) {
$setupsql .= "; SET search_path = '".$CFG->dboptions['dbschema']."'";
}
set_config('dbsetupsql', $setupsql, 'enrol_database');
set_config('dbsybasequoting', '0', 'enrol_database');
if (!empty($CFG->dboptions['dbsocket']) and ($CFG->dbhost === 'localhost' or $CFG->dbhost === '127.0.0.1')) {
if (strpos($CFG->dboptions['dbsocket'], '/') !== false) {
$socket = $CFG->dboptions['dbsocket'];
if (!empty($CFG->dboptions['dbport'])) {
$socket .= ':' . $CFG->dboptions['dbport'];
}
set_config('dbhost', $socket, 'enrol_database');
} else {
set_config('dbhost', '', 'enrol_database');
}
}
break;
case 'mssql':
set_config('dbtype', 'mssqlnative', 'enrol_database');
set_config('dbsybasequoting', '1', 'enrol_database');
// The native sqlsrv driver uses a comma as separator between host and port.
$dbhost = $CFG->dbhost;
if (!empty($dboptions['dbport'])) {
$dbhost .= ',' . $dboptions['dbport'];
}
set_config('dbhost', $dbhost, 'enrol_database');
break;
default:
throw new exception('Unknown database driver '.get_class($DB));
}
// NOTE: It is stongly discouraged to create new tables in advanced_testcase classes,
// but there is no other simple way to test ext database enrol sync, so let's
// disable transactions are try to cleanup after the tests.
$table = new \xmldb_table('enrol_database_test_enrols');
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('courseid', XMLDB_TYPE_CHAR, '255', null, null, null);
$table->add_field('userid', XMLDB_TYPE_CHAR, '255', null, null, null);
$table->add_field('roleid', XMLDB_TYPE_CHAR, '255', null, null, null);
$table->add_field('otheruser', XMLDB_TYPE_CHAR, '1', null, XMLDB_NOTNULL, null, '0');
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
if ($dbman->table_exists($table)) {
$dbman->drop_table($table);
}
$dbman->create_table($table);
set_config('remoteenroltable', $CFG->prefix.'enrol_database_test_enrols', 'enrol_database');
set_config('remotecoursefield', 'courseid', 'enrol_database');
set_config('remoteuserfield', 'userid', 'enrol_database');
set_config('remoterolefield', 'roleid', 'enrol_database');
set_config('remoteotheruserfield', 'otheruser', 'enrol_database');
$table = new \xmldb_table('enrol_database_test_courses');
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('fullname', XMLDB_TYPE_CHAR, '255', null, null, null);
$table->add_field('shortname', XMLDB_TYPE_CHAR, '255', null, null, null);
$table->add_field('idnumber', XMLDB_TYPE_CHAR, '255', null, null, null);
$table->add_field('category', XMLDB_TYPE_CHAR, '255', null, null, null);
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
if ($dbman->table_exists($table)) {
$dbman->drop_table($table);
}
$dbman->create_table($table);
set_config('newcoursetable', $CFG->prefix.'enrol_database_test_courses', 'enrol_database');
set_config('newcoursefullname', 'fullname', 'enrol_database');
set_config('newcourseshortname', 'shortname', 'enrol_database');
set_config('newcourseidnumber', 'idnumber', 'enrol_database');
set_config('newcoursecategory', 'category', 'enrol_database');
// Create some test users and courses.
for ($i = 1; $i <= 4; $i++) {
self::$courses[$i] = $this->getDataGenerator()->create_course(array('fullname' => 'Test course '.$i, 'shortname' => 'tc'.$i, 'idnumber' => 'courseid'.$i));
}
for ($i = 1; $i <= 10; $i++) {
self::$users[$i] = $this->getDataGenerator()->create_user(array('username' => 'username'.$i, 'idnumber' => 'userid'.$i, 'email' => 'user'.$i.'@example.com'));
}
foreach (get_all_roles() as $role) {
self::$roles[$role->shortname] = $role;
}
}
protected function cleanup_enrol_database() {
global $DB;
$dbman = $DB->get_manager();
$table = new \xmldb_table('enrol_database_test_enrols');
$dbman->drop_table($table);
$table = new \xmldb_table('enrol_database_test_courses');
$dbman->drop_table($table);
self::$courses = null;
self::$users = null;
self::$roles = null;
ini_set('error_log', $this->oldlog);
}
protected function reset_enrol_database() {
global $DB;
$DB->delete_records('enrol_database_test_enrols', array());
$DB->delete_records('enrol_database_test_courses', array());
$plugin = enrol_get_plugin('database');
$instances = $DB->get_records('enrol', array('enrol' => 'database'));
foreach($instances as $instance) {
$plugin->delete_instance($instance);
}
}
protected function assertIsEnrolled($userindex, $courseindex, $status=null, $rolename = null) {
global $DB;
$dbinstance = $DB->get_record('enrol', array('courseid' => self::$courses[$courseindex]->id, 'enrol' => 'database'), '*', MUST_EXIST);
$conditions = array('enrolid' => $dbinstance->id, 'userid' => self::$users[$userindex]->id);
if ($status !== null) {
$conditions['status'] = $status;
}
$this->assertTrue($DB->record_exists('user_enrolments', $conditions));
$this->assertHasRoleAssignment($userindex, $courseindex, $rolename);
}
protected function assertHasRoleAssignment($userindex, $courseindex, $rolename = null) {
global $DB;
$dbinstance = $DB->get_record('enrol', array('courseid' => self::$courses[$courseindex]->id, 'enrol' => 'database'), '*', MUST_EXIST);
$coursecontext = \context_course::instance(self::$courses[$courseindex]->id);
if ($rolename === false) {
$this->assertFalse($DB->record_exists('role_assignments', array('component' => 'enrol_database', 'itemid' => $dbinstance->id, 'userid' => self::$users[$userindex]->id, 'contextid' => $coursecontext->id)));
} else if ($rolename !== null) {
$this->assertTrue($DB->record_exists('role_assignments', array('component' => 'enrol_database', 'itemid' => $dbinstance->id, 'userid' => self::$users[$userindex]->id, 'contextid' => $coursecontext->id, 'roleid' => self::$roles[$rolename]->id)));
}
}
protected function assertIsNotEnrolled($userindex, $courseindex) {
global $DB;
if (!$dbinstance = $DB->get_record('enrol', array('courseid' => self::$courses[$courseindex]->id, 'enrol' => 'database'))) {
return;
}
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid' => $dbinstance->id, 'userid' => self::$users[$userindex]->id)));
}
public function test_sync_user_enrolments(): void {
global $DB;
$this->init_enrol_database();
$this->resetAfterTest(false);
$this->preventResetByRollback();
$plugin = enrol_get_plugin('database');
// Test basic enrol sync for one user after login.
$this->reset_enrol_database();
$plugin->set_config('localcoursefield', 'idnumber');
$plugin->set_config('localuserfield', 'idnumber');
$plugin->set_config('localrolefield', 'shortname');
$plugin->set_config('defaultrole', self::$roles['student']->id);
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid2', 'roleid' => 'teacher'));
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid2', 'courseid' => 'courseid1', 'roleid' => null));
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid4', 'courseid' => 'courseid4', 'roleid' => 'editingteacher', 'otheruser' => '1'));
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'xxxxxxx', 'courseid' => 'courseid1', 'roleid' => 'student')); // Bogus record to be ignored.
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'xxxxxxxxx', 'roleid' => 'student')); // Bogus record to be ignored.
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->assertEquals(0, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(0, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$plugin->sync_user_enrolments(self::$users[1]);
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(2, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
// Make sure there are no errors or changes on the next login.
$plugin->sync_user_enrolments(self::$users[1]);
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(2, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$plugin->sync_user_enrolments(self::$users[2]);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(3, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$plugin->sync_user_enrolments(self::$users[4]);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(4, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, 'editingteacher');
// Enrolment removals.
$DB->delete_records('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_KEEP);
$plugin->sync_user_enrolments(self::$users[1]);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(4, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND);
$plugin->sync_user_enrolments(self::$users[1]);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(4, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_SUSPENDED, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$plugin->sync_user_enrolments(self::$users[1]);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(4, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$DB->delete_records('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
$plugin->sync_user_enrolments(self::$users[1]);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(3, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_SUSPENDED, false);
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$plugin->sync_user_enrolments(self::$users[1]);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(4, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$DB->delete_records('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
$plugin->sync_user_enrolments(self::$users[1]);
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(3, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsNotEnrolled(1, 1);
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$DB->delete_records('enrol_database_test_enrols', array('userid' => 'userid4', 'courseid' => 'courseid4', 'roleid' => 'editingteacher'));
$plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
$plugin->sync_user_enrolments(self::$users[4]);
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(2, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, false);
// Test all other mapping options.
$this->reset_enrol_database();
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->assertEquals(0, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(0, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$plugin->set_config('localcoursefield', 'id');
$plugin->set_config('localuserfield', 'id');
$plugin->set_config('localrolefield', 'id');
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->id, 'courseid' => self::$courses[1]->id, 'roleid' => self::$roles['student']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->id, 'courseid' => self::$courses[2]->id, 'roleid' => self::$roles['teacher']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[2]->id, 'courseid' => self::$courses[1]->id, 'roleid' => self::$roles['student']->id));
$plugin->sync_user_enrolments(self::$users[1]);
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(2, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$this->reset_enrol_database();
$plugin->set_config('localcoursefield', 'shortname');
$plugin->set_config('localuserfield', 'email');
$plugin->set_config('localrolefield', 'id');
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->email, 'courseid' => self::$courses[1]->shortname, 'roleid' => self::$roles['student']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->email, 'courseid' => self::$courses[2]->shortname, 'roleid' => self::$roles['teacher']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[2]->email, 'courseid' => self::$courses[1]->shortname, 'roleid' => self::$roles['student']->id));
$plugin->sync_user_enrolments(self::$users[1]);
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(2, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$this->reset_enrol_database();
$plugin->set_config('localcoursefield', 'id');
$plugin->set_config('localuserfield', 'username');
$plugin->set_config('localrolefield', 'id');
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->username, 'courseid' => self::$courses[1]->id, 'roleid' => self::$roles['student']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->username, 'courseid' => self::$courses[2]->id, 'roleid' => self::$roles['teacher']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[2]->username, 'courseid' => self::$courses[1]->id, 'roleid' => self::$roles['student']->id));
$plugin->sync_user_enrolments(self::$users[1]);
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(2, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
}
/**
* @depends test_sync_user_enrolments
*/
public function test_sync_users(): void {
global $DB;
$this->resetAfterTest(false);
$this->preventResetByRollback();
$this->reset_enrol_database();
$plugin = enrol_get_plugin('database');
$trace = new \null_progress_trace();
// Test basic enrol sync for one user after login.
$this->reset_enrol_database();
$plugin->set_config('localcoursefield', 'idnumber');
$plugin->set_config('localuserfield', 'idnumber');
$plugin->set_config('localrolefield', 'shortname');
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid2', 'roleid' => 'editingteacher'));
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid2', 'courseid' => 'courseid1', 'roleid' => 'student'));
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid4', 'courseid' => 'courseid4', 'roleid' => 'editingteacher', 'otheruser' => '1'));
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'xxxxxxx', 'courseid' => 'courseid1', 'roleid' => 'student')); // Bogus record to be ignored.
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'xxxxxxxxx', 'roleid' => 'student')); // Bogus record to be ignored.
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->assertEquals(0, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(0, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$plugin->sync_enrolments($trace);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(4, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'editingteacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, 'editingteacher');
$plugin->set_config('defaultrole', self::$roles['teacher']->id);
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid3', 'courseid' => 'courseid3'));
$plugin->sync_enrolments($trace);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(4, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(5, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'editingteacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, 'editingteacher');
$this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
// Test different unenrolment options.
$DB->delete_records('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_KEEP);
$plugin->sync_enrolments($trace);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(4, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(5, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'editingteacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, 'editingteacher');
$this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
$plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND);
$plugin->sync_enrolments($trace);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(4, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(5, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_SUSPENDED, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'editingteacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, 'editingteacher');
$this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$plugin->sync_enrolments($trace);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(4, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(5, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'editingteacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, 'editingteacher');
$this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
$DB->delete_records('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
$plugin->sync_enrolments($trace);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(4, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(4, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_SUSPENDED, false);
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'editingteacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, 'editingteacher');
$this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$plugin->sync_enrolments($trace);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(4, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(5, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'editingteacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, 'editingteacher');
$this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
$DB->delete_records('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
$plugin->sync_enrolments($trace);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(4, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(4, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsNotEnrolled(1, 1);
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'editingteacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, 'editingteacher');
$this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'student'));
$DB->insert_record('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'teacher'));
$plugin->sync_enrolments($trace);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(4, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(6, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'teacher');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'editingteacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, 'editingteacher');
$this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
$DB->delete_records('enrol_database_test_enrols', array('userid' => 'userid1', 'courseid' => 'courseid1', 'roleid' => 'teacher'));
$plugin->sync_enrolments($trace);
$this->assertEquals(4, $DB->count_records('user_enrolments', array()));
$this->assertEquals(4, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(5, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'editingteacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsNotEnrolled(4, 4);
$this->assertHasRoleAssignment(4, 4, 'editingteacher');
$this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
// Test all other mapping options.
$this->reset_enrol_database();
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->assertEquals(0, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(0, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$plugin->set_config('localcoursefield', 'id');
$plugin->set_config('localuserfield', 'id');
$plugin->set_config('localrolefield', 'id');
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->id, 'courseid' => self::$courses[1]->id, 'roleid' => self::$roles['student']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->id, 'courseid' => self::$courses[2]->id, 'roleid' => self::$roles['teacher']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[2]->id, 'courseid' => self::$courses[1]->id, 'roleid' => self::$roles['student']->id));
$plugin->sync_enrolments($trace);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(3, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->reset_enrol_database();
$plugin->set_config('localcoursefield', 'shortname');
$plugin->set_config('localuserfield', 'email');
$plugin->set_config('localrolefield', 'id');
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->email, 'courseid' => self::$courses[1]->shortname, 'roleid' => self::$roles['student']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->email, 'courseid' => self::$courses[2]->shortname, 'roleid' => self::$roles['teacher']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[2]->email, 'courseid' => self::$courses[1]->shortname, 'roleid' => self::$roles['student']->id));
$plugin->sync_enrolments($trace);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(3, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$this->reset_enrol_database();
$plugin->set_config('localcoursefield', 'id');
$plugin->set_config('localuserfield', 'username');
$plugin->set_config('localrolefield', 'id');
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->username, 'courseid' => self::$courses[1]->id, 'roleid' => self::$roles['student']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->username, 'courseid' => self::$courses[2]->id, 'roleid' => self::$roles['teacher']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[2]->username, 'courseid' => self::$courses[1]->id, 'roleid' => self::$roles['student']->id));
$plugin->sync_enrolments($trace);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(3, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
// Test sync of one course only.
$this->reset_enrol_database();
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->username, 'courseid' => self::$courses[1]->id, 'roleid' => self::$roles['student']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[1]->username, 'courseid' => self::$courses[2]->id, 'roleid' => self::$roles['teacher']->id));
$DB->insert_record('enrol_database_test_enrols', array('userid' => self::$users[2]->username, 'courseid' => self::$courses[1]->id, 'roleid' => self::$roles['student']->id));
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->assertEquals(0, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(0, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$plugin->sync_enrolments($trace, self::$courses[3]->id);
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->assertEquals(1, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(0, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$plugin->sync_enrolments($trace, self::$courses[1]->id);
$this->assertEquals(2, $DB->count_records('user_enrolments', array()));
$this->assertEquals(2, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(2, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$plugin->sync_enrolments($trace, self::$courses[2]->id);
$this->assertEquals(3, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(3, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
$plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
$DB->delete_records('enrol_database_test_enrols', array());
$plugin->sync_enrolments($trace, self::$courses[1]->id);
$this->assertEquals(1, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(1, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
$this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
$plugin->sync_enrolments($trace, self::$courses[2]->id);
$this->assertEquals(0, $DB->count_records('user_enrolments', array()));
$this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
$this->assertEquals(0, $DB->count_records('role_assignments', array('component' => 'enrol_database')));
}
/**
* @depends test_sync_users
*/
public function test_sync_courses(): void {
global $DB;
$this->resetAfterTest(true);
$this->preventResetByRollback();
$this->reset_enrol_database();
$plugin = enrol_get_plugin('database');
$trace = new \null_progress_trace();
$plugin->set_config('localcategoryfield', 'id');
$coursecat = $this->getDataGenerator()->create_category(array('name' => 'Test category 1', 'idnumber' => 'tcid1'));
$defcat = $DB->get_record('course_categories', array('id' => $plugin->get_config('defaultcategory')));
$course1 = array('fullname' => 'New course 1', 'shortname' => 'nc1', 'idnumber' => 'ncid1', 'category' => $coursecat->id);
$course2 = array('fullname' => 'New course 2', 'shortname' => 'nc2', 'idnumber' => 'ncid2', 'category' => null);
// Duplicate records are to be ignored.
$course3 = array('fullname' => 'New course 3', 'shortname' => 'xx', 'idnumber' => 'yy2', 'category' => $defcat->id);
$course4 = array('fullname' => 'New course 4', 'shortname' => 'xx', 'idnumber' => 'yy3', 'category' => $defcat->id);
$course5 = array('fullname' => 'New course 5', 'shortname' => 'xx1', 'idnumber' => 'yy', 'category' => $defcat->id);
$course6 = array('fullname' => 'New course 6', 'shortname' => 'xx2', 'idnumber' => 'yy', 'category' => $defcat->id);
$DB->insert_record('enrol_database_test_courses', $course1);
$DB->insert_record('enrol_database_test_courses', $course2);
$DB->insert_record('enrol_database_test_courses', $course3);
$DB->insert_record('enrol_database_test_courses', $course4);
$DB->insert_record('enrol_database_test_courses', $course5);
$DB->insert_record('enrol_database_test_courses', $course6);
$this->assertEquals(1+count(self::$courses), $DB->count_records('course'));
$plugin->sync_courses($trace);
$this->assertEquals(4+1+count(self::$courses), $DB->count_records('course'));
$this->assertTrue($DB->record_exists('course', $course1));
$course2['category'] = $defcat->id;
$this->assertTrue($DB->record_exists('course', $course2));
// People should NOT push duplicates there because the results are UNDEFINED! But anyway skip the duplicates.
$this->assertEquals(1, $DB->count_records('course', array('idnumber' => 'yy')));
$this->assertEquals(1, $DB->count_records('course', array('shortname' => 'xx')));
// Check default number of sections matches with the created course sections.
$recordcourse1 = $DB->get_record('course', $course1);
$courseconfig = get_config('moodlecourse');
$numsections = $DB->count_records('course_sections', array('course' => $recordcourse1->id));
// To compare numsections we have to add topic 0 to default numsections.
$this->assertEquals(($courseconfig->numsections + 1), $numsections);
// Test category mapping via idnumber.
$plugin->set_config('localcategoryfield', 'idnumber');
$course7 = array('fullname' => 'New course 7', 'shortname' => 'nc7', 'idnumber' => 'ncid7', 'category' => 'tcid1');
$DB->insert_record('enrol_database_test_courses', $course7);
$plugin->sync_courses($trace);
$this->assertEquals(1+4+1+count(self::$courses), $DB->count_records('course'));
$this->assertTrue($DB->record_exists('course', $course1));
$this->assertTrue($DB->record_exists('course', $course2));
$course7['category'] = $coursecat->id;
$this->assertTrue($DB->record_exists('course', $course7));
// Test course template.
$template = $this->getDataGenerator()->create_course(array('numsections' => 666, 'shortname' => 'crstempl'));
$plugin->set_config('templatecourse', 'crstempl');
$course8 = array('fullname' => 'New course 8', 'shortname' => 'nc8', 'idnumber' => 'ncid8', 'category' => null);
$DB->insert_record('enrol_database_test_courses', $course8);
$plugin->sync_courses($trace);
$this->assertEquals(2+1+4+1+count(self::$courses), $DB->count_records('course'));
$course8['category'] = $defcat->id;
$record = $DB->get_record('course', $course8);
$this->assertFalse(empty($record));
$this->assertEquals(666, course_get_format($record)->get_last_section_number());
// Test invalid category.
$course9 = array('fullname' => 'New course 9', 'shortname' => 'nc9', 'idnumber' => 'ncid9', 'category' => 'xxxxxxx');
$DB->insert_record('enrol_database_test_courses', $course9);
$plugin->sync_courses($trace);
$this->assertEquals(2+1+4+1+count(self::$courses), $DB->count_records('course'));
$this->assertFalse($DB->record_exists('course', array('idnumber' => 'ncid9')));
// Test when categories not specified.
$plugin->set_config('newcoursecategory', '');
$plugin->sync_courses($trace);
$this->assertEquals(1+2+1+4+1+count(self::$courses), $DB->count_records('course'));
$this->assertTrue($DB->record_exists('course', array('idnumber' => 'ncid9')));
// Final cleanup - remove extra tables, fixtures and caches.
$this->cleanup_enrol_database();
}
}
+11
View File
@@ -0,0 +1,11 @@
This files describes API changes in the enrol_database code.
=== 3.11 ===
* Final deprecation enrol/database/cli/sync.php. Refer below for substitute.
=== 3.9 ===
* Class enrol_database_admin_setting_category has been removed. This class was only used by the database
enrolment plugin settings and it was replaced by admin_settings_coursecat_select.
=== 3.7 ===
* enrol/database/cli/sync.php script has been deprecated in favour of enrol_database\task\sync_enrolments task.
+29
View File
@@ -0,0 +1,29 @@
<?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/>.
/**
* Database enrolment plugin version specification.
*
* @package enrol_database
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'enrol_database'; // Full name of the plugin (used for diagnostics)
+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/>.
/**
* User enrolment edit script.
*
* This page allows the current user to edit a user enrolment.
* It is not compatible with the frontpage.
*
* NOTE: plugins are free to implement own edit scripts with extra logic.
*
* @package core_enrol
* @copyright 2011 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require('../config.php');
require_once("$CFG->dirroot/enrol/locallib.php"); // Required for the course enrolment manager.
require_once("$CFG->dirroot/enrol/renderer.php"); // Required for the course enrolment users table.
require_once("$CFG->dirroot/enrol/editenrolment_form.php"); // Forms for this page.
$ueid = required_param('ue', PARAM_INT);
$filter = optional_param('ifilter', 0, PARAM_INT); // Table filter for return url.
$ue = $DB->get_record('user_enrolments', array('id' => $ueid), '*', MUST_EXIST);
$user = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
$instance = $DB->get_record('enrol', array('id'=>$ue->enrolid), '*', MUST_EXIST);
$course = $DB->get_record('course', array('id'=>$instance->courseid), '*', MUST_EXIST);
// The URL of the enrolled users page for the course.
$usersurl = new moodle_url('/user/index.php', array('id' => $course->id));
// Do not allow any changes if plugin disabled, not available or not suitable.
if (!$plugin = enrol_get_plugin($instance->enrol)) {
redirect($usersurl);
}
if (!$plugin->allow_manage($instance)) {
redirect($usersurl);
}
// Obviously.
require_login($course);
// The user must be able to manage enrolments within the course.
require_capability('enrol/'.$instance->enrol.':manage', context_course::instance($course->id, MUST_EXIST));
// Get the enrolment manager for this course.
$manager = new course_enrolment_manager($PAGE, $course, $filter);
// Get an enrolment users table object. Doing this will automatically retrieve the the URL params
// relating to table the user was viewing before coming here, and allows us to return the user to the
// exact page of the users screen they can from.
$table = new course_enrolment_users_table($manager, $PAGE);
// The URl to return the user too after this screen.
$returnurl = new moodle_url($usersurl, $manager->get_url_params()+$table->get_url_params());
// The URL of this page.
$url = new moodle_url('/enrol/editenrolment.php', $returnurl->params());
$PAGE->set_url($url);
$PAGE->set_pagelayout('admin');
navigation_node::override_active_url($usersurl);
// Get the enrolment edit form.
$mform = new enrol_user_enrolment_form($url,
[
'user' => $user,
'course' => $course,
'ue' => $ue,
'enrolinstancename' => $plugin->get_instance_name($instance)
]
);
$mform->set_data($PAGE->url->params());
if ($mform->is_cancelled()) {
redirect($returnurl);
} else if ($data = $mform->get_data()) {
if (!empty($data->duration) && $data->timeend == 0) {
$data->timeend = $data->timestart + $data->duration;
}
if ($manager->edit_enrolment($ue, $data)) {
redirect($returnurl);
}
}
$fullname = fullname($user);
$title = get_string('editenrolment', 'core_enrol');
$PAGE->set_title($title);
$PAGE->set_heading($title);
$PAGE->navbar->add($title);
$PAGE->navbar->add($fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($fullname);
$mform->display();
echo $OUTPUT->footer();
+91
View File
@@ -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/>.
/**
* Contains the form used to edit enrolments for a user.
*
* @package core_enrol
* @copyright 2011 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core_enrol\enrol_helper;
defined('MOODLE_INTERNAL') || die();
require_once("$CFG->libdir/formslib.php");
class enrol_user_enrolment_form extends moodleform {
function definition() {
$mform = $this->_form;
$ue = $this->_customdata['ue'];
$instancename = $this->_customdata['enrolinstancename'];
$modal = !empty($this->_customdata['modal']);
$periodmenu = enrol_get_period_list();
$duration = enrol_calculate_duration($ue->timestart, $ue->timeend);
$mform->addElement('static', 'enrolmentmethod', get_string('enrolmentmethod', 'enrol'), $instancename);
$options = array(ENROL_USER_ACTIVE => get_string('participationactive', 'enrol'),
ENROL_USER_SUSPENDED => get_string('participationsuspended', 'enrol'));
if (isset($options[$ue->status])) {
$mform->addElement('select', 'status', get_string('participationstatus', 'enrol'), $options);
}
$mform->addElement('date_time_selector', 'timestart', get_string('enroltimestart', 'enrol'), array('optional' => true));
$mform->addElement('select', 'duration', get_string('enrolperiod', 'enrol'), $periodmenu);
$mform->setDefault('duration', $duration);
$mform->disabledIf('duration', 'timestart[enabled]', 'notchecked', 1);
$mform->disabledIf('duration', 'timeend[enabled]', 'checked', 1);
$mform->addElement('date_time_selector', 'timeend', get_string('enroltimeend', 'enrol'), array('optional' => true));
$mform->addElement('static', 'timecreated', get_string('enroltimecreated', 'enrol'), userdate($ue->timecreated));
$mform->addElement('hidden', 'ue');
$mform->setType('ue', PARAM_INT);
$mform->addElement('hidden', 'ifilter');
$mform->setType('ifilter', PARAM_ALPHA);
// Show action buttons if this is not being rendered as a fragment.
if (!$modal) {
$this->add_action_buttons();
}
$this->set_data(array(
'ue' => $ue->id,
'status' => $ue->status,
'timestart' => $ue->timestart,
'timeend' => $ue->timeend
));
}
function validation($data, $files) {
$errors = parent::validation($data, $files);
if (!empty($data['timestart']) and !empty($data['timeend'])) {
if ($data['timestart'] >= $data['timeend']) {
$errors['timeend'] = get_string('enroltimeendinvalid', 'enrol');
}
}
return $errors;
}
}
+115
View File
@@ -0,0 +1,115 @@
<?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 new instance of an enrolment plugin to specified course or edits current instance.
*
* @package core_enrol
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require('../config.php');
require_once('editinstance_form.php');
$courseid = required_param('courseid', PARAM_INT);
$type = required_param('type', PARAM_COMPONENT);
$instanceid = optional_param('id', 0, PARAM_INT);
$return = optional_param('returnurl', 0, PARAM_LOCALURL);
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
$context = context_course::instance($course->id, MUST_EXIST);
$plugin = enrol_get_plugin($type);
if (!$plugin) {
throw new moodle_exception('invaliddata', 'error');
}
require_login($course);
require_capability('enrol/' . $type . ':config', $context);
$url = new moodle_url('/enrol/editinstance.php', ['courseid' => $course->id, 'id' => $instanceid, 'type' => $type]);
$PAGE->set_url($url);
$PAGE->set_pagelayout('admin');
$PAGE->set_docs_path('enrol/' . $type . '/edit');
if (empty($return)) {
$return = new moodle_url('/enrol/instances.php', array('id' => $course->id));
}
if (!enrol_is_enabled($type)) {
redirect($return);
}
if ($instanceid) {
$instance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => $type, 'id' => $instanceid), '*', MUST_EXIST);
if ($instance->status == ENROL_INSTANCE_DISABLED) { // The instance is currently disabled.
navigation_node::override_active_url(new moodle_url('/enrol/instances.php', ['id' => $course->id]));
$name = $instance->name ?: get_string('pluginname', 'enrol_' . $type);
$PAGE->navbar->add($name, $url);
}
} else {
require_capability('moodle/course:enrolconfig', $context);
// No instance yet, we have to add new instance.
navigation_node::override_active_url(new moodle_url('/enrol/instances.php', array('id' => $course->id)));
$instance = (object)$plugin->get_instance_defaults();
$instance->id = null;
$instance->courseid = $course->id;
$instance->status = ENROL_INSTANCE_ENABLED; // Do not use default for automatically created instances here.
$PAGE->navbar->add(get_string('pluginname', 'enrol_' . $type), $url);
}
$mform = new enrol_instance_edit_form(null, array($instance, $plugin, $context, $type, $return));
if ($mform->is_cancelled()) {
redirect($return);
} else if ($data = $mform->get_data()) {
if ($instance->id) {
$reset = false;
if (isset($data->status)) {
$reset = ($instance->status != $data->status);
}
foreach ($data as $key => $value) {
$instance->$key = $value;
}
$instance->timemodified = time();
$plugin->update_instance($instance, $data);
if ($reset) {
$context->mark_dirty();
}
} else {
$fields = (array) $data;
$plugin->add_instance($course, $fields);
}
redirect($return);
}
$PAGE->set_heading($course->fullname);
$PAGE->set_title(get_string('pluginname', 'enrol_' . $type));
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('pluginname', 'enrol_' . $type));
$mform->display();
echo $OUTPUT->footer();
+89
View File
@@ -0,0 +1,89 @@
<?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 new instance of enrol_plugin to specified course or edits current instance.
*
* @package core_enrol
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
/**
* Standard edit form shared by all enrol plugins.
*
* @package core_enrol
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol_instance_edit_form extends moodleform {
/**
* Called to define this moodle form
*
* @return void
*/
public function definition() {
global $DB;
$mform = $this->_form;
list($instance, $plugin, $context, $type, $returnurl) = $this->_customdata;
$plugin->edit_instance_form($instance, $mform, $context);
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'courseid');
$mform->setType('courseid', PARAM_INT);
$mform->addElement('hidden', 'type');
$mform->setType('type', PARAM_COMPONENT);
$instance->type = $type;
$mform->addElement('hidden', 'returnurl');
$mform->setType('returnurl', PARAM_LOCALURL);
$mform->setConstant('returnurl', $returnurl);
$this->add_action_buttons(true, ($instance->id ? null : get_string('addinstance', 'enrol')));
$this->set_data($instance);
}
/**
* Validate this form. Calls plugin validation method.
*
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);
list($instance, $plugin, $context, $type) = $this->_customdata;
$pluginerrors = $plugin->edit_instance_validation($data, $files, $instance, $context);
$errors = array_merge($errors, $pluginerrors);
return $errors;
}
}
File diff suppressed because it is too large Load Diff
@@ -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/>.
/**
* Payment subsystem callback implementation for enrol_fee.
*
* @package enrol_fee
* @category payment
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_fee\payment;
/**
* Payment subsystem callback implementation for enrol_fee.
*
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class service_provider implements \core_payment\local\callback\service_provider {
/**
* Callback function that returns the enrolment cost and the accountid
* for the course that $instanceid enrolment instance belongs to.
*
* @param string $paymentarea Payment area
* @param int $instanceid The enrolment instance id
* @return \core_payment\local\entities\payable
*/
public static function get_payable(string $paymentarea, int $instanceid): \core_payment\local\entities\payable {
global $DB;
$instance = $DB->get_record('enrol', ['enrol' => 'fee', 'id' => $instanceid], '*', MUST_EXIST);
return new \core_payment\local\entities\payable($instance->cost, $instance->currency, $instance->customint1);
}
/**
* Callback function that returns the URL of the page the user should be redirected to in the case of a successful payment.
*
* @param string $paymentarea Payment area
* @param int $instanceid The enrolment instance id
* @return \moodle_url
*/
public static function get_success_url(string $paymentarea, int $instanceid): \moodle_url {
global $DB;
$courseid = $DB->get_field('enrol', 'courseid', ['enrol' => 'fee', 'id' => $instanceid], MUST_EXIST);
return new \moodle_url('/course/view.php', ['id' => $courseid]);
}
/**
* Callback function that delivers what the user paid for to them.
*
* @param string $paymentarea
* @param int $instanceid The enrolment instance id
* @param int $paymentid payment id as inserted into the 'payments' table, if needed for reference
* @param int $userid The userid the order is going to deliver to
* @return bool Whether successful or not
*/
public static function deliver_order(string $paymentarea, int $instanceid, int $paymentid, int $userid): bool {
global $DB;
$instance = $DB->get_record('enrol', ['enrol' => 'fee', 'id' => $instanceid], '*', MUST_EXIST);
$plugin = enrol_get_plugin('fee');
if ($instance->enrolperiod) {
$timestart = time();
$timeend = $timestart + $instance->enrolperiod;
} else {
$timestart = 0;
$timeend = 0;
}
$plugin->enrol_user($instance, $userid, $instance->roleid, $timestart, $timeend);
return true;
}
}
+447
View File
@@ -0,0 +1,447 @@
<?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/>.
/**
* Fee enrolment plugin.
*
* This plugin allows you to set up paid courses.
*
* @package enrol_fee
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Fee enrolment plugin implementation.
*
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol_fee_plugin extends enrol_plugin {
/**
* Returns the list of currencies that the payment subsystem supports and therefore we can work with.
*
* @return array[currencycode => currencyname]
*/
public function get_possible_currencies(): array {
$codes = \core_payment\helper::get_supported_currencies();
$currencies = [];
foreach ($codes as $c) {
$currencies[$c] = new lang_string($c, 'core_currencies');
}
uasort($currencies, function($a, $b) {
return strcmp($a, $b);
});
return $currencies;
}
/**
* Returns optional enrolment information icons.
*
* This is used in course list for quick overview of enrolment options.
*
* We are not using single instance parameter because sometimes
* we might want to prevent icon repetition when multiple instances
* of one type exist. One instance may also produce several icons.
*
* @param array $instances all enrol instances of this type in one course
* @return array of pix_icon
*/
public function get_info_icons(array $instances) {
$found = false;
foreach ($instances as $instance) {
if ($instance->enrolstartdate != 0 && $instance->enrolstartdate > time()) {
continue;
}
if ($instance->enrolenddate != 0 && $instance->enrolenddate < time()) {
continue;
}
$found = true;
break;
}
if ($found) {
return array(new pix_icon('icon', get_string('pluginname', 'enrol_fee'), 'enrol_fee'));
}
return array();
}
public function roles_protected() {
// Users with role assign cap may tweak the roles later.
return false;
}
public function allow_unenrol(stdClass $instance) {
// Users with unenrol cap may unenrol other users manually - requires enrol/fee:unenrol.
return true;
}
public function allow_manage(stdClass $instance) {
// Users with manage cap may tweak period and status - requires enrol/fee:manage.
return true;
}
public function show_enrolme_link(stdClass $instance) {
return ($instance->status == ENROL_INSTANCE_ENABLED);
}
/**
* Returns true if the user can add a new instance in this course.
* @param int $courseid
* @return boolean
*/
public function can_add_instance($courseid) {
$context = context_course::instance($courseid, MUST_EXIST);
if (empty(\core_payment\helper::get_supported_currencies())) {
return false;
}
if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/fee:config', $context)) {
return false;
}
// Multiple instances supported - different cost for different roles.
return true;
}
/**
* We are a good plugin and don't invent our own UI/validation code path.
*
* @return boolean
*/
public function use_standard_editing_ui() {
return true;
}
/**
* Add new instance of enrol plugin.
* @param object $course
* @param array $fields instance fields
* @return int id of new instance, null if can not be created
*/
public function add_instance($course, array $fields = null) {
if ($fields && !empty($fields['cost'])) {
$fields['cost'] = unformat_float($fields['cost']);
}
return parent::add_instance($course, $fields);
}
/**
* Update instance of enrol plugin.
* @param stdClass $instance
* @param stdClass $data modified instance fields
* @return boolean
*/
public function update_instance($instance, $data) {
if ($data) {
$data->cost = unformat_float($data->cost);
}
return parent::update_instance($instance, $data);
}
/**
* Creates course enrol form, checks if form submitted
* and enrols user if necessary. It can also redirect.
*
* @param stdClass $instance
* @return string html text, usually a form in a text box
*/
public function enrol_page_hook(stdClass $instance) {
return $this->show_payment_info($instance);
}
/**
* Returns optional enrolment instance description text.
*
* This is used in detailed course information.
*
*
* @param object $instance
* @return string short html text
*/
public function get_description_text($instance) {
return $this->show_payment_info($instance);
}
/**
* Generates payment information to display on enrol/info page.
*
* @param stdClass $instance
* @return false|string
* @throws coding_exception
* @throws dml_exception
*/
private function show_payment_info(stdClass $instance) {
global $USER, $OUTPUT, $DB;
ob_start();
if ($DB->record_exists('user_enrolments', array('userid' => $USER->id, 'enrolid' => $instance->id))) {
return ob_get_clean();
}
if ($instance->enrolstartdate != 0 && $instance->enrolstartdate > time()) {
return ob_get_clean();
}
if ($instance->enrolenddate != 0 && $instance->enrolenddate < time()) {
return ob_get_clean();
}
$course = $DB->get_record('course', array('id' => $instance->courseid));
$context = context_course::instance($course->id);
if ( (float) $instance->cost <= 0 ) {
$cost = (float) $this->get_config('cost');
} else {
$cost = (float) $instance->cost;
}
if (abs($cost) < 0.01) { // No cost, other enrolment methods (instances) should be used.
echo '<p>'.get_string('nocost', 'enrol_fee').'</p>';
} else {
$data = [
'isguestuser' => isguestuser() || !isloggedin(),
'cost' => \core_payment\helper::get_cost_as_string($cost, $instance->currency),
'instanceid' => $instance->id,
'description' => get_string('purchasedescription', 'enrol_fee',
format_string($course->fullname, true, ['context' => $context])),
'successurl' => \enrol_fee\payment\service_provider::get_success_url('fee', $instance->id)->out(false),
];
echo $OUTPUT->render_from_template('enrol_fee/payment_region', $data);
}
return $OUTPUT->box(ob_get_clean());
}
/**
* Restore instance and map settings.
*
* @param restore_enrolments_structure_step $step
* @param stdClass $data
* @param stdClass $course
* @param int $oldid
*/
public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
global $DB;
if ($step->get_task()->get_target() == backup::TARGET_NEW_COURSE) {
$merge = false;
} else {
$merge = array(
'courseid' => $data->courseid,
'enrol' => $this->get_name(),
'roleid' => $data->roleid,
'cost' => $data->cost,
'currency' => $data->currency,
);
}
if ($merge and $instances = $DB->get_records('enrol', $merge, 'id')) {
$instance = reset($instances);
$instanceid = $instance->id;
} else {
$instanceid = $this->add_instance($course, (array) $data);
}
$step->set_mapping('enrol', $oldid, $instanceid);
}
/**
* Restore user enrolment.
*
* @param restore_enrolments_structure_step $step
* @param stdClass $data
* @param stdClass $instance
* @param int $oldinstancestatus
* @param int $userid
*/
public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
$this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status);
}
/**
* Return an array of valid options for the status.
*
* @return array
*/
protected function get_status_options() {
$options = array(ENROL_INSTANCE_ENABLED => get_string('yes'),
ENROL_INSTANCE_DISABLED => get_string('no'));
return $options;
}
/**
* Return an array of valid options for the roleid.
*
* @param stdClass $instance
* @param context $context
* @return array
*/
protected function get_roleid_options($instance, $context) {
if ($instance->id) {
$roles = get_default_enrol_roles($context, $instance->roleid);
} else {
$roles = get_default_enrol_roles($context, $this->get_config('roleid'));
}
return $roles;
}
/**
* Add elements to the edit instance form.
*
* @param stdClass $instance
* @param MoodleQuickForm $mform
* @param context $context
* @return bool
*/
public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
$mform->addElement('text', 'name', get_string('custominstancename', 'enrol'));
$mform->setType('name', PARAM_TEXT);
$options = $this->get_status_options();
$mform->addElement('select', 'status', get_string('status', 'enrol_fee'), $options);
$mform->setDefault('status', $this->get_config('status'));
$accounts = \core_payment\helper::get_payment_accounts_menu($context);
if ($accounts) {
$accounts = ((count($accounts) > 1) ? ['' => ''] : []) + $accounts;
$mform->addElement('select', 'customint1', get_string('paymentaccount', 'payment'), $accounts);
} else {
$mform->addElement('static', 'customint1_text', get_string('paymentaccount', 'payment'),
html_writer::span(get_string('noaccountsavilable', 'payment'), 'alert alert-danger'));
$mform->addElement('hidden', 'customint1');
$mform->setType('customint1', PARAM_INT);
}
$mform->addHelpButton('customint1', 'paymentaccount', 'enrol_fee');
$mform->addElement('text', 'cost', get_string('cost', 'enrol_fee'), array('size' => 4));
$mform->setType('cost', PARAM_RAW);
$mform->setDefault('cost', format_float($this->get_config('cost'), 2, true));
$supportedcurrencies = $this->get_possible_currencies();
$mform->addElement('select', 'currency', get_string('currency', 'enrol_fee'), $supportedcurrencies);
$mform->setDefault('currency', $this->get_config('currency'));
$roles = $this->get_roleid_options($instance, $context);
$mform->addElement('select', 'roleid', get_string('assignrole', 'enrol_fee'), $roles);
$mform->setDefault('roleid', $this->get_config('roleid'));
$options = array('optional' => true, 'defaultunit' => 86400);
$mform->addElement('duration', 'enrolperiod', get_string('enrolperiod', 'enrol_fee'), $options);
$mform->setDefault('enrolperiod', $this->get_config('enrolperiod'));
$mform->addHelpButton('enrolperiod', 'enrolperiod', 'enrol_fee');
$options = array('optional' => true);
$mform->addElement('date_time_selector', 'enrolstartdate', get_string('enrolstartdate', 'enrol_fee'), $options);
$mform->setDefault('enrolstartdate', 0);
$mform->addHelpButton('enrolstartdate', 'enrolstartdate', 'enrol_fee');
$options = array('optional' => true);
$mform->addElement('date_time_selector', 'enrolenddate', get_string('enrolenddate', 'enrol_fee'), $options);
$mform->setDefault('enrolenddate', 0);
$mform->addHelpButton('enrolenddate', 'enrolenddate', 'enrol_fee');
if (enrol_accessing_via_instance($instance)) {
$warningtext = get_string('instanceeditselfwarningtext', 'core_enrol');
$mform->addElement('static', 'selfwarn', get_string('instanceeditselfwarning', 'core_enrol'), $warningtext);
}
}
/**
* Perform custom validation of the data used to edit the instance.
*
* @param array $data array of ("fieldname"=>value) of submitted data
* @param array $files array of uploaded files "element_name"=>tmp_file_path
* @param object $instance The instance loaded from the DB
* @param context $context The context of the instance we are editing
* @return array of "element_name"=>"error_description" if there are errors,
* or an empty array if everything is OK.
* @return void
*/
public function edit_instance_validation($data, $files, $instance, $context) {
$errors = array();
if (!empty($data['enrolenddate']) and $data['enrolenddate'] < $data['enrolstartdate']) {
$errors['enrolenddate'] = get_string('enrolenddaterror', 'enrol_fee');
}
$cost = str_replace(get_string('decsep', 'langconfig'), '.', $data['cost']);
if (!is_numeric($cost)) {
$errors['cost'] = get_string('costerror', 'enrol_fee');
}
$validstatus = array_keys($this->get_status_options());
$validcurrency = array_keys($this->get_possible_currencies());
$validroles = array_keys($this->get_roleid_options($instance, $context));
$tovalidate = array(
'name' => PARAM_TEXT,
'status' => $validstatus,
'currency' => $validcurrency,
'roleid' => $validroles,
'enrolperiod' => PARAM_INT,
'enrolstartdate' => PARAM_INT,
'enrolenddate' => PARAM_INT
);
$typeerrors = $this->validate_param_types($data, $tovalidate);
$errors = array_merge($errors, $typeerrors);
if ($data['status'] == ENROL_INSTANCE_ENABLED &&
(!$data['customint1']
|| !array_key_exists($data['customint1'], \core_payment\helper::get_payment_accounts_menu($context)))) {
$errors['status'] = 'Enrolments can not be enabled without specifying the payment account';
}
return $errors;
}
/**
* Execute synchronisation.
* @param progress_trace $trace
* @return int exit code, 0 means ok
*/
public function sync(progress_trace $trace) {
$this->process_expirations($trace);
return 0;
}
/**
* Is it possible to delete enrol instance via standard UI?
*
* @param stdClass $instance
* @return bool
*/
public function can_delete_instance($instance) {
$context = context_course::instance($instance->courseid);
return has_capability('enrol/fee:config', $context);
}
/**
* Is it possible to hide/show enrol instance via standard UI?
*
* @param stdClass $instance
* @return bool
*/
public function can_hide_show_instance($instance) {
$context = context_course::instance($instance->courseid);
return has_capability('enrol/fee:config', $context);
}
}
+269
View File
@@ -0,0 +1,269 @@
<?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 enrol_fee.
*
* @package enrol_fee
* @category privacy
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_fee\privacy;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\transform;
use core_privacy\local\request\userlist;
use core_privacy\local\request\writer;
use core_payment\helper as payment_helper;
/**
* Privacy Subsystem for enrol_fee implementing null_provider.
*
* @copyright 2020 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\null_provider,
\core_payment\privacy\consumer_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';
}
public static function get_contextid_for_payment(string $paymentarea, int $itemid): ?int {
global $DB;
$sql = "SELECT ctx.id
FROM {enrol} e
JOIN {context} ctx ON (e.courseid = ctx.instanceid AND ctx.contextlevel = :contextcourse)
WHERE e.id = :enrolid AND e.enrol = :enrolname";
$params = [
'contextcourse' => CONTEXT_COURSE,
'enrolid' => $itemid,
'enrolname' => 'fee',
];
$contextid = $DB->get_field_sql($sql, $params);
return $contextid ?: null;
}
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if ($context instanceof \context_course) {
$sql = "SELECT p.userid
FROM {payments} p
JOIN {enrol} e ON (p.component = :component AND p.itemid = e.id)
WHERE e.courseid = :courseid";
$params = [
'component' => 'enrol_fee',
'courseid' => $context->instanceid,
];
$userlist->add_from_sql('userid', $sql, $params);
} else if ($context instanceof \context_system) {
// If context is system, then the enrolment belongs to a deleted enrolment.
$sql = "SELECT p.userid
FROM {payments} p
LEFT JOIN {enrol} e ON p.itemid = e.id
WHERE p.component = :component AND e.id IS NULL";
$params = [
'component' => 'enrol_fee',
];
$userlist->add_from_sql('userid', $sql, $params);
}
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
$subcontext = [
get_string('pluginname', 'enrol_fee'),
];
foreach ($contextlist as $context) {
if (!$context instanceof \context_course) {
continue;
}
$feeplugins = $DB->get_records('enrol', ['courseid' => $context->instanceid, 'enrol' => 'fee']);
foreach ($feeplugins as $feeplugin) {
\core_payment\privacy\provider::export_payment_data_for_user_in_context(
$context,
$subcontext,
$contextlist->get_user()->id,
'enrol_fee',
'fee',
$feeplugin->id
);
}
}
if (in_array(SYSCONTEXTID, $contextlist->get_contextids())) {
// Orphaned payments.
$sql = "SELECT p.*
FROM {payments} p
LEFT JOIN {enrol} e ON p.itemid = e.id
WHERE p.userid = :userid AND p.component = :component AND e.id IS NULL";
$params = [
'component' => 'enrol_fee',
'userid' => $contextlist->get_user()->id,
];
$orphanedpayments = $DB->get_recordset_sql($sql, $params);
foreach ($orphanedpayments as $payment) {
\core_payment\privacy\provider::export_payment_data_for_user_in_context(
\context_system::instance(),
$subcontext,
$payment->userid,
$payment->component,
$payment->paymentarea,
$payment->itemid
);
}
$orphanedpayments->close();
}
}
/**
* Delete all data for all users in the specified context.
*
* @param \context $context The specific context to delete data for.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
if ($context instanceof \context_course) {
$sql = "SELECT p.id
FROM {payments} p
JOIN {enrol} e ON (p.component = :component AND p.itemid = e.id)
WHERE e.courseid = :courseid";
$params = [
'component' => 'enrol_fee',
'courseid' => $context->instanceid,
];
\core_payment\privacy\provider::delete_data_for_payment_sql($sql, $params);
} else if ($context instanceof \context_system) {
// If context is system, then the enrolment belongs to a deleted enrolment.
$sql = "SELECT p.id
FROM {payments} p
LEFT JOIN {enrol} e ON p.itemid = e.id
WHERE p.component = :component AND e.id IS NULL";
$params = [
'component' => 'enrol_fee',
];
\core_payment\privacy\provider::delete_data_for_payment_sql($sql, $params);
}
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
global $DB;
if (empty($contextlist->count())) {
return;
}
$contexts = $contextlist->get_contexts();
$courseids = [];
foreach ($contexts as $context) {
if ($context instanceof \context_course) {
$courseids[] = $context->instanceid;
}
}
[$insql, $inparams] = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED);
$sql = "SELECT p.id
FROM {payments} p
JOIN {enrol} e ON (p.component = :component AND p.itemid = e.id)
WHERE p.userid = :userid AND e.courseid $insql";
$params = $inparams + [
'component' => 'enrol_fee',
'userid' => $contextlist->get_user()->id,
];
\core_payment\privacy\provider::delete_data_for_payment_sql($sql, $params);
if (in_array(SYSCONTEXTID, $contextlist->get_contextids())) {
// Orphaned payments.
$sql = "SELECT p.id
FROM {payments} p
LEFT JOIN {enrol} e ON p.itemid = e.id
WHERE p.component = :component AND p.userid = :userid AND e.id IS NULL";
$params = [
'component' => 'enrol_fee',
'userid' => $contextlist->get_user()->id,
];
\core_payment\privacy\provider::delete_data_for_payment_sql($sql, $params);
}
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
global $DB;
$context = $userlist->get_context();
if ($context instanceof \context_course) {
[$usersql, $userparams] = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
$sql = "SELECT p.id
FROM {payments} p
JOIN {enrol} e ON (p.component = :component AND p.itemid = e.id)
WHERE e.courseid = :courseid AND p.userid $usersql";
$params = $userparams + [
'component' => 'enrol_fee',
'courseid' => $context->instanceid,
];
\core_payment\privacy\provider::delete_data_for_payment_sql($sql, $params);
} else if ($context instanceof \context_system) {
// Orphaned payments.
[$usersql, $userparams] = $DB->get_in_or_equal($userlist->get_userids(), SQL_PARAMS_NAMED);
$sql = "SELECT p.id
FROM {payments} p
LEFT JOIN {enrol} e ON p.itemid = e.id
WHERE p.component = :component AND p.userid $usersql AND e.id IS NULL";
$params = $userparams + [
'component' => 'enrol_fee',
];
\core_payment\privacy\provider::delete_data_for_payment_sql($sql, $params);
}
}
}
+61
View File
@@ -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/>.
/**
* Capabilities for fee enrolment plugin.
*
* @package enrol_fee
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
'enrol/fee:config' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'manager' => CAP_ALLOW,
)
),
'enrol/fee:manage' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'manager' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
)
),
'enrol/fee:unenrol' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'manager' => CAP_ALLOW,
)
),
'enrol/fee:unenrolself' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
)
),
);
+54
View File
@@ -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/>.
/**
* Strings for component 'enrol_fee', language 'en'
*
* @package enrol_fee
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['assignrole'] = 'Assign role';
$string['cost'] = 'Enrolment fee';
$string['costerror'] = 'The enrolment fee must be a number.';
$string['currency'] = 'Currency';
$string['defaultrole'] = 'Default role assignment';
$string['defaultrole_desc'] = 'Select the role to assign to users after making a payment.';
$string['enrolenddate'] = 'End date';
$string['enrolenddate_help'] = 'If enabled, users can be enrolled until this date only.';
$string['enrolenddaterror'] = 'The enrolment end date cannot be earlier than the start date.';
$string['enrolperiod'] = 'Enrolment duration';
$string['enrolperiod_desc'] = 'Default length of time that the enrolment is valid. If set to zero, the enrolment duration will be unlimited by default.';
$string['enrolperiod_help'] = 'Length of time that the enrolment is valid, starting with the moment the user is enrolled. If disabled, the enrolment duration will be unlimited.';
$string['enrolstartdate'] = 'Start date';
$string['enrolstartdate_help'] = 'If enabled, users can only be enrolled from this date onwards.';
$string['expiredaction'] = 'Enrolment expiry action';
$string['expiredaction_help'] = 'Select the action to be performed when a user\'s enrolment expires. Please note that some user data and settings are deleted when a user is unenrolled.';
$string['fee:config'] = 'Configure enrolment on payment enrol instances';
$string['fee:manage'] = 'Manage enrolled users';
$string['fee:unenrol'] = 'Unenrol users from course';
$string['fee:unenrolself'] = 'Unenrol self from course';
$string['nocost'] = 'There is no cost to enrol in this course!';
$string['paymentaccount'] = 'Payment account';
$string['paymentaccount_help'] = 'Enrolment fees will be paid to this account.';
$string['pluginname'] = 'Enrolment on payment';
$string['pluginname_desc'] = 'The enrolment on payment enrolment method allows you to set up courses requiring a payment. If the fee for any course is set to zero, then students are not asked to pay for entry. There is a site-wide fee that you set here as a default for the whole site and then a course setting that you can set for each course individually. The course fee overrides the site fee.';
$string['privacy:metadata'] = 'The enrolment on payment enrolment plugin does not store any personal data.';
$string['purchasedescription'] = 'Enrolment in course {$a}';
$string['sendpaymentbutton'] = 'Select payment type';
$string['status'] = 'Allow enrolment on payment enrolments';
$string['status_desc'] = 'Allow users to make a payment to enrol into a course by default.';
+25
View File
@@ -0,0 +1,25 @@
<?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/>.
/**
* Fee enrolment plugin.
*
* This plugin allows you to set up paid courses.
*
* @package enrol_fee
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+78
View File
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 32.001 32.001" style="enable-background:new 0 0 32.001 32.001;" xml:space="preserve" preserveAspectRatio="xMinYMid meet">
<g>
<g id="banknote">
<path style="fill:#010002;" d="M31.415,10.586l-10-10C20.919,0.09,20.2-0.109,19.519,0.059c-0.359,0.088-0.68,0.273-0.934,0.527
c-0.227,0.227-0.398,0.508-0.496,0.822c-0.453,1.469-1.236,2.746-2.395,3.904C14.146,6.859,12.13,8.031,9.997,9.27
c-2.264,1.312-4.603,2.672-6.52,4.588c-1.629,1.631-2.738,3.445-3.388,5.551c-0.219,0.711-0.028,1.48,0.496,2.006l10,10
c0.496,0.496,1.215,0.695,1.896,0.527c0.359-0.09,0.68-0.273,0.934-0.527c0.227-0.227,0.398-0.508,0.496-0.824
c0.454-1.469,1.237-2.746,2.397-3.904c1.547-1.547,3.562-2.717,5.697-3.955c2.262-1.314,4.602-2.674,6.518-4.59
c1.629-1.629,2.738-3.445,3.389-5.551C32.13,11.881,31.938,11.109,31.415,10.586z M12,30c-3.312-3.312-6.688-6.689-10-10
c2.842-9.201,15.16-8.799,18-18c3.312,3.311,6.689,6.688,10.002,10C27.159,21.199,14.841,20.799,12,30z"/>
<path style="fill:#010002;" d="M19.562,14.9c-0.326-0.273-0.654-0.459-0.984-0.551c-0.328-0.092-0.656-0.129-0.988-0.105
c-0.328,0.025-0.664,0.1-1,0.229c-0.336,0.131-0.674,0.273-1.014,0.438c-0.537-0.617-1.074-1.227-1.611-1.793
c0.242-0.219,0.477-0.33,0.703-0.338c0.227-0.01,0.445,0.014,0.652,0.066c0.211,0.053,0.404,0.098,0.582,0.133
c0.18,0.035,0.336-0.004,0.473-0.119c0.145-0.125,0.225-0.287,0.236-0.482c0.01-0.197-0.064-0.389-0.229-0.576
c-0.211-0.242-0.465-0.389-0.77-0.443c-0.301-0.053-0.609-0.049-0.93,0.021c-0.316,0.072-0.617,0.191-0.902,0.359
s-0.514,0.34-0.684,0.508c-0.065-0.062-0.13-0.123-0.195-0.184c-0.072-0.066-0.162-0.102-0.27-0.1
c-0.109,0-0.199,0.047-0.273,0.133c-0.072,0.084-0.105,0.182-0.092,0.285c0.01,0.107,0.053,0.189,0.127,0.252
c0.065,0.055,0.13,0.109,0.195,0.166c-0.256,0.309-0.467,0.65-0.633,1.01c-0.168,0.361-0.268,0.719-0.305,1.066
c-0.039,0.35-0.002,0.67,0.105,0.967c0.107,0.299,0.305,0.553,0.594,0.793c0.471,0.391,1.025,0.557,1.668,0.52
c0.641-0.039,1.332-0.23,2.075-0.629c0.59,0.682,1.182,1.359,1.773,1.988c-0.25,0.211-0.469,0.332-0.662,0.371
c-0.193,0.041-0.365,0.037-0.521-0.01c-0.156-0.049-0.301-0.119-0.434-0.209c-0.133-0.092-0.264-0.17-0.395-0.234
c-0.129-0.064-0.262-0.1-0.398-0.102s-0.281,0.064-0.441,0.201c-0.164,0.143-0.246,0.309-0.246,0.496
c0,0.186,0.086,0.375,0.254,0.566c0.17,0.191,0.391,0.352,0.658,0.479s0.569,0.207,0.901,0.229
c0.332,0.023,0.682-0.027,1.051-0.164c0.371-0.135,0.738-0.379,1.1-0.742c0.174,0.17,0.35,0.332,0.525,0.488
c0.074,0.064,0.164,0.096,0.273,0.088c0.105-0.004,0.197-0.053,0.27-0.141c0.074-0.09,0.105-0.189,0.094-0.293
c-0.01-0.105-0.053-0.186-0.125-0.244c-0.176-0.141-0.352-0.289-0.527-0.445c0.299-0.367,0.539-0.754,0.717-1.137
c0.178-0.385,0.283-0.756,0.318-1.1c0.035-0.346-0.006-0.658-0.119-0.941C20.046,15.383,19.847,15.137,19.562,14.9z
M13.971,15.578c-0.283,0.012-0.53-0.082-0.746-0.291c-0.092-0.088-0.156-0.195-0.195-0.322c-0.041-0.127-0.055-0.266-0.039-0.418
c0.014-0.15,0.059-0.307,0.137-0.465c0.074-0.158,0.184-0.316,0.324-0.469c0.507,0.504,1.013,1.057,1.52,1.629
C14.588,15.453,14.254,15.566,13.971,15.578z M18.688,17.58c-0.09,0.166-0.193,0.314-0.314,0.443
c-0.561-0.566-1.121-1.188-1.68-1.826c0.143-0.064,0.293-0.131,0.455-0.199s0.324-0.113,0.486-0.141
c0.166-0.025,0.33-0.018,0.494,0.021c0.162,0.041,0.316,0.129,0.459,0.268c0.141,0.141,0.23,0.287,0.266,0.445
c0.039,0.16,0.041,0.322,0.014,0.488C18.839,17.246,18.78,17.412,18.688,17.58z"/>
<path style="fill:#010002;" d="M14.717,22.18h-0.002c-0.625,0.48-1.235,0.986-1.8,1.553c-0.543,0.543-1.034,1.115-1.461,1.699
l-0.684,0.934l0.002,0.002c-0.125,0.195-0.104,0.457,0.066,0.627c0.195,0.195,0.514,0.195,0.71,0
c0.03-0.031,0.054-0.064,0.074-0.1l0.639-0.875c0.396-0.541,0.854-1.074,1.361-1.58c0.561-0.561,1.081-0.994,1.714-1.475
l-0.002-0.002c0.018-0.014,0.035-0.025,0.051-0.041c0.196-0.195,0.196-0.514,0-0.709C15.204,22.029,14.914,22.021,14.717,22.18z"
/>
<path style="fill:#010002;" d="M18.378,7.715c-0.523,0.523-1.09,0.994-1.678,1.443c-0.041,0.023-0.082,0.049-0.117,0.086
c-0.197,0.197-0.197,0.518,0,0.715c0.191,0.193,0.5,0.197,0.699,0.014l0.004,0.002c0.625-0.48,1.234-0.988,1.799-1.553
c0.543-0.543,1.033-1.113,1.461-1.697l0.684-0.938l-0.002-0.002c0.17-0.199,0.162-0.496-0.025-0.684
c-0.197-0.197-0.516-0.197-0.713,0c-0.037,0.037-0.064,0.08-0.088,0.123l-0.664,0.91C19.341,6.678,18.884,7.209,18.378,7.715z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

+79
View File
@@ -0,0 +1,79 @@
<?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/>.
/**
* Settings for the Fee enrolment plugin
*
* @package enrol_fee
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
if ($ADMIN->fulltree) {
$currencies = enrol_get_plugin('fee')->get_possible_currencies();
if (empty($currencies)) {
$notify = new \core\output\notification(
get_string('nocurrencysupported', 'core_payment'),
\core\output\notification::NOTIFY_WARNING
);
$settings->add(new admin_setting_heading('enrol_fee_nocurrency', '', $OUTPUT->render($notify)));
}
$settings->add(new admin_setting_heading('enrol_fee_settings', '', get_string('pluginname_desc', 'enrol_fee')));
// Note: let's reuse the ext sync constants and strings here, internally it is very similar,
// it describes what should happen when users are not supposed to be enrolled any more.
$options = array(
ENROL_EXT_REMOVED_KEEP => get_string('extremovedkeep', 'enrol'),
ENROL_EXT_REMOVED_SUSPENDNOROLES => get_string('extremovedsuspendnoroles', 'enrol'),
ENROL_EXT_REMOVED_UNENROL => get_string('extremovedunenrol', 'enrol'),
);
$settings->add(new admin_setting_configselect(
'enrol_fee/expiredaction',
get_string('expiredaction', 'enrol_fee'),
get_string('expiredaction_help', 'enrol_fee'),
ENROL_EXT_REMOVED_SUSPENDNOROLES,
$options));
$settings->add(new admin_setting_heading('enrol_fee_defaults',
get_string('enrolinstancedefaults', 'admin'), get_string('enrolinstancedefaults_desc', 'admin')));
$options = array(ENROL_INSTANCE_ENABLED => get_string('yes'),
ENROL_INSTANCE_DISABLED => get_string('no'));
$settings->add(new admin_setting_configselect('enrol_fee/status',
get_string('status', 'enrol_fee'), get_string('status_desc', 'enrol_fee'), ENROL_INSTANCE_DISABLED, $options));
if (!empty($currencies)) {
$settings->add(new admin_setting_configtext('enrol_fee/cost', get_string('cost', 'enrol_fee'), '', 0, PARAM_FLOAT, 4));
$settings->add(new admin_setting_configselect('enrol_fee/currency', get_string('currency', 'enrol_fee'), '', 'USD',
$currencies));
}
if (!during_initial_install()) {
$options = get_default_enrol_roles(context_system::instance());
$student = get_archetype_roles('student');
$student = reset($student);
$settings->add(new admin_setting_configselect('enrol_fee/roleid',
get_string('defaultrole', 'enrol_fee'), get_string('defaultrole_desc', 'enrol_fee'), $student->id ?? null, $options));
}
$settings->add(new admin_setting_configduration('enrol_fee/enrolperiod',
get_string('enrolperiod', 'enrol_fee'), get_string('enrolperiod_desc', 'enrol_fee'), 0));
}
@@ -0,0 +1,80 @@
{{!
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 enrol_fee/payment_region
This template will render information about course fee along with a button for payment.
Classes required for JS:
* none
Data attributes required for JS:
* data-component
* data-paymentarea
* data-itemid
* data-cost
* data-description
* data-successurl
Context variables required for this template:
* cost - Human readable cost string including amount and currency
* instanceid - Id of the enrolment instance
* description - The description for this purchase
* successurl - The URL of the course
Example context (json):
{
"cost": "$108.50",
"instanceid": 11,
"description": "Enrolment in course Introduction to algorithms",
"successurl": "https://moodlesite/course/view.php?id=2",
"isguestuser": false
}
}}
<div class="enrol_fee_payment_region text-center">
{{#isguestuser}}
<div class="mdl-align">
<p>{{# str }} paymentrequired {{/ str}}</p>
<p><b>{{cost}}</b></p>
<p><a href="{{config.wwwroot}}/login/">{{# str }} loginsite {{/ str }}</a></p>
</div>
{{/isguestuser}}
{{^isguestuser}}
<p>{{# str }} paymentrequired {{/ str}}</p>
<p><b>{{cost}}</b></p>
<button
class="btn btn-secondary"
type="button"
id="gateways-modal-trigger-{{ uniqid }}"
data-action="core_payment/triggerPayment"
data-component="enrol_fee"
data-paymentarea="fee"
data-itemid="{{instanceid}}"
data-cost="{{cost}}"
data-successurl="{{successurl}}"
data-description="{{description}}"
>
{{# str }} sendpaymentbutton, enrol_fee {{/ str }}
</button>
{{/isguestuser}}
</div>
{{#js}}
require(['core_payment/gateways_modal'], function(modal) {
modal.init();
});
{{/js}}
+51
View File
@@ -0,0 +1,51 @@
@enrol @enrol_fee
Feature: Signing up for a course with a fee enrolment method
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| manager1 | Manager | 1 | manager1@example.com |
And the following "courses" exist:
| fullname | shortname | format | summary |
| Course 1 | C1 | topics | |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| manager1 | C1 | manager |
And the following "core_payment > payment accounts" exist:
| name | gateways |
| Account1 | paypal |
And I log in as "admin"
And I navigate to "Plugins > Enrolments > Manage enrol plugins" in site administration
And I click on "Enable" "link" in the "Enrolment on payment" "table_row"
And I log out
And I log in as "manager1"
And I am on the "Course 1" "enrolment methods" page
And I select "Enrolment on payment" from the "Add method" singleselect
And I set the following fields to these values:
| Payment account | Account1 |
| Enrolment fee | 123.45 |
| Currency | Euro |
And I press "Add method"
And I log out
@javascript
Scenario: Student can see the payment prompt on the course enrolment page
When I log in as "student1"
And I am on course index
And I follow "Course 1"
Then I should see "This course requires a payment for entry."
And I should see "123.45"
And I press "Select payment type"
And I should see "PayPal" in the "Select payment type" "dialogue"
And I click on "Cancel" "button" in the "Select payment type" "dialogue"
Scenario: Guest can see the login prompt on the course enrolment page
When I log in as "guest"
And I am on course index
And I follow "Course 1"
Then I should see "This course requires a payment for entry."
And I should see "123.45"
And I should see "Log in to the site"
@@ -0,0 +1,133 @@
<?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/>.
/**
* Unit tests for the enrol_fee's payment subsystem callback implementation.
*
* @package enrol_fee
* @category test
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_fee\payment;
/**
* Unit tests for the enrol_fee's payment subsystem callback implementation.
*
* @coversDefaultClass \enrol_fee\payment\service_provider
*/
class service_provider_test extends \advanced_testcase {
/**
* Test for service_provider::get_payable().
*
* @covers ::get_payable
*/
public function test_get_payable(): void {
global $DB;
$this->resetAfterTest();
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
$feeplugin = enrol_get_plugin('fee');
$generator = $this->getDataGenerator();
$account = $generator->get_plugin_generator('core_payment')->create_payment_account(['gateways' => 'paypal']);
$course = $generator->create_course();
$data = [
'courseid' => $course->id,
'customint1' => $account->get('id'),
'cost' => 250,
'currency' => 'USD',
'roleid' => $studentrole->id,
];
$id = $feeplugin->add_instance($course, $data);
$payable = service_provider::get_payable('fee', $id);
$this->assertEquals($account->get('id'), $payable->get_account_id());
$this->assertEquals(250, $payable->get_amount());
$this->assertEquals('USD', $payable->get_currency());
}
/**
* Test for service_provider::get_success_url().
*
* @covers ::get_success_url
*/
public function test_get_success_url(): void {
global $CFG, $DB;
$this->resetAfterTest();
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
$feeplugin = enrol_get_plugin('fee');
$generator = $this->getDataGenerator();
$account = $generator->get_plugin_generator('core_payment')->create_payment_account(['gateways' => 'paypal']);
$course = $generator->create_course();
$data = [
'courseid' => $course->id,
'customint1' => $account->get('id'),
'cost' => 250,
'currency' => 'USD',
'roleid' => $studentrole->id,
];
$id = $feeplugin->add_instance($course, $data);
$successurl = service_provider::get_success_url('fee', $id);
$this->assertEquals(
$CFG->wwwroot . '/course/view.php?id=' . $course->id,
$successurl->out(false)
);
}
/**
* Test for service_provider::deliver_order().
*
* @covers ::deliver_order
*/
public function test_deliver_order(): void {
global $DB;
$this->resetAfterTest();
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
$feeplugin = enrol_get_plugin('fee');
$generator = $this->getDataGenerator();
$account = $generator->get_plugin_generator('core_payment')->create_payment_account(['gateways' => 'paypal']);
$course = $generator->create_course();
$context = \context_course::instance($course->id);
$user = $generator->create_user();
$data = [
'courseid' => $course->id,
'customint1' => $account->get('id'),
'cost' => 250,
'currency' => 'USD',
'roleid' => $studentrole->id,
];
$id = $feeplugin->add_instance($course, $data);
$paymentid = $generator->get_plugin_generator('core_payment')->create_payment([
'accountid' => $account->get('id'),
'amount' => 10,
'userid' => $user->id
]);
service_provider::deliver_order('fee', $id, $paymentid, $user->id);
$this->assertTrue(is_enrolled($context, $user));
$this->assertTrue(user_has_role_assignment($user->id, $studentrole->id, $context->id));
}
}
+29
View File
@@ -0,0 +1,29 @@
<?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/>.
/**
* Fee enrolment plugin version specification.
*
* @package enrol_fee
* @copyright 2019 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'enrol_fee'; // Full name of the plugin (used for diagnostics).
+57
View File
@@ -0,0 +1,57 @@
<?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/>.
/**
* Special flatfile settings.
*
* @package enrol_flatfile
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once("$CFG->libdir/adminlib.php");
/**
* Setting class that stores only non-empty values.
*/
class enrol_flatfile_role_setting extends admin_setting_configtext {
public function __construct($role) {
parent::__construct('enrol_flatfile/map_'.$role->id, $role->localname, '', $role->shortname);
}
public function config_read($name) {
$value = parent::config_read($name);
if (is_null($value)) {
// In other settings NULL means we have to ask user for new value,
// here we just ignore missing role mappings.
$value = '';
}
return $value;
}
public function config_write($name, $value) {
if ($value === '') {
// We do not want empty values in config table,
// delete it instead.
$value = null;
}
return parent::config_write($name, $value);
}
}
+229
View File
@@ -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/>.
/**
* Privacy Subsystem implementation for enrol_flatfile.
*
* @package enrol_flatfile
* @category privacy
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_flatfile\privacy;
use core_privacy\local\metadata\collection;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\context;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\userlist;
use core_privacy\local\request\writer;
use core_privacy\local\request\transform;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for enrol_flatfile implementing null_provider.
*
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\plugin\provider,
\core_privacy\local\request\core_userlist_provider {
/**
* Returns meta data about this system.
*
* @param collection $collection The initialised collection to add items to.
* @return collection A listing of user data stored through this system.
*/
public static function get_metadata(collection $collection): collection {
return $collection->add_database_table('enrol_flatfile', [
'action' => 'privacy:metadata:enrol_flatfile:action',
'roleid' => 'privacy:metadata:enrol_flatfile:roleid',
'userid' => 'privacy:metadata:enrol_flatfile:userid',
'courseid' => 'privacy:metadata:enrol_flatfile:courseid',
'timestart' => 'privacy:metadata:enrol_flatfile:timestart',
'timeend' => 'privacy:metadata:enrol_flatfile:timeend',
'timemodified' => 'privacy:metadata:enrol_flatfile:timemodified'
], 'privacy:metadata:enrol_flatfile');
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid The user to search.
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$sql = "SELECT c.id
FROM {enrol_flatfile} ef
JOIN {context} c ON c.contextlevel = ? AND c.instanceid = ef.courseid
WHERE ef.userid = ?";
$params = [CONTEXT_COURSE, $userid];
$contextlist = new contextlist();
$contextlist->set_component('enrol_flatfile');
return $contextlist->add_from_sql($sql, $params);
}
/**
* Get the list of users who have data within a context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if ($context->contextlevel != CONTEXT_COURSE) {
return;
}
$sql = "SELECT userid FROM {enrol_flatfile} WHERE courseid = ?";
$params = [$context->instanceid];
$userlist->add_from_sql('userid', $sql, $params);
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
global $DB;
// Ensure all contexts are CONTEXT_COURSE.
$contexts = static::validate_contextlist_contexts($contextlist);
if (empty($contexts)) {
return;
}
// Get the context instance ids from the contexts. These are the course ids..
$contextinstanceids = array_map(function($context) {
return $context->instanceid;
}, $contexts);
$userid = $contextlist->get_user()->id;
// Now, we just need to fetch and format all entries corresponding to the contextids provided.
$sql = "SELECT ef.action, r.shortname, ef.courseid, ef.timestart, ef.timeend, ef.timemodified
FROM {enrol_flatfile} ef
JOIN {context} c ON c.contextlevel = :contextlevel AND c.instanceid = ef.courseid
JOIN {role} r ON r.id = ef.roleid
WHERE ef.userid = :userid";
$params = ['contextlevel' => CONTEXT_COURSE, 'userid' => $userid];
list($insql, $inparams) = $DB->get_in_or_equal($contextinstanceids, SQL_PARAMS_NAMED);
$sql .= " AND ef.courseid $insql";
$params = array_merge($params, $inparams);
$futureenrolments = $DB->get_recordset_sql($sql, $params);
$enrolmentdata = [];
foreach ($futureenrolments as $futureenrolment) {
// It's possible to have more than one future enrolment per course.
$futureenrolment->timestart = transform::datetime($futureenrolment->timestart);
$futureenrolment->timeend = transform::datetime($futureenrolment->timeend);
$futureenrolment->timemodified = transform::datetime($futureenrolment->timemodified);
$enrolmentdata[$futureenrolment->courseid][] = $futureenrolment;
}
$futureenrolments->close();
// And finally, write out the data to the relevant course contexts.
$subcontext = \core_enrol\privacy\provider::get_subcontext([get_string('pluginname', 'enrol_flatfile')]);
foreach ($enrolmentdata as $courseid => $enrolments) {
$data = (object) [
'pendingenrolments' => $enrolments,
];
writer::with_context(\context_course::instance($courseid))->export_data($subcontext, $data);
}
}
/**
* Delete all data for all users in the specified context.
*
* @param \context $context The specific context to delete data for.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
if ($context->contextlevel != CONTEXT_COURSE) {
return;
}
global $DB;
$DB->delete_records('enrol_flatfile', ['courseid' => $context->instanceid]);
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
// Only delete data from contexts which are at the CONTEXT_COURSE contextlevel.
$contexts = self::validate_contextlist_contexts($contextlist);
if (empty($contexts)) {
return;
}
// Get the course ids based on the provided contexts.
$contextinstanceids = array_map(function($context) {
return $context->instanceid;
}, $contextlist->get_contexts());
global $DB;
$user = $contextlist->get_user();
list($insql, $inparams) = $DB->get_in_or_equal($contextinstanceids, SQL_PARAMS_NAMED);
$params = array_merge(['userid' => $user->id], $inparams);
$sql = "userid = :userid AND courseid $insql";
$DB->delete_records_select('enrol_flatfile', $sql, $params);
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
global $DB;
$context = $userlist->get_context();
if ($context->contextlevel != CONTEXT_COURSE) {
return;
}
$userids = $userlist->get_userids();
list($insql, $inparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
$params = array_merge(['courseid' => $context->instanceid], $inparams);
$sql = "courseid = :courseid AND userid $insql";
$DB->delete_records_select('enrol_flatfile', $sql, $params);
}
/**
* Simple sanity check on the contextlist contexts, making sure they're of CONTEXT_COURSE contextlevel.
*
* @param approved_contextlist $contextlist
* @return array the array of contexts filtered to only include those of CONTEXT_COURSE contextlevel.
*/
protected static function validate_contextlist_contexts(approved_contextlist $contextlist) {
return array_reduce($contextlist->get_contexts(), function($carry, $context) {
if ($context->contextlevel == CONTEXT_COURSE) {
$carry[] = $context;
}
return $carry;
}, []);
}
}
@@ -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/>.
/**
* Scheduled task for processing flatfile enrolments.
*
* @package enrol_flatfile
* @copyright 2014 Troy Williams
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_flatfile\task;
defined('MOODLE_INTERNAL') || die;
/**
* Simple task to run sync enrolments.
*
* @copyright 2014 Troy Williams
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class flatfile_sync_task extends \core\task\scheduled_task {
/**
* Get a descriptive name for this task (shown to admins).
*
* @return string
*/
public function get_name() {
return get_string('flatfilesync', 'enrol_flatfile');
}
/**
* Do the job.
* Throw exceptions on errors (the job will be retried).
*/
public function execute() {
global $CFG;
require_once($CFG->dirroot . '/enrol/flatfile/lib.php');
if (!enrol_is_enabled('flatfile')) {
return;
}
// Instance of enrol_flatfile_plugin.
$plugin = enrol_get_plugin('flatfile');
$result = $plugin->sync(new \null_progress_trace());
return $result;
}
}
+89
View File
@@ -0,0 +1,89 @@
<?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/>.
/**
* Flatfile CLI tool.
*
* Notes:
* - it is required to use the web server account when executing PHP CLI scripts
* - you need to change the "www-data" to match the apache user account
* - use "su" if "sudo" not available
*
* Update
*
* This plugin now has a enrolment sync scheduled task. Scheduled tasks were
* introduced in Moodle 2.7. It is possible to override the scheduled tasks
* configuration and run a single scheduled task immediately using the
* admin/cli/scheduled_task.php script. This is the recommended
* method to use for immediate enrollment synchronisation.
*
* Usage help:
* $ php admin/cli/scheduled_task.php -h
*
* Execute task:
* $ sudo -u www-data /usr/bin/php admin/cli/scheduled_task.php /
* --execute=\\enrol_flatfile\\task\\flatfile_sync_task
*
* @package enrol_flatfile
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__.'/../../../config.php');
require_once("$CFG->libdir/clilib.php");
// Now get cli options.
list($options, $unrecognized) = cli_get_params(array('verbose'=>false, 'help'=>false), array('v'=>'verbose', 'h'=>'help'));
if ($unrecognized) {
$unrecognized = implode("\n ", $unrecognized);
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
}
if ($options['help']) {
$help =
"Process flat file enrolments, update expiration and send notifications.
Options:
-v, --verbose Print verbose progress information
-h, --help Print out this help
Example:
\$ sudo -u www-data /usr/bin/php enrol/flatfile/cli/sync.php
";
echo $help;
die;
}
if (!enrol_is_enabled('flatfile')) {
cli_error('enrol_flatfile plugin is disabled, synchronisation stopped', 2);
}
/** @var $plugin enrol_flatfile_plugin */
$plugin = enrol_get_plugin('flatfile');
if (empty($options['verbose'])) {
$trace = new null_progress_trace();
} else {
$trace = new text_progress_trace();
}
$result = $plugin->sync($trace);
exit($result);
+43
View File
@@ -0,0 +1,43 @@
<?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/>.
/**
* Capabilities for manual enrolment plugin.
*
* @package enrol_flatfile
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
/* Manage enrolments of users - requires allowmodifications enabled. */
'enrol/flatfile:manage' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
)
),
/* Unenrol anybody (including self) - requires allowmodifications enabled */
'enrol/flatfile:unenrol' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
)
),
);
+35
View File
@@ -0,0 +1,35 @@
<?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/>.
/**
* Flatfile enrolment plugin installation.
*
* @package enrol_flatfile
* @copyright 2013 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
function xmldb_enrol_flatfile_install() {
global $CFG, $DB;
// Flatfile role mappings are empty by default now.
$roles = get_all_roles();
foreach ($roles as $role) {
set_config('map_'.$role->id, $role->shortname, 'enrol_flatfile');
}
}
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="enrol/flatfile/db" VERSION="20120122" COMMENT="XMLDB file for Moodle enrol/flatfile"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="enrol_flatfile" COMMENT="enrol_flatfile table retrofitted from MySQL">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="action" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="roleid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="timestart" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timeend" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="id" TYPE="primary" FIELDS="id" COMMENT="primary key"/>
<KEY NAME="courseid-id" TYPE="foreign" FIELDS="courseid" REFTABLE="course" REFFIELDS="id"/>
<KEY NAME="userid-id" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
<KEY NAME="roleid-id" TYPE="foreign" FIELDS="roleid" REFTABLE="role" REFFIELDS="id"/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
+34
View File
@@ -0,0 +1,34 @@
<?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/>.
/**
* Defines message providers (types of message sent) for the PayPal enrolment plugin.
*
* @package enrol_flatfile
* @copyright 2012 Andrew Davis
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$messageproviders = array(
'flatfile_enrolment' => [
'defaults' => [
'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED,
],
],
);
+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/>.
/**
* Definition of flatfile enrolment scheduled tasks.
*
* @package enrol_flatfile
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$tasks = array(
array(
'classname' => '\enrol_flatfile\task\flatfile_sync_task',
'blocking' => 0,
'minute' => '15',
'hour' => '*',
'day' => '*',
'dayofweek' => '*',
'month' => '*'
)
);
+39
View File
@@ -0,0 +1,39 @@
<?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/>.
/**
* Keeps track of upgrades to the enrol_flatfile plugin
*
* @package enrol_flatfile
* @copyright 2010 Aparup Banerjee <aparup@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function xmldb_enrol_flatfile_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+6
View File
@@ -0,0 +1,6 @@
add, student, 5, CF101
add, teacher, 6, CF101
add, teacheredit, 7, CF101
del, student, 8, CF101
del, student, 17, CF101
add, student, 21, CF101, 1091115000, 1091215000
+72
View File
@@ -0,0 +1,72 @@
<?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 component 'enrol_flatfile', language 'en'.
*
* @package enrol_flatfile
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['encoding'] = 'File encoding';
$string['expiredaction'] = 'Enrolment expiry action';
$string['expiredaction_help'] = 'Select action to carry out when user enrolment expires. Please note that some user data and settings are purged from course during course unenrolment.';
$string['filelockedmail'] = 'The text file you are using for file-based enrolments ({$a}) can not be deleted by the cron process. This usually means the permissions are wrong on it. Please fix the permissions so that Moodle can delete the file, otherwise it might be processed repeatedly.';
$string['filelockedmailsubject'] = 'Important error: Enrolment file';
$string['flatfile:manage'] = 'Manage user enrolments manually';
$string['flatfile:unenrol'] = 'Unenrol users from the course manually';
$string['flatfileenrolments'] = 'Flat file (CSV) enrolments';
$string['flatfilesync'] = 'Flat file enrolment sync';
$string['location'] = 'File location';
$string['location_desc'] = 'Specify full path to the enrolment file. The file is automatically deleted after processing.';
$string['notifyadmin'] = 'Notify administrator';
$string['notifyenrolled'] = 'Notify enrolled users';
$string['notifyenroller'] = 'Notify user responsible for enrolments';
$string['messageprovider:flatfile_enrolment'] = 'Flat file enrolment messages';
$string['mapping'] = 'Flat file role mapping';
$string['pluginname'] = 'Flat file (CSV)';
$string['pluginname_desc'] = 'This method will repeatedly check for and process a specially-formatted text file in the location that you specify.
The file is a comma separated file assumed to have four or six fields per line:
operation, role, user idnumber, course idnumber [, starttime [, endtime]]
where:
* operation - add | del
* role - student | teacher | teacheredit
* user idnumber - idnumber in the user table NB not id
* course idnumber - idnumber in the course table NB not id
* starttime - start time (in seconds since epoch) - optional
* endtime - end time (in seconds since epoch) - optional
It could look something like this:
<pre class="informationbox">
add, student, 5, CF101
add, teacher, 6, CF101
add, teacheredit, 7, CF101
del, student, 8, CF101
del, student, 17, CF101
add, student, 21, CF101, 1091115000, 1091215000
</pre>';
$string['privacy:metadata:enrol_flatfile'] = 'The Flat file (CSV) enrolment plugin may store personal data relating to future enrolments in the enrol_flatfile table.';
$string['privacy:metadata:enrol_flatfile:action'] = 'The enrolment action expected on the given date';
$string['privacy:metadata:enrol_flatfile:courseid'] = 'The course ID to which the enrolment relates';
$string['privacy:metadata:enrol_flatfile:roleid'] = 'The ID of the role to be assigned or unassigned';
$string['privacy:metadata:enrol_flatfile:timestart'] = 'The time when the enrolment starts';
$string['privacy:metadata:enrol_flatfile:timeend'] = 'The time when the enrolment ends';
$string['privacy:metadata:enrol_flatfile:timemodified'] = 'The time when the enrolment is modified';
$string['privacy:metadata:enrol_flatfile:userid'] = 'The ID of the user to which the role assignment relates';
+696
View File
@@ -0,0 +1,696 @@
<?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/>.
/**
* Flatfile enrolment plugin.
*
* This plugin lets the user specify a "flatfile" (CSV) containing enrolment information.
* On a regular cron cycle, the specified file is parsed and then deleted.
*
* @package enrol_flatfile
* @copyright 2010 Eugene Venter
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Flatfile enrolment plugin implementation.
*
* Comma separated file assumed to have four or six fields per line:
* operation, role, idnumber(user), idnumber(course) [, starttime [, endtime]]
* where:
* operation = add | del
* role = student | teacher | teacheredit
* idnumber(user) = idnumber in the user table NB not id
* idnumber(course) = idnumber in the course table NB not id
* starttime = start time (in seconds since epoch) - optional
* endtime = end time (in seconds since epoch) - optional
*
* @author Eugene Venter - based on code by Petr Skoda, Martin Dougiamas, Martin Langhoff and others
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol_flatfile_plugin extends enrol_plugin {
protected $lasternoller = null;
protected $lasternollercourseid = 0;
/**
* Does this plugin assign protected roles are can they be manually removed?
* @return bool - false means anybody may tweak roles, it does not use itemid and component when assigning roles
*/
public function roles_protected() {
return false;
}
/**
* Does this plugin allow manual unenrolment of all users?
* All plugins allowing this must implement 'enrol/xxx:unenrol' capability
*
* @param stdClass $instance course enrol instance
* @return bool - true means user with 'enrol/xxx:unenrol' may unenrol others freely, false means nobody may touch user_enrolments
*/
public function allow_unenrol(stdClass $instance) {
return true;
}
/**
* Does this plugin allow manual unenrolment of a specific user?
* All plugins allowing this must implement 'enrol/xxx:unenrol' capability
*
* This is useful especially for synchronisation plugins that
* do suspend instead of full unenrolment.
*
* @param stdClass $instance course enrol instance
* @param stdClass $ue record from user_enrolments table, specifies user
*
* @return bool - true means user with 'enrol/xxx:unenrol' may unenrol this user, false means nobody may touch this user enrolment
*/
public function allow_unenrol_user(stdClass $instance, stdClass $ue) {
return true;
}
/**
* Does this plugin allow manual changes in user_enrolments table?
*
* All plugins allowing this must implement 'enrol/xxx:manage' capability
*
* @param stdClass $instance course enrol instance
* @return bool - true means it is possible to change enrol period and status in user_enrolments table
*/
public function allow_manage(stdClass $instance) {
return true;
}
/**
* Is it possible to delete enrol instance via standard UI?
*
* @param object $instance
* @return bool
*/
public function can_delete_instance($instance) {
$context = context_course::instance($instance->courseid);
return has_capability('enrol/flatfile:manage', $context);
}
/**
* Is it possible to hide/show enrol instance via standard UI?
*
* @param stdClass $instance
* @return bool
*/
public function can_hide_show_instance($instance) {
$context = context_course::instance($instance->courseid);
return has_capability('enrol/flatfile:manage', $context);
}
/**
* Enrol user into course via enrol instance.
*
* @param stdClass $instance
* @param int $userid
* @param int $roleid optional role id
* @param int $timestart 0 means unknown
* @param int $timeend 0 means forever
* @param int $status default to ENROL_USER_ACTIVE for new enrolments, no change by default in updates
* @param bool $recovergrades restore grade history
* @return void
*/
public function enrol_user(stdClass $instance, $userid, $roleid = null, $timestart = 0, $timeend = 0, $status = null, $recovergrades = null) {
parent::enrol_user($instance, $userid, null, $timestart, $timeend, $status, $recovergrades);
if ($roleid) {
$context = context_course::instance($instance->courseid, MUST_EXIST);
role_assign($roleid, $userid, $context->id, 'enrol_'.$this->get_name(), $instance->id);
}
}
/**
* Execute synchronisation.
* @param progress_trace
* @return int exit code, 0 means ok, 2 means plugin disabled
*/
public function sync(progress_trace $trace) {
if (!enrol_is_enabled('flatfile')) {
return 2;
}
$mailadmins = $this->get_config('mailadmins', 0);
if ($mailadmins) {
$buffer = new progress_trace_buffer(new text_progress_trace(), false);
$trace = new combined_progress_trace(array($trace, $buffer));
}
$processed = false;
$processed = $this->process_file($trace) || $processed;
$processed = $this->process_buffer($trace) || $processed;
$processed = $this->process_expirations($trace) || $processed;
if ($processed and $mailadmins) {
if ($log = $buffer->get_buffer()) {
$eventdata = new \core\message\message();
$eventdata->courseid = SITEID;
$eventdata->modulename = 'moodle';
$eventdata->component = 'enrol_flatfile';
$eventdata->name = 'flatfile_enrolment';
$eventdata->userfrom = get_admin();
$eventdata->userto = get_admin();
$eventdata->subject = 'Flatfile Enrolment Log';
$eventdata->fullmessage = $log;
$eventdata->fullmessageformat = FORMAT_PLAIN;
$eventdata->fullmessagehtml = '';
$eventdata->smallmessage = '';
message_send($eventdata);
}
$buffer->reset_buffer();
}
return 0;
}
/**
* Sorry, we do not want to show paths in cron output.
*
* @param string $filepath
* @return string
*/
protected function obfuscate_filepath($filepath) {
global $CFG;
if (strpos($filepath, $CFG->dataroot.'/') === 0 or strpos($filepath, $CFG->dataroot.'\\') === 0) {
$disclosefile = '$CFG->dataroot'.substr($filepath, strlen($CFG->dataroot));
} else if (strpos($filepath, $CFG->dirroot.'/') === 0 or strpos($filepath, $CFG->dirroot.'\\') === 0) {
$disclosefile = '$CFG->dirroot'.substr($filepath, strlen($CFG->dirroot));
} else {
$disclosefile = basename($filepath);
}
return $disclosefile;
}
/**
* Process flatfile.
* @param progress_trace $trace
* @return bool true if any data processed, false if not
*/
protected function process_file(progress_trace $trace) {
global $CFG, $DB;
// We may need more memory here.
core_php_time_limit::raise();
raise_memory_limit(MEMORY_HUGE);
$filelocation = $this->get_config('location');
if (empty($filelocation)) {
// Default legacy location.
$filelocation = "$CFG->dataroot/1/enrolments.txt";
}
$disclosefile = $this->obfuscate_filepath($filelocation);
if (!file_exists($filelocation)) {
$trace->output("Flatfile enrolments file not found: $disclosefile");
$trace->finished();
return false;
}
$trace->output("Processing flat file enrolments from: $disclosefile ...");
$content = file_get_contents($filelocation);
if ($content !== false) {
$rolemap = $this->get_role_map($trace);
$content = core_text::convert($content, $this->get_config('encoding', 'utf-8'), 'utf-8');
$content = str_replace("\r", '', $content);
$content = explode("\n", $content);
$line = 0;
foreach($content as $fields) {
$line++;
if (trim($fields) === '') {
// Empty lines are ignored.
continue;
}
// Deal with different separators.
if (strpos($fields, ',') !== false) {
$fields = explode(',', $fields);
} else {
$fields = explode(';', $fields);
}
// If a line is incorrectly formatted ie does not have 4 comma separated fields then ignore it.
if (count($fields) < 4 or count($fields) > 6) {
$trace->output("Line incorrectly formatted - ignoring $line", 1);
continue;
}
$fields[0] = trim(core_text::strtolower($fields[0]));
$fields[1] = trim(core_text::strtolower($fields[1]));
$fields[2] = trim($fields[2]);
$fields[3] = trim($fields[3]);
$fields[4] = isset($fields[4]) ? (int)trim($fields[4]) : 0;
$fields[5] = isset($fields[5]) ? (int)trim($fields[5]) : 0;
// Deal with quoted values - all or nothing, we need to support "' in idnumbers, sorry.
if (strpos($fields[0], "'") === 0) {
foreach ($fields as $k=>$v) {
$fields[$k] = trim($v, "'");
}
} else if (strpos($fields[0], '"') === 0) {
foreach ($fields as $k=>$v) {
$fields[$k] = trim($v, '"');
}
}
$trace->output("$line: $fields[0], $fields[1], $fields[2], $fields[3], $fields[4], $fields[5]", 1);
// Check correct formatting of operation field.
if ($fields[0] !== "add" and $fields[0] !== "del") {
$trace->output("Unknown operation in field 1 - ignoring line $line", 1);
continue;
}
// Check correct formatting of role field.
if (!isset($rolemap[$fields[1]])) {
$trace->output("Unknown role in field2 - ignoring line $line", 1);
continue;
}
$roleid = $rolemap[$fields[1]];
if (empty($fields[2]) or !$user = $DB->get_record("user", array("idnumber"=>$fields[2], 'deleted'=>0))) {
$trace->output("Unknown user idnumber or deleted user in field 3 - ignoring line $line", 1);
continue;
}
if (!$course = $DB->get_record("course", array("idnumber"=>$fields[3]))) {
$trace->output("Unknown course idnumber in field 4 - ignoring line $line", 1);
continue;
}
if ($fields[4] > $fields[5] and $fields[5] != 0) {
$trace->output("Start time was later than end time - ignoring line $line", 1);
continue;
}
$this->process_records($trace, $fields[0], $roleid, $user, $course, $fields[4], $fields[5]);
}
unset($content);
}
if (!unlink($filelocation)) {
$eventdata = new \core\message\message();
$eventdata->courseid = SITEID;
$eventdata->modulename = 'moodle';
$eventdata->component = 'enrol_flatfile';
$eventdata->name = 'flatfile_enrolment';
$eventdata->userfrom = get_admin();
$eventdata->userto = get_admin();
$eventdata->subject = get_string('filelockedmailsubject', 'enrol_flatfile');
$eventdata->fullmessage = get_string('filelockedmail', 'enrol_flatfile', $filelocation);
$eventdata->fullmessageformat = FORMAT_PLAIN;
$eventdata->fullmessagehtml = '';
$eventdata->smallmessage = '';
message_send($eventdata);
$trace->output("Error deleting enrolment file: $disclosefile", 1);
} else {
$trace->output("Deleted enrolment file", 1);
}
$trace->output("...finished enrolment file processing.");
$trace->finished();
return true;
}
/**
* Process any future enrollments stored in the buffer.
* @param progress_trace $trace
* @return bool true if any data processed, false if not
*/
protected function process_buffer(progress_trace $trace) {
global $DB;
if (!$future_enrols = $DB->get_records_select('enrol_flatfile', "timestart < ?", array(time()))) {
$trace->output("No enrolments to be processed in flatfile buffer");
$trace->finished();
return false;
}
$trace->output("Starting processing of flatfile buffer");
foreach($future_enrols as $en) {
$user = $DB->get_record('user', array('id'=>$en->userid));
$course = $DB->get_record('course', array('id'=>$en->courseid));
if ($user and $course) {
$trace->output("buffer: $en->action $en->roleid $user->id $course->id $en->timestart $en->timeend", 1);
$this->process_records($trace, $en->action, $en->roleid, $user, $course, $en->timestart, $en->timeend, false);
}
$DB->delete_records('enrol_flatfile', array('id'=>$en->id));
}
$trace->output("Finished processing of flatfile buffer");
$trace->finished();
return true;
}
/**
* Process user enrolment line.
*
* @param progress_trace $trace
* @param string $action
* @param int $roleid
* @param stdClass $user
* @param stdClass $course
* @param int $timestart
* @param int $timeend
* @param bool $buffer_if_future
*/
protected function process_records(progress_trace $trace, $action, $roleid, $user, $course, $timestart, $timeend, $buffer_if_future = true) {
global $CFG, $DB;
// Check if timestart is for future processing.
if ($timestart > time() and $buffer_if_future) {
// Populate into enrol_flatfile table as a future role to be assigned by cron.
// Note: since 2.0 future enrolments do not cause problems if you disable guest access.
$future_en = new stdClass();
$future_en->action = $action;
$future_en->roleid = $roleid;
$future_en->userid = $user->id;
$future_en->courseid = $course->id;
$future_en->timestart = $timestart;
$future_en->timeend = $timeend;
$future_en->timemodified = time();
$DB->insert_record('enrol_flatfile', $future_en);
$trace->output("User $user->id will be enrolled later into course $course->id using role $roleid ($timestart, $timeend)", 1);
return;
}
$context = context_course::instance($course->id);
if ($action === 'add') {
// Clear the buffer just in case there were some future enrolments.
$DB->delete_records('enrol_flatfile', array('userid'=>$user->id, 'courseid'=>$course->id, 'roleid'=>$roleid));
$instance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'flatfile'));
if (empty($instance)) {
// Only add an enrol instance to the course if non-existent.
$enrolid = $this->add_instance($course);
$instance = $DB->get_record('enrol', array('id' => $enrolid));
}
$notify = false;
if ($ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$user->id))) {
// Update only.
$this->update_user_enrol($instance, $user->id, ENROL_USER_ACTIVE, $timestart, $timeend);
if (!$DB->record_exists('role_assignments', array('contextid'=>$context->id, 'roleid'=>$roleid, 'userid'=>$user->id, 'component'=>'enrol_flatfile', 'itemid'=>$instance->id))) {
role_assign($roleid, $user->id, $context->id, 'enrol_flatfile', $instance->id);
}
$trace->output("User $user->id enrolment updated in course $course->id using role $roleid ($timestart, $timeend)", 1);
} else {
// Enrol the user with this plugin instance.
$this->enrol_user($instance, $user->id, $roleid, $timestart, $timeend);
$trace->output("User $user->id enrolled in course $course->id using role $roleid ($timestart, $timeend)", 1);
$notify = true;
}
if ($notify and $this->get_config('mailstudents')) {
$oldforcelang = force_current_language($user->lang);
// Send welcome notification to enrolled users.
$a = new stdClass();
$a->coursename = format_string($course->fullname, true, array('context' => $context));
$a->profileurl = "$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id";
$subject = get_string('enrolmentnew', 'enrol', format_string($course->shortname, true, array('context' => $context)));
$eventdata = new \core\message\message();
$eventdata->courseid = $course->id;
$eventdata->modulename = 'moodle';
$eventdata->component = 'enrol_flatfile';
$eventdata->name = 'flatfile_enrolment';
$eventdata->userfrom = $this->get_enroller($course->id);
$eventdata->userto = $user;
$eventdata->subject = $subject;
$eventdata->fullmessage = get_string('welcometocoursetext', '', $a);
$eventdata->fullmessageformat = FORMAT_PLAIN;
$eventdata->fullmessagehtml = '';
$eventdata->smallmessage = '';
if (message_send($eventdata)) {
$trace->output("Notified enrolled user", 1);
} else {
$trace->output("Failed to notify enrolled user", 1);
}
force_current_language($oldforcelang);
}
if ($notify and $this->get_config('mailteachers', 0)) {
// Notify person responsible for enrolments.
$enroller = $this->get_enroller($course->id);
$oldforcelang = force_current_language($enroller->lang);
$a = new stdClass();
$a->course = format_string($course->fullname, true, array('context' => $context));
$a->user = fullname($user);
$subject = get_string('enrolmentnew', 'enrol', format_string($course->shortname, true, array('context' => $context)));
$eventdata = new \core\message\message();
$eventdata->courseid = $course->id;
$eventdata->modulename = 'moodle';
$eventdata->component = 'enrol_flatfile';
$eventdata->name = 'flatfile_enrolment';
$eventdata->userfrom = get_admin();
$eventdata->userto = $enroller;
$eventdata->subject = $subject;
$eventdata->fullmessage = get_string('enrolmentnewuser', 'enrol', $a);
$eventdata->fullmessageformat = FORMAT_PLAIN;
$eventdata->fullmessagehtml = '';
$eventdata->smallmessage = '';
if (message_send($eventdata)) {
$trace->output("Notified enroller {$eventdata->userto->id}", 1);
} else {
$trace->output("Failed to notify enroller {$eventdata->userto->id}", 1);
}
force_current_language($oldforcelang);
}
return;
} else if ($action === 'del') {
// Clear the buffer just in case there were some future enrolments.
$DB->delete_records('enrol_flatfile', array('userid'=>$user->id, 'courseid'=>$course->id, 'roleid'=>$roleid));
$action = $this->get_config('unenrolaction');
if ($action == ENROL_EXT_REMOVED_KEEP) {
$trace->output("del action is ignored", 1);
return;
}
// Loops through all enrolment methods, try to unenrol if roleid somehow matches.
$instances = $DB->get_records('enrol', array('courseid' => $course->id));
$unenrolled = false;
foreach ($instances as $instance) {
if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$user->id))) {
continue;
}
if ($instance->enrol === 'flatfile') {
$plugin = $this;
} else {
if (!enrol_is_enabled($instance->enrol)) {
continue;
}
if (!$plugin = enrol_get_plugin($instance->enrol)) {
continue;
}
if (!$plugin->allow_unenrol_user($instance, $ue)) {
continue;
}
}
// For some reason the del action includes a role name, this complicates everything.
$componentroles = array();
$manualroles = array();
$ras = $DB->get_records('role_assignments', array('userid'=>$user->id, 'contextid'=>$context->id));
foreach ($ras as $ra) {
if ($ra->component === '') {
$manualroles[$ra->roleid] = $ra->roleid;
} else if ($ra->component === 'enrol_'.$instance->enrol and $ra->itemid == $instance->id) {
$componentroles[$ra->roleid] = $ra->roleid;
}
}
if ($componentroles and !isset($componentroles[$roleid])) {
// Do not unenrol using this method, user has some other protected role!
continue;
} else if (empty($ras)) {
// If user does not have any roles then let's just suspend as many methods as possible.
} else if (!$plugin->roles_protected()) {
if (!$componentroles and $manualroles and !isset($manualroles[$roleid])) {
// Most likely we want to keep users enrolled because they have some other course roles.
continue;
}
}
if ($action == ENROL_EXT_REMOVED_UNENROL) {
$unenrolled = true;
if (!$plugin->roles_protected()) {
role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'roleid'=>$roleid, 'component'=>'', 'itemid'=>0), true);
}
$plugin->unenrol_user($instance, $user->id);
$trace->output("User $user->id was unenrolled from course $course->id (enrol_$instance->enrol)", 1);
} else if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
if ($plugin->allow_manage($instance)) {
if ($ue->status == ENROL_USER_ACTIVE) {
$unenrolled = true;
$plugin->update_user_enrol($instance, $user->id, ENROL_USER_SUSPENDED);
if (!$plugin->roles_protected()) {
role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'enrol_'.$instance->enrol, 'itemid'=>$instance->id), true);
role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'roleid'=>$roleid, 'component'=>'', 'itemid'=>0), true);
}
$trace->output("User $user->id enrolment was suspended in course $course->id (enrol_$instance->enrol)", 1);
}
}
}
}
if (!$unenrolled) {
if (0 == $DB->count_records('role_assignments', array('userid'=>$user->id, 'contextid'=>$context->id))) {
role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'', 'itemid'=>0), true);
}
$trace->output("User $user->id (with role $roleid) not unenrolled from course $course->id", 1);
}
return;
}
}
/**
* Returns the user who is responsible for flatfile enrolments in given curse.
*
* Usually it is the first editing teacher - the person with "highest authority"
* as defined by sort_by_roleassignment_authority() having 'enrol/flatfile:manage'
* or 'moodle/role:assign' capability.
*
* @param int $courseid enrolment instance id
* @return stdClass user record
*/
protected function get_enroller($courseid) {
if ($this->lasternollercourseid == $courseid and $this->lasternoller) {
return $this->lasternoller;
}
$context = context_course::instance($courseid);
$users = get_enrolled_users($context, 'enrol/flatfile:manage');
if (!$users) {
$users = get_enrolled_users($context, 'moodle/role:assign');
}
if ($users) {
$users = sort_by_roleassignment_authority($users, $context);
$this->lasternoller = reset($users);
unset($users);
} else {
$this->lasternoller = get_admin();
}
$this->lasternollercourseid == $courseid;
return $this->lasternoller;
}
/**
* Returns a mapping of ims roles to role ids.
*
* @param progress_trace $trace
* @return array imsrolename=>roleid
*/
protected function get_role_map(progress_trace $trace) {
global $DB;
// Get all roles.
$rolemap = array();
$roles = $DB->get_records('role', null, '', 'id, name, shortname');
foreach ($roles as $id=>$role) {
$alias = $this->get_config('map_'.$id, $role->shortname, '');
$alias = trim(core_text::strtolower($alias));
if ($alias === '') {
// Either not configured yet or somebody wants to skip these intentionally.
continue;
}
if (isset($rolemap[$alias])) {
$trace->output("Duplicate role alias $alias detected!");
} else {
$rolemap[$alias] = $id;
}
}
return $rolemap;
}
/**
* Restore instance and map settings.
*
* @param restore_enrolments_structure_step $step
* @param stdClass $data
* @param stdClass $course
* @param int $oldid
*/
public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
global $DB;
if ($instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>$this->get_name()))) {
$instanceid = $instance->id;
} else {
$instanceid = $this->add_instance($course);
}
$step->set_mapping('enrol', $oldid, $instanceid);
}
/**
* Restore user enrolment.
*
* @param restore_enrolments_structure_step $step
* @param stdClass $data
* @param stdClass $instance
* @param int $oldinstancestatus
* @param int $userid
*/
public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
$this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status);
}
/**
* Restore role assignment.
*
* @param stdClass $instance
* @param int $roleid
* @param int $userid
* @param int $contextid
*/
public function restore_role_assignment($instance, $roleid, $userid, $contextid) {
role_assign($roleid, $userid, $contextid, 'enrol_'.$instance->enrol, $instance->id);
}
}
+71
View File
@@ -0,0 +1,71 @@
<?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/>.
/**
* Flatfile enrolments plugin settings and presets.
*
* @package enrol_flatfile
* @copyright 2010 Eugene Venter
* @author Eugene Venter - based on code by Petr Skoda and others
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/adminlib.php');
if ($ADMIN->fulltree) {
//--- general settings -----------------------------------------------------------------------------------
$settings->add(new admin_setting_heading('enrol_flatfile_settings', '', get_string('pluginname_desc', 'enrol_flatfile')));
$settings->add(new admin_setting_configfile('enrol_flatfile/location', get_string('location', 'enrol_flatfile'), get_string('location_desc', 'enrol_flatfile'), ''));
$options = core_text::get_encodings();
$settings->add(new admin_setting_configselect('enrol_flatfile/encoding', get_string('encoding', 'enrol_flatfile'), '', 'UTF-8', $options));
$settings->add(new admin_setting_configcheckbox('enrol_flatfile/mailstudents', get_string('notifyenrolled', 'enrol_flatfile'), '', 0));
$settings->add(new admin_setting_configcheckbox('enrol_flatfile/mailteachers', get_string('notifyenroller', 'enrol_flatfile'), '', 0));
$settings->add(new admin_setting_configcheckbox('enrol_flatfile/mailadmins', get_string('notifyadmin', 'enrol_flatfile'), '', 0));
$options = array(ENROL_EXT_REMOVED_UNENROL => get_string('extremovedunenrol', 'enrol'),
ENROL_EXT_REMOVED_KEEP => get_string('extremovedkeep', 'enrol'),
ENROL_EXT_REMOVED_SUSPENDNOROLES => get_string('extremovedsuspendnoroles', 'enrol'));
$settings->add(new admin_setting_configselect('enrol_flatfile/unenrolaction', get_string('extremovedaction', 'enrol'), get_string('extremovedaction_help', 'enrol'), ENROL_EXT_REMOVED_SUSPENDNOROLES, $options));
// Note: let's reuse the ext sync constants and strings here, internally it is very similar,
// it describes what should happen when users are not supposed to be enrolled any more.
$options = array(
ENROL_EXT_REMOVED_KEEP => get_string('extremovedkeep', 'enrol'),
ENROL_EXT_REMOVED_SUSPENDNOROLES => get_string('extremovedsuspendnoroles', 'enrol'),
ENROL_EXT_REMOVED_UNENROL => get_string('extremovedunenrol', 'enrol'),
);
$settings->add(new admin_setting_configselect('enrol_flatfile/expiredaction', get_string('expiredaction', 'enrol_flatfile'), get_string('expiredaction_help', 'enrol_flatfile'), ENROL_EXT_REMOVED_SUSPENDNOROLES, $options));
//--- mapping -------------------------------------------------------------------------------------------
if (!during_initial_install()) {
$settings->add(new admin_setting_heading('enrol_flatfile_mapping', get_string('mapping', 'enrol_flatfile'), ''));
$roles = role_fix_names(get_all_roles());
foreach ($roles as $role) {
$settings->add(new enrol_flatfile_role_setting($role));
}
unset($roles);
}
}
+555
View File
@@ -0,0 +1,555 @@
<?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/>.
/**
* flatfile enrolment sync tests.
*
* @package enrol_flatfile
* @category test
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_flatfile;
use enrol_flatfile\task\flatfile_sync_task;
/**
* flatfile enrolment sync tests.
*
* @package enrol_flatfile
* @category test
* @copyright 2012 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class flatfile_test extends \advanced_testcase {
protected function enable_plugin() {
$enabled = enrol_get_plugins(true);
$enabled['flatfile'] = true;
$enabled = array_keys($enabled);
set_config('enrol_plugins_enabled', implode(',', $enabled));
}
protected function disable_plugin() {
$enabled = enrol_get_plugins(true);
unset($enabled['flatfile']);
$enabled = array_keys($enabled);
set_config('enrol_plugins_enabled', implode(',', $enabled));
}
public function test_basics(): void {
$this->assertFalse(enrol_is_enabled('flatfile'));
$plugin = enrol_get_plugin('flatfile');
$this->assertInstanceOf('enrol_flatfile_plugin', $plugin);
$this->assertEquals(ENROL_EXT_REMOVED_SUSPENDNOROLES, get_config('enrol_flatfile', 'expiredaction'));
}
public function test_sync_nothing(): void {
$this->resetAfterTest();
$this->disable_plugin();
$flatfileplugin = enrol_get_plugin('flatfile');
// Just make sure the sync does not throw any errors when nothing to do.
$flatfileplugin->sync(new \null_progress_trace());
$this->enable_plugin();
$flatfileplugin->sync(new \null_progress_trace());
}
public function test_sync(): void {
global $CFG, $DB;
$this->resetAfterTest();
/** @var \enrol_flatfile_plugin $flatfileplugin */
$flatfileplugin = enrol_get_plugin('flatfile');
/** @var \enrol_manual_plugin $manualplugin */
$manualplugin = enrol_get_plugin('manual');
$this->assertNotEmpty($manualplugin);
$trace = new \null_progress_trace();
$this->enable_plugin();
$file = "$CFG->dataroot/enrol.txt";
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$managerrole = $DB->get_record('role', array('shortname'=>'manager'));
$this->assertNotEmpty($managerrole);
$user1 = $this->getDataGenerator()->create_user(array('idnumber'=>'u1'));
$user2 = $this->getDataGenerator()->create_user(array('idnumber'=>'u2'));
$user3 = $this->getDataGenerator()->create_user(array('idnumber'=>'u3'));
$user4 = $this->getDataGenerator()->create_user(array('idnumber'=>'čtvrtý'));
$user5 = $this->getDataGenerator()->create_user(array('idnumber'=>'u5'));
$user6 = $this->getDataGenerator()->create_user(array('idnumber'=>'u6'));
$user7 = $this->getDataGenerator()->create_user(array('idnumber'=>''));
$course1 = $this->getDataGenerator()->create_course(array('idnumber'=>'c1'));
$course2 = $this->getDataGenerator()->create_course(array('idnumber'=>'c2'));
$course3 = $this->getDataGenerator()->create_course(array('idnumber'=>'c3'));
$context1 = \context_course::instance($course1->id);
$context2 = \context_course::instance($course2->id);
$context3 = \context_course::instance($course3->id);
$maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$maninstance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$maninstance3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST);
// Rename teacher role.
$flatfileplugin->set_config('map_'.$teacherrole->id, 'ucitel');
// Disable manager role.
$flatfileplugin->set_config('map_'.$managerrole->id, '');
// Set file location.
$flatfileplugin->set_config('location', $file);
$now = time();
$before = $now - 60;
$future = $now + 60*60*5;
$farfuture = $now + 60*60*24*5;
// Test add action.
$data ="'add','student','u1','c1'
\"add\" , \"ucitel\", u2 , c2
add,manager,u3,c1
add,student,čtvrtý,c2,$before
add,student,u5,c1,0,0,1
add,student,u5,c2,20,10
add,student,u6,c1,0,$future
add,student,u6,c2,$future,0
add,student,u6,c3,$future,$farfuture
add,student,,c2";
file_put_contents($file, $data);
$this->assertEquals(0, $DB->count_records('user_enrolments'));
$this->assertEquals(0, $DB->count_records('role_assignments'));
$this->assertEquals(0, $DB->count_records('enrol_flatfile'));
$this->assertTrue(file_exists($file));
$flatfileplugin->sync($trace);
$this->assertFalse(file_exists($file));
$this->assertEquals(4, $DB->count_records('user_enrolments'));
$this->assertEquals(4, $DB->count_records('role_assignments'));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_flatfile')));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context2->id, 'userid'=>$user2->id, 'roleid'=>$teacherrole->id, 'component'=>'enrol_flatfile')));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context2->id, 'userid'=>$user4->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_flatfile')));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user6->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_flatfile')));
// Test buffer.
$this->assertEquals(2, $DB->count_records('enrol_flatfile'));
$flatfileplugin->sync($trace);
$this->assertEquals(2, $DB->count_records('enrol_flatfile'));
$this->assertEquals(4, $DB->count_records('user_enrolments'));
$this->assertEquals(4, $DB->count_records('role_assignments'));
$DB->set_field('enrol_flatfile', 'timestart', time()-60, array('timestart'=>$future, 'timeend'=>0));
$flatfileplugin->sync($trace);
$this->assertEquals(1, $DB->count_records('enrol_flatfile'));
$this->assertEquals(5, $DB->count_records('user_enrolments'));
$this->assertEquals(5, $DB->count_records('role_assignments'));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context2->id, 'userid'=>$user6->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_flatfile')));
$this->assertTrue($DB->record_exists('enrol_flatfile', array('userid'=>$user6->id, 'roleid'=>$studentrole->id, 'timeend'=>$farfuture)));
// Test encoding.
$data = "add;student;čtvrtý;c3";
$data = \core_text::convert($data, 'utf-8', 'iso-8859-2');
file_put_contents($file, $data);
$flatfileplugin->set_config('encoding', 'iso-8859-2');
$flatfileplugin->sync($trace);
$this->assertEquals(6, $DB->count_records('user_enrolments'));
$this->assertEquals(6, $DB->count_records('role_assignments'));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context3->id, 'userid'=>$user4->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_flatfile')));
$flatfileplugin->set_config('encoding', 'UTF-8');
// Test unenrolling purges buffer.
$manualplugin->enrol_user($maninstance1, $user1->id, $teacherrole->id);
$manualplugin->enrol_user($maninstance3, $user5->id, $teacherrole->id);
$this->assertEquals(8, $DB->count_records('user_enrolments'));
$this->assertEquals(8, $DB->count_records('role_assignments'));
$this->assertEquals(1, $DB->count_records('enrol_flatfile'));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user1->id, 'roleid'=>$teacherrole->id)));
$flatfileplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_KEEP);
$data = "del,student,u1,c1\ndel,teacher,u6,c3";
file_put_contents($file, $data);
$flatfileplugin->sync($trace);
$this->assertEquals(8, $DB->count_records('user_enrolments'));
$this->assertEquals(8, $DB->count_records('role_assignments'));
$this->assertEquals(1, $DB->count_records('enrol_flatfile'));
$data = "del,student,u6,c3";
file_put_contents($file, $data);
$flatfileplugin->sync($trace);
$this->assertEquals(8, $DB->count_records('user_enrolments'));
$this->assertEquals(8, $DB->count_records('role_assignments'));
$this->assertEquals(0, $DB->count_records('enrol_flatfile'));
$flatfileplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
$data = "
del,student,u1,c1
del,grrr,u5,c1
del,guest,u5,c2
del,student,u6,c2
del,ucitel,u5,c3";
file_put_contents($file, $data);
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_flatfile')));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context2->id, 'userid'=>$user6->id, 'roleid'=>$studentrole->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context3->id, 'userid'=>$user5->id, 'roleid'=>$teacherrole->id)));
$flatfileplugin->sync($trace);
$this->assertEquals(8, $DB->count_records('user_enrolments'));
$this->assertEquals(5, $DB->count_records('role_assignments'));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_flatfile')));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user1->id, 'roleid'=>$teacherrole->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context2->id, 'userid'=>$user6->id, 'roleid'=>$studentrole->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context3->id, 'userid'=>$user5->id, 'roleid'=>$teacherrole->id)));
$flatfileplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
$manualplugin->enrol_user($maninstance3, $user5->id, $teacherrole->id);
$data = "
add,student,u1,c1
add,student,u6,c2";
file_put_contents($file, $data);
$flatfileplugin->sync($trace);
$this->assertEquals(8, $DB->count_records('user_enrolments'));
$this->assertEquals(8, $DB->count_records('role_assignments'));
$this->assertEquals(0, $DB->count_records('enrol_flatfile'));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_flatfile')));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context2->id, 'userid'=>$user6->id, 'roleid'=>$studentrole->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context3->id, 'userid'=>$user5->id, 'roleid'=>$teacherrole->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('userid'=>$user5->id, 'enrolid'=>$maninstance3->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('userid'=>$user1->id, 'enrolid'=>$maninstance1->id)));
$data = "
del,student,u1,c1
del,grrr,u5,c1
del,guest,u5,c2
del,student,u6,c2
del,ucitel,u5,c3";
file_put_contents($file, $data);
$flatfileplugin->sync($trace);
$this->assertEquals(5, $DB->count_records('user_enrolments'));
$this->assertEquals(5, $DB->count_records('role_assignments'));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'component'=>'enrol_flatfile')));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user1->id, 'roleid'=>$teacherrole->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context2->id, 'userid'=>$user6->id, 'roleid'=>$studentrole->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context3->id, 'userid'=>$user5->id, 'roleid'=>$teacherrole->id)));
$this->assertFalse($DB->record_exists('user_enrolments', array('userid'=>$user5->id, 'enrolid'=>$maninstance3->id)));
$this->assertTrue($DB->record_exists('user_enrolments', array('userid'=>$user1->id, 'enrolid'=>$maninstance1->id)));
}
public function test_notification(): void {
global $CFG, $DB;
$this->resetAfterTest();
$this->preventResetByRollback();
/** @var \enrol_flatfile_plugin $flatfileplugin */
$flatfileplugin = enrol_get_plugin('flatfile');
/** @var \enrol_manual_plugin $manualplugin */
$manualplugin = enrol_get_plugin('manual');
$this->assertNotEmpty($manualplugin);
$this->enable_plugin();
$trace = new \progress_trace_buffer(new \text_progress_trace(), false);
$file = "$CFG->dataroot/enrol.txt";
$flatfileplugin->set_config('location', $file);
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'));
$this->assertNotEmpty($teacherrole);
$user1 = $this->getDataGenerator()->create_user(array('idnumber'=>'u1'));
$user2 = $this->getDataGenerator()->create_user(array('idnumber'=>'u2'));
$user3 = $this->getDataGenerator()->create_user(array('idnumber'=>'u3'));
$admin = get_admin();
$course1 = $this->getDataGenerator()->create_course(array('idnumber'=>'c1'));
$course2 = $this->getDataGenerator()->create_course(array('idnumber'=>'c2'));
$context1 = \context_course::instance($course1->id);
$context2 = \context_course::instance($course2->id);
$maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$now = time();
$future = $now + 60*60*5;
$farfuture = $now + 60*60*24*5;
$manualplugin->enrol_user($maninstance1, $user3->id, $teacherrole->id);
$data =
"add,student,u1,c1
add,student,u2,c2
add,student,u2,c1,$future,$farfuture";
file_put_contents($file, $data);
$this->assertEquals(1, $DB->count_records('user_enrolments'));
$this->assertEquals(1, $DB->count_records('role_assignments'));
$this->assertEquals(0, $DB->count_records('enrol_flatfile'));
$flatfileplugin->set_config('mailadmins', 1);
$flatfileplugin->set_config('mailteachers', 1);
$flatfileplugin->set_config('mailstudents', 1);
$sink = $this->redirectMessages();
$flatfileplugin->sync($trace);
$this->assertEquals(3, $DB->count_records('user_enrolments'));
$this->assertEquals(3, $DB->count_records('role_assignments'));
$this->assertEquals(1, $DB->count_records('enrol_flatfile'));
$messages = $sink->get_messages();
$this->assertCount(5, $messages);
// Notify student from teacher.
$this->assertEquals($user1->id, $messages[0]->useridto);
$this->assertEquals($user3->id, $messages[0]->useridfrom);
// Notify teacher.
$this->assertEquals($user3->id, $messages[1]->useridto);
$this->assertEquals($admin->id, $messages[1]->useridfrom);
// Notify student when teacher not present.
$this->assertEquals($user2->id, $messages[2]->useridto);
$this->assertEquals($admin->id, $messages[2]->useridfrom);
// Notify admin when teacher not present.
$this->assertEquals($admin->id, $messages[3]->useridto);
$this->assertEquals($admin->id, $messages[3]->useridfrom);
// Sent report to admin from self.
$this->assertEquals($admin->id, $messages[4]->useridto);
$this->assertEquals($admin->id, $messages[4]->useridfrom);
}
public function test_expired(): void {
global $DB;
$this->resetAfterTest();
/** @var \enrol_flatfile_plugin $flatfileplugin */
$flatfileplugin = enrol_get_plugin('flatfile');
/** @var \enrol_manual_plugin $manualplugin */
$manualplugin = enrol_get_plugin('manual');
$this->assertNotEmpty($manualplugin);
$now = time();
$trace = new \null_progress_trace();
$this->enable_plugin();
// Prepare some data.
$studentrole = $DB->get_record('role', array('shortname'=>'student'));
$this->assertNotEmpty($studentrole);
$teacherrole = $DB->get_record('role', array('shortname'=>'teacher'));
$this->assertNotEmpty($teacherrole);
$managerrole = $DB->get_record('role', array('shortname'=>'manager'));
$this->assertNotEmpty($managerrole);
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$user3 = $this->getDataGenerator()->create_user();
$user4 = $this->getDataGenerator()->create_user();
$course1 = $this->getDataGenerator()->create_course();
$course2 = $this->getDataGenerator()->create_course();
$context1 = \context_course::instance($course1->id);
$context2 = \context_course::instance($course2->id);
$data = array('roleid'=>$studentrole->id, 'courseid'=>$course1->id);
$id = $flatfileplugin->add_instance($course1, $data);
$instance1 = $DB->get_record('enrol', array('id'=>$id));
$data = array('roleid'=>$studentrole->id, 'courseid'=>$course2->id);
$id = $flatfileplugin->add_instance($course2, $data);
$instance2 = $DB->get_record('enrol', array('id'=>$id));
$data = array('roleid'=>$teacherrole->id, 'courseid'=>$course2->id);
$id = $flatfileplugin->add_instance($course2, $data);
$instance3 = $DB->get_record('enrol', array('id'=>$id));
$maninstance1 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST);
$manualplugin->enrol_user($maninstance1, $user3->id, $studentrole->id);
$this->assertEquals(1, $DB->count_records('user_enrolments'));
$this->assertEquals(1, $DB->count_records('role_assignments'));
$this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id)));
$flatfileplugin->enrol_user($instance1, $user1->id, $studentrole->id);
$flatfileplugin->enrol_user($instance1, $user2->id, $studentrole->id);
$flatfileplugin->enrol_user($instance1, $user3->id, $studentrole->id, 0, $now-60);
$flatfileplugin->enrol_user($instance2, $user1->id, $studentrole->id, 0, 0);
$flatfileplugin->enrol_user($instance2, $user2->id, $studentrole->id, 0, $now-60*60);
$flatfileplugin->enrol_user($instance2, $user3->id, $studentrole->id, 0, $now+60*60);
$flatfileplugin->enrol_user($instance3, $user1->id, $teacherrole->id, $now-60*60*24*7, $now-60);
$flatfileplugin->enrol_user($instance3, $user4->id, $teacherrole->id);
role_assign($managerrole->id, $user3->id, $context1->id);
$this->assertEquals(9, $DB->count_records('user_enrolments'));
$this->assertEquals(10, $DB->count_records('role_assignments'));
$this->assertEquals(7, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id)));
$this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id)));
$this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$managerrole->id)));
// Execute tests.
$flatfileplugin->set_config('expiredaction', ENROL_EXT_REMOVED_KEEP);
$code = $flatfileplugin->sync($trace);
$this->assertSame(0, $code);
$this->assertEquals(9, $DB->count_records('user_enrolments'));
$this->assertEquals(10, $DB->count_records('role_assignments'));
$flatfileplugin->set_config('expiredaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
$flatfileplugin->sync($trace);
$this->assertEquals(9, $DB->count_records('user_enrolments'));
$this->assertEquals(7, $DB->count_records('role_assignments'));
$this->assertEquals(5, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id)));
$this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id)));
$this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$managerrole->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user3->id, 'roleid'=>$studentrole->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context2->id, 'userid'=>$user2->id, 'roleid'=>$studentrole->id)));
$this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context2->id, 'userid'=>$user1->id, 'roleid'=>$teacherrole->id)));
$this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context2->id, 'userid'=>$user1->id, 'roleid'=>$studentrole->id)));
$flatfileplugin->set_config('expiredaction', ENROL_EXT_REMOVED_UNENROL);
role_assign($studentrole->id, $user3->id, $context1->id, 'enrol_flatfile', $instance1->id);
role_assign($studentrole->id, $user2->id, $context2->id, 'enrol_flatfile', $instance2->id);
role_assign($teacherrole->id, $user1->id, $context2->id, 'enrol_flatfile', $instance3->id);
$this->assertEquals(9, $DB->count_records('user_enrolments'));
$this->assertEquals(10, $DB->count_records('role_assignments'));
$this->assertEquals(7, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id)));
$this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id)));
$this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$managerrole->id)));
$flatfileplugin->sync($trace);
$this->assertEquals(6, $DB->count_records('user_enrolments'));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$instance1->id, 'userid'=>$user3->id)));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$instance2->id, 'userid'=>$user2->id)));
$this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$instance3->id, 'userid'=>$user1->id)));
$this->assertEquals(6, $DB->count_records('role_assignments'));
$this->assertEquals(5, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id)));
$this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id)));
$this->assertEquals(0, $DB->count_records('role_assignments', array('roleid'=>$managerrole->id)));
}
/**
* Flatfile enrolment sync task test.
*/
public function test_flatfile_sync_task(): void {
global $CFG, $DB;
$this->resetAfterTest();
$flatfileplugin = enrol_get_plugin('flatfile');
$trace = new \null_progress_trace();
$this->enable_plugin();
$file = "$CFG->dataroot/enrol.txt";
$flatfileplugin->set_config('location', $file);
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$this->assertNotEmpty($studentrole);
$user1 = $this->getDataGenerator()->create_user(array('idnumber' => 'u1'));
$course1 = $this->getDataGenerator()->create_course(array('idnumber' => 'c1'));
$context1 = \context_course::instance($course1->id);
$data =
"add,student,u1,c1";
file_put_contents($file, $data);
$task = new flatfile_sync_task;
$task->execute();
$this->assertEquals(1, $DB->count_records('role_assignments', array('roleid' => $studentrole->id)));
}
/**
* Test for getting user enrolment actions.
*/
public function test_get_user_enrolment_actions(): void {
global $CFG, $PAGE;
$this->resetAfterTest();
// Set page URL to prevent debugging messages.
$PAGE->set_url('/enrol/editinstance.php');
$pluginname = 'flatfile';
// Only enable the flatfile enrol plugin.
$CFG->enrol_plugins_enabled = $pluginname;
$generator = $this->getDataGenerator();
// Get the enrol plugin.
$plugin = enrol_get_plugin($pluginname);
// Create a course.
$course = $generator->create_course();
// Enable this enrol plugin for the course.
$plugin->add_instance($course);
// Create a student.
$student = $generator->create_user();
// Enrol the student to the course.
$generator->enrol_user($student->id, $course->id, 'student', $pluginname);
// Teachers don't have enrol/flatfile:manage and enrol/flatfile:unenrol capabilities by default.
// Login as admin for simplicity.
$this->setAdminUser();
require_once($CFG->dirroot . '/enrol/locallib.php');
$manager = new \course_enrolment_manager($PAGE, $course);
$userenrolments = $manager->get_user_enrolments($student->id);
$this->assertCount(1, $userenrolments);
$ue = reset($userenrolments);
$actions = $plugin->get_user_enrolment_actions($manager, $ue);
// Flatfile enrolment has 2 enrol actions for active users -- edit and unenrol.
$this->assertCount(2, $actions);
}
}
@@ -0,0 +1,312 @@
<?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 tests for enrol_flatfile.
*
* @package enrol_flatfile
* @category test
* @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_flatfile\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\metadata\collection;
use core_privacy\tests\provider_testcase;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\writer;
use enrol_flatfile\privacy\provider;
/**
* Privacy tests for enrol_flatfile.
*
* @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider_test extends provider_testcase {
/** @var \stdClass $user1 a test user.*/
protected $user1;
/** @var \stdClass $user2 a test user.*/
protected $user2;
/** @var \stdClass $user3 a test user.*/
protected $user3;
/** @var \stdClass $user4 a test user.*/
protected $user4;
/** @var \context $coursecontext1 a course context.*/
protected $coursecontext1;
/** @var \context $coursecontext2 a course context.*/
protected $coursecontext2;
/** @var \context $coursecontext3 a course context.*/
protected $coursecontext3;
/**
* Called before every test.
*/
public function setUp(): void {
$this->resetAfterTest(true);
}
/**
* Verify that get_metadata returns the database table mapping.
*/
public function test_get_metadata(): void {
$collection = new collection('enrol_flatfile');
$collection = provider::get_metadata($collection);
$collectiondata = $collection->get_collection();
$this->assertNotEmpty($collectiondata);
$this->assertInstanceOf(\core_privacy\local\metadata\types\database_table::class, $collectiondata[0]);
}
/**
* Verify that the relevant course contexts are returned for users with pending enrolment records.
*/
public function test_get_contexts_for_user(): void {
global $DB;
// Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
$this->create_future_enrolments();
$this->assertEquals(5, $DB->count_records('enrol_flatfile'));
// We expect to see 2 entries for user1, in course1 and course3.
$contextlist = provider::get_contexts_for_userid($this->user1->id);
$this->assertEquals(2, $contextlist->count());
$contextids = $contextlist->get_contextids();
$this->assertContainsEquals($this->coursecontext1->id, $contextids);
$this->assertContainsEquals($this->coursecontext3->id, $contextids);
// And 1 for user2 on course2.
$contextlist = provider::get_contexts_for_userid($this->user2->id);
$this->assertEquals(1, $contextlist->count());
$contextids = $contextlist->get_contextids();
$this->assertContainsEquals($this->coursecontext2->id, $contextids);
}
/**
* Verify the export includes any future enrolment records for the user.
*/
public function test_export_user_data(): void {
// Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
$this->create_future_enrolments();
// Get contexts containing user data.
$contextlist = provider::get_contexts_for_userid($this->user1->id);
$this->assertEquals(2, $contextlist->count());
$approvedcontextlist = new approved_contextlist(
$this->user1,
'enrol_flatfile',
$contextlist->get_contextids()
);
// Export for the approved contexts.
provider::export_user_data($approvedcontextlist);
// Verify we see one future course enrolment in course1, and one in course3.
$subcontext = \core_enrol\privacy\provider::get_subcontext([get_string('pluginname', 'enrol_flatfile')]);
$writer = writer::with_context($this->coursecontext1);
$this->assertNotEmpty($writer->get_data($subcontext));
$writer = writer::with_context($this->coursecontext3);
$this->assertNotEmpty($writer->get_data($subcontext));
// Verify we have nothing in course 2 for this user.
$writer = writer::with_context($this->coursecontext2);
$this->assertEmpty($writer->get_data($subcontext));
}
/**
* Verify export will limit any future enrolment records to only those contextids provided.
*/
public function test_export_user_data_restricted_context_subset(): void {
// Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
$this->create_future_enrolments();
// Now, limit the export scope to just course1's context and verify only that data is seen in any export.
$subsetapprovedcontextlist = new approved_contextlist(
$this->user1,
'enrol_flatfile',
[$this->coursecontext1->id]
);
// Export for the approved contexts.
provider::export_user_data($subsetapprovedcontextlist);
// Verify we see one future course enrolment in course1 only.
$subcontext = \core_enrol\privacy\provider::get_subcontext([get_string('pluginname', 'enrol_flatfile')]);
$writer = writer::with_context($this->coursecontext1);
$this->assertNotEmpty($writer->get_data($subcontext));
// And nothing in the course3 context.
$writer = writer::with_context($this->coursecontext3);
$this->assertEmpty($writer->get_data($subcontext));
}
/**
* Verify that records can be deleted by context.
*/
public function test_delete_data_for_all_users_in_context(): void {
global $DB;
// Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
$this->create_future_enrolments();
// Verify we have 3 future enrolments for course 1.
$this->assertEquals(3, $DB->count_records('enrol_flatfile', ['courseid' => $this->coursecontext1->instanceid]));
// Now, run delete by context and confirm that all records are removed.
provider::delete_data_for_all_users_in_context($this->coursecontext1);
$this->assertEquals(0, $DB->count_records('enrol_flatfile', ['courseid' => $this->coursecontext1->instanceid]));
}
public function test_delete_data_for_user(): void {
global $DB;
// Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
$this->create_future_enrolments();
// Verify we have 2 future enrolments for course 1 and course 3.
$contextlist = provider::get_contexts_for_userid($this->user1->id);
$this->assertEquals(2, $contextlist->count());
$contextids = $contextlist->get_contextids();
$this->assertContainsEquals($this->coursecontext1->id, $contextids);
$this->assertContainsEquals($this->coursecontext3->id, $contextids);
$approvedcontextlist = new approved_contextlist(
$this->user1,
'enrol_flatfile',
$contextids
);
// Now, run delete for user and confirm that both records are removed.
provider::delete_data_for_user($approvedcontextlist);
$contextlist = provider::get_contexts_for_userid($this->user1->id);
$this->assertEquals(0, $contextlist->count());
$this->assertEquals(0, $DB->count_records('enrol_flatfile', ['userid' => $this->user1->id]));
}
/**
* Test for provider::get_users_in_context().
*/
public function test_get_users_in_context(): void {
global $DB;
// Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
$this->create_future_enrolments();
$this->assertEquals(5, $DB->count_records('enrol_flatfile'));
// We expect to see 3 entries for course1, and that's user1, user3 and user4.
$userlist = new \core_privacy\local\request\userlist($this->coursecontext1, 'enrol_flatfile');
provider::get_users_in_context($userlist);
$this->assertEqualsCanonicalizing(
[$this->user1->id, $this->user3->id, $this->user4->id],
$userlist->get_userids());
// And 1 for course2 which is for user2.
$userlist = new \core_privacy\local\request\userlist($this->coursecontext2, 'enrol_flatfile');
provider::get_users_in_context($userlist);
$this->assertEquals([$this->user2->id], $userlist->get_userids());
// And 1 for course3 which is for user1 again.
$userlist = new \core_privacy\local\request\userlist($this->coursecontext3, 'enrol_flatfile');
provider::get_users_in_context($userlist);
$this->assertEquals([$this->user1->id], $userlist->get_userids());
}
/**
* Test for provider::delete_data_for_users().
*/
public function test_delete_data_for_users(): void {
global $DB;
// Create, via flatfile syncing, the future enrolments entries in the enrol_flatfile table.
$this->create_future_enrolments();
// Verify we have 3 future enrolment for user 1, user 3 and user 4.
$userlist = new \core_privacy\local\request\userlist($this->coursecontext1, 'enrol_flatfile');
provider::get_users_in_context($userlist);
$this->assertEqualsCanonicalizing(
[$this->user1->id, $this->user3->id, $this->user4->id],
$userlist->get_userids());
$approveduserlist = new \core_privacy\local\request\approved_userlist($this->coursecontext1, 'enrol_flatfile',
[$this->user1->id, $this->user3->id]);
// Now, run delete for user and confirm that the record is removed.
provider::delete_data_for_users($approveduserlist);
$userlist = new \core_privacy\local\request\userlist($this->coursecontext1, 'enrol_flatfile');
provider::get_users_in_context($userlist);
$this->assertEquals([$this->user4->id], $userlist->get_userids());
$this->assertEquals(
[$this->user4->id],
$DB->get_fieldset_select('enrol_flatfile', 'userid', 'courseid = ?', [$this->coursecontext1->instanceid])
);
}
/**
* Helper to sync a file and create the enrol_flatfile DB entries, for use with the get, export and delete tests.
*/
protected function create_future_enrolments() {
global $CFG;
$this->user1 = $this->getDataGenerator()->create_user(['idnumber' => 'u1']);
$this->user2 = $this->getDataGenerator()->create_user(['idnumber' => 'u2']);
$this->user3 = $this->getDataGenerator()->create_user(['idnumber' => 'u3']);
$this->user4 = $this->getDataGenerator()->create_user(['idnumber' => 'u4']);
$course1 = $this->getDataGenerator()->create_course(['idnumber' => 'c1']);
$course2 = $this->getDataGenerator()->create_course(['idnumber' => 'c2']);
$course3 = $this->getDataGenerator()->create_course(['idnumber' => 'c3']);
$this->coursecontext1 = \context_course::instance($course1->id);
$this->coursecontext2 = \context_course::instance($course2->id);
$this->coursecontext3 = \context_course::instance($course3->id);
$now = time();
$future = $now + 60 * 60 * 5;
$farfuture = $now + 60 * 60 * 24 * 5;
$file = "$CFG->dataroot/enrol.txt";
$data = "add,student,u1,c1,$future,0
add,student,u2,c2,$future,0
add,student,u3,c1,$future,0
add,student,u4,c1,$future,0
add,student,u1,c3,$future,$farfuture";
file_put_contents($file, $data);
$trace = new \null_progress_trace();
$this->enable_plugin();
$flatfileplugin = enrol_get_plugin('flatfile');
$flatfileplugin->set_config('location', $file);
$flatfileplugin->sync($trace);
}
/**
* Enables the flatfile plugin for testing.
*/
protected function enable_plugin() {
$enabled = enrol_get_plugins(true);
$enabled['flatfile'] = true;
$enabled = array_keys($enabled);
set_config('enrol_plugins_enabled', implode(',', $enabled));
}
}
+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/>.
/**
* Flat file enrolment plugin version specification.
*
* @package enrol_flatfile
* @copyright 2010 Eugene Venter
* @author Eugene Venter
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDRR).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'enrol_flatfile'; // Full name of the plugin (used for diagnostics)
+124
View File
@@ -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/>.
/**
* Guest enrolment method external API
*
* @package enrol_guest
* @category external
* @copyright 2015 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.1
*/
use core_external\external_api;
use core_external\external_description;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->libdir . '/enrollib.php');
/**
* Guest enrolment method external API
*
* @package enrol_guest
* @category external
* @copyright 2015 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.1
*/
class enrol_guest_external extends external_api {
/**
* Returns description of get_instance_info() parameters.
*
* @return external_function_parameters
* @since Moodle 3.1
*/
public static function get_instance_info_parameters() {
return new external_function_parameters(
array('instanceid' => new external_value(PARAM_INT, 'Instance id of guest enrolment plugin.'))
);
}
/**
* Return guest enrolment instance information.
*
* @param int $instanceid instance id of guest enrolment plugin.
* @return array warnings and instance information.
* @since Moodle 3.1
*/
public static function get_instance_info($instanceid) {
global $DB;
$params = self::validate_parameters(self::get_instance_info_parameters(), array('instanceid' => $instanceid));
$warnings = array();
// Retrieve guest enrolment plugin.
$enrolplugin = enrol_get_plugin('guest');
if (empty($enrolplugin)) {
throw new moodle_exception('invaliddata', 'error');
}
self::validate_context(context_system::instance());
$enrolinstance = $DB->get_record('enrol', array('id' => $params['instanceid']), '*', MUST_EXIST);
$course = $DB->get_record('course', array('id' => $enrolinstance->courseid), '*', MUST_EXIST);
if (!core_course_category::can_view_course_info($course) && !can_access_course($course)) {
throw new moodle_exception('coursehidden');
}
$instanceinfo = $enrolplugin->get_enrol_info($enrolinstance);
// Specific instance information.
$instanceinfo->passwordrequired = $instanceinfo->requiredparam->passwordrequired;
unset($instanceinfo->requiredparam);
$result = array();
$result['instanceinfo'] = $instanceinfo;
$result['warnings'] = $warnings;
return $result;
}
/**
* Returns description of get_instance_info() result value.
*
* @return external_description
* @since Moodle 3.1
*/
public static function get_instance_info_returns() {
return new external_single_structure(
array(
'instanceinfo' => new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'Id of course enrolment instance'),
'courseid' => new external_value(PARAM_INT, 'Id of course'),
'type' => new external_value(PARAM_PLUGIN, 'Type of enrolment plugin'),
'name' => new external_value(PARAM_RAW, 'Name of enrolment plugin'),
'status' => new external_value(PARAM_BOOL, 'Is the enrolment enabled?'),
'passwordrequired' => new external_value(PARAM_BOOL, 'Is a password required?'),
)
),
'warnings' => new external_warnings()
)
);
}
}
+127
View File
@@ -0,0 +1,127 @@
<?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 enrol_guest\external;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use context_system;
use moodle_exception;
use core_text;
use stdClass;
/**
* This is the external method validating a guest password.
*
* @package enrol_guest
* @since Moodle 4.3
* @copyright 2023 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class validate_password extends external_api {
/**
* Webservice parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters(
[
'instanceid' => new external_value(PARAM_INT, 'instance id of guest enrolment plugin'),
'password' => new external_value(PARAM_RAW, 'the course password'),
]
);
}
/**
* Perform password validation.
*
* If password is correct: keep it as user preference.
* If password is not correct: remove existing user preference (if any)
*
* @throws moodle_exception
* @param int $instanceid instance id of guest enrolment plugin
* @param string $password the course password
* @return stdClass validation result info
*/
public static function execute(int $instanceid, string $password): stdClass {
global $CFG, $DB;
require_once($CFG->libdir . '/enrollib.php');
$params = external_api::validate_parameters(self::execute_parameters(), [
'instanceid' => $instanceid,
'password' => $password,
]);
$warnings = [];
$validated = false;
$hint = '';
// Retrieve guest enrolment plugin.
$enrolplugin = enrol_get_plugin('guest');
if (empty($enrolplugin)) {
throw new moodle_exception('invaliddata', 'error');
}
self::validate_context(context_system::instance());
$enrolinstance = $DB->get_record('enrol',
['id' => $params['instanceid'], 'status' => ENROL_INSTANCE_ENABLED], '*', MUST_EXIST);
$course = $DB->get_record('course', ['id' => $enrolinstance->courseid], '*', MUST_EXIST);
if (!\core_course_category::can_view_course_info($course) && !can_access_course($course)) {
throw new moodle_exception('coursehidden');
}
if ($enrolinstance->password) {
if ($params['password'] === $enrolinstance->password) {
$validated = true;
set_user_preference('enrol_guest_ws_password_' . $enrolinstance->id, $params['password']);
} else {
// Always unset in case there was something stored.
unset_user_preference('enrol_guest_ws_password_' . $enrolinstance->id);
if ($enrolplugin->get_config('showhint')) {
$hint = core_text::substr($enrolinstance->password, 0, 1);
$hint = get_string('passwordinvalidhint', 'enrol_guest', $hint);
}
}
}
$result = (object)[
'validated' => $validated,
'hint' => $hint,
'warnings' => $warnings,
];
return $result;
}
/**
* Describes the return information.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'validated' => new external_value(PARAM_BOOL, 'Whether the password was successfully validated'),
'hint' => new external_value(PARAM_RAW, 'Password hint (if enabled)', VALUE_OPTIONAL),
'warnings' => new external_warnings(),
]);
}
}
+41
View File
@@ -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/>.
/**
* Privacy Subsystem implementation for enrol_guest.
*
* @package enrol_guest
* @copyright 2018 Carlos Escobedo <carlos@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace enrol_guest\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for enrol_guest implementing null_provider.
*
* @copyright 2018 Carlos Escobedo <carlos@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';
}
}
+40
View File
@@ -0,0 +1,40 @@
<?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/>.
/**
* Capabilities for guest access plugin.
*
* @package enrol_guest
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
'enrol/guest:config' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_COURSE,
'archetypes' => array(
'manager' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
)
),
);
+43
View File
@@ -0,0 +1,43 @@
<?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/>.
/**
* Guest enrolment external functions and service definitions.
*
* @package enrol_guest
* @category external
* @copyright 2015 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.1
*/
$functions = array(
'enrol_guest_get_instance_info' => array(
'classname' => 'enrol_guest_external',
'methodname' => 'get_instance_info',
'description' => 'Return guest enrolment instance information.',
'type' => 'read',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'enrol_guest_validate_password' => [
'classname' => 'enrol_guest\external\validate_password',
'description' => 'Perform password validation.',
'type' => 'write',
'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE],
],
);
+39
View File
@@ -0,0 +1,39 @@
<?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 keeps track of upgrades to the guest enrolment plugin.
*
* @package enrol_guest
* @copyright 2011 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function xmldb_enrol_guest_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
+46
View File
@@ -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/>.
/**
* Strings for component 'enrol_guest', language 'en'.
*
* @package enrol_guest
* @copyright 2010 onwards Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['allowguests'] = 'This course allows guest users to enter';
$string['guest:config'] = 'Configure guest access instances';
$string['guestaccess_withpassword'] = 'Guest access requires password';
$string['guestaccess_withoutpassword'] = 'Guest access';
$string['password'] = 'Password';
$string['password_help'] = 'A password allows guest access to the course to be restricted to only those who know the password. Guests will be required to supply the password each time they access the course.';
$string['passwordinvalid'] = 'Incorrect access password, please try again';
$string['passwordinvalidhint'] = 'That access password was incorrect, please try again<br />
(Here\'s a hint - it starts with \'{$a}\')';
$string['pluginname'] = 'Guest access';
$string['pluginname_desc'] = 'Guest access plugin is only granting temporary access to courses, it is not actually enrolling users.';
$string['requirepassword'] = 'Require guest access password';
$string['requirepassword_desc'] = 'Require access password in new courses and prevent removing of access password from existing courses.';
$string['showhint'] = 'Show hint';
$string['showhint_desc'] = 'Show first letter of the guest access password.';
$string['status'] = 'Allow guest access';
$string['status_desc'] = 'Allow temporary guest access by default.';
$string['status_help'] = 'This setting determines whether a user can access the course as a guest, without being required to enrol.';
$string['status_link'] = 'enrol/guest';
$string['usepasswordpolicy'] = 'Use password policy';
$string['usepasswordpolicy_desc'] = 'Use standard password policy for guest access passwords.';
$string['privacy:metadata'] = 'The Guest access enrolment plugin does not store any personal data.';
+579
View File
@@ -0,0 +1,579 @@
<?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/>.
/**
* Guest access plugin.
*
* This plugin does not add any entries into the user_enrolments table,
* the access control is granted on the fly via the tricks in require_login().
*
* @package enrol_guest
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Class enrol_guest_plugin
*
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class enrol_guest_plugin extends enrol_plugin {
/**
* Returns optional enrolment information icons.
*
* This is used in course list for quick overview of enrolment options.
*
* We are not using single instance parameter because sometimes
* we might want to prevent icon repetition when multiple instances
* of one type exist. One instance may also produce several icons.
*
* @param array $instances all enrol instances of this type in one course
* @return array of pix_icon
*/
public function get_info_icons(array $instances) {
foreach ($instances as $instance) {
if ($instance->password !== '') {
return array(new pix_icon('withpassword', get_string('guestaccess_withpassword', 'enrol_guest'), 'enrol_guest'));
} else {
return array(new pix_icon('withoutpassword', get_string('guestaccess_withoutpassword', 'enrol_guest'), 'enrol_guest'));
}
}
}
/**
* Enrol a user using a given enrolment instance.
*
* @param stdClass $instance
* @param int $userid
* @param null $roleid
* @param int $timestart
* @param int $timeend
* @param null $status
* @param null $recovergrades
*/
public function enrol_user(stdClass $instance, $userid, $roleid = null, $timestart = 0, $timeend = 0, $status = null, $recovergrades = null) {
// no real enrolments here!
return;
}
/**
* Enrol a user from a given enrolment instance.
*
* @param stdClass $instance
* @param int $userid
*/
public function unenrol_user(stdClass $instance, $userid) {
// nothing to do, we never enrol here!
return;
}
/**
* Attempt to automatically gain temporary guest access to course,
* calling code has to make sure the plugin and instance are active.
*
* @param stdClass $instance course enrol instance
* @return bool|int false means no guest access, integer means end of cached time
*/
public function try_guestaccess(stdClass $instance) {
global $USER, $CFG;
$allow = false;
if ($instance->password === '') {
$allow = true;
} else if (isset($USER->enrol_guest_passwords[$instance->id])) { // this is a hack, ideally we should not add stuff to $USER...
if ($USER->enrol_guest_passwords[$instance->id] === $instance->password) {
$allow = true;
}
} else if (WS_SERVER) { // Mobile app mostly.
$storedpass = get_user_preferences('enrol_guest_ws_password_'. $instance->id);
// We check first if there is a supplied password.
if (!is_null($storedpass)) {
$allow = $storedpass === $instance->password;
if (!$allow) {
// Reset, probably the course password was changed.
unset_user_preference('enrol_guest_ws_password_' . $instance->id);
}
}
}
if ($allow) {
// Temporarily assign them some guest role for this context
$context = context_course::instance($instance->courseid);
load_temp_course_role($context, $CFG->guestroleid);
return ENROL_MAX_TIMESTAMP;
}
return false;
}
/**
* Returns true if the current user can add a new instance of enrolment plugin in course.
* @param int $courseid
* @return boolean
*/
public function can_add_instance($courseid) {
global $DB;
$context = context_course::instance($courseid, MUST_EXIST);
if (!has_capability('moodle/course:enrolconfig', $context) or !has_capability('enrol/guest:config', $context)) {
return false;
}
if ($DB->record_exists('enrol', array('courseid'=>$courseid, 'enrol'=>'guest'))) {
return false;
}
return true;
}
/**
* Creates course enrol form, checks if form submitted
* and enrols user if necessary. It can also redirect.
*
* @param stdClass $instance
* @return string html text, usually a form in a text box
*/
public function enrol_page_hook(stdClass $instance) {
global $CFG, $OUTPUT, $SESSION, $USER;
if ($instance->password === '') {
return null;
}
if (isset($USER->enrol['tempguest'][$instance->courseid]) and $USER->enrol['tempguest'][$instance->courseid] > time()) {
// no need to show the guest access when user can already enter course as guest
return null;
}
require_once("$CFG->dirroot/enrol/guest/locallib.php");
$form = new enrol_guest_enrol_form(NULL, $instance);
$instanceid = optional_param('instance', 0, PARAM_INT);
if ($instance->id == $instanceid) {
if ($data = $form->get_data()) {
// add guest role
$context = context_course::instance($instance->courseid);
$USER->enrol_guest_passwords[$instance->id] = $data->guestpassword; // this is a hack, ideally we should not add stuff to $USER...
if (isset($USER->enrol['tempguest'][$instance->courseid])) {
remove_temp_course_roles($context);
}
load_temp_course_role($context, $CFG->guestroleid);
$USER->enrol['tempguest'][$instance->courseid] = ENROL_MAX_TIMESTAMP;
// go to the originally requested page
if (!empty($SESSION->wantsurl)) {
$destination = $SESSION->wantsurl;
unset($SESSION->wantsurl);
} else {
$destination = "$CFG->wwwroot/course/view.php?id=$instance->courseid";
}
redirect($destination);
}
}
ob_start();
$form->display();
$output = ob_get_clean();
return $OUTPUT->box($output, 'generalbox');
}
/**
* Called after updating/inserting course.
*
* @param bool $inserted true if course just inserted
* @param object $course
* @param object $data form data
* @return void
*/
public function course_updated($inserted, $course, $data) {
global $DB;
if ($inserted) {
if (isset($data->enrol_guest_status_0)) {
$fields = array('status'=>$data->enrol_guest_status_0);
if ($fields['status'] == ENROL_INSTANCE_ENABLED) {
$fields['password'] = $data->enrol_guest_password_0;
} else {
if ($this->get_config('requirepassword')) {
$fields['password'] = generate_password(20);
}
}
$this->add_instance($course, $fields);
} else {
if ($this->get_config('defaultenrol')) {
$this->add_default_instance($course);
}
}
} else {
$instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'guest'));
foreach ($instances as $instance) {
$i = $instance->id;
if (isset($data->{'enrol_guest_status_'.$i})) {
$reset = ($instance->status != $data->{'enrol_guest_status_'.$i});
$instance->status = $data->{'enrol_guest_status_'.$i};
$instance->timemodified = time();
if ($instance->status == ENROL_INSTANCE_ENABLED) {
if ($instance->password !== $data->{'enrol_guest_password_'.$i}) {
$reset = true;
}
$instance->password = $data->{'enrol_guest_password_'.$i};
}
$DB->update_record('enrol', $instance);
\core\event\enrol_instance_updated::create_from_record($instance)->trigger();
if ($reset) {
$context = context_course::instance($course->id);
$context->mark_dirty();
}
}
}
}
}
/**
* Add new instance of enrol plugin.
* @param object $course
* @param array instance fields
* @return int id of new instance, null if can not be created
*/
public function add_instance($course, array $fields = NULL) {
$fields = (array)$fields;
if (!isset($fields['password'])) {
$fields['password'] = '';
}
return parent::add_instance($course, $fields);
}
/**
* Add new instance of enrol plugin with default settings.
* @param object $course
* @return int id of new instance
*/
public function add_default_instance($course) {
$fields = array('status'=>$this->get_config('status'));
if ($this->get_config('requirepassword')) {
$fields['password'] = generate_password(20);
}
return $this->add_instance($course, $fields);
}
/**
* Restore instance and map settings.
*
* @param restore_enrolments_structure_step $step
* @param stdClass $data
* @param stdClass $course
* @param int $oldid
*/
public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
global $DB;
if (!$DB->record_exists('enrol', array('courseid' => $data->courseid, 'enrol' => $this->get_name()))) {
$this->add_instance($course, (array)$data);
}
// No need to set mapping, we do not restore users or roles here.
$step->set_mapping('enrol', $oldid, 0);
}
/**
* Is it possible to delete enrol instance via standard UI?
*
* @param object $instance
* @return bool
*/
public function can_delete_instance($instance) {
$context = context_course::instance($instance->courseid);
return has_capability('enrol/guest:config', $context);
}
/**
* Is it possible to hide/show enrol instance via standard UI?
*
* @param stdClass $instance
* @return bool
*/
public function can_hide_show_instance($instance) {
$context = context_course::instance($instance->courseid);
if (!has_capability('enrol/guest:config', $context)) {
return false;
}
// If the instance is currently disabled, before it can be enabled, we must check whether the password meets the
// password policies.
if ($instance->status == ENROL_INSTANCE_DISABLED) {
if ($this->get_config('requirepassword')) {
if (empty($instance->password)) {
return false;
}
}
// Only check the password if it is set.
if (!empty($instance->password) && $this->get_config('usepasswordpolicy')) {
if (!check_password_policy($instance->password, $errmsg)) {
return false;
}
}
}
return true;
}
/**
* Get default settings for enrol_guest.
*
* @return array
*/
public function get_instance_defaults() {
$fields = array();
$fields['status'] = $this->get_config('status');
return $fields;
}
/**
* Return information for enrolment instance containing list of parameters required
* for enrolment, name of enrolment plugin etc.
*
* @param stdClass $instance enrolment instance
* @return stdClass instance info.
* @since Moodle 3.1
*/
public function get_enrol_info(stdClass $instance) {
$instanceinfo = new stdClass();
$instanceinfo->id = $instance->id;
$instanceinfo->courseid = $instance->courseid;
$instanceinfo->type = $this->get_name();
$instanceinfo->name = $this->get_instance_name($instance);
$instanceinfo->status = $instance->status == ENROL_INSTANCE_ENABLED;
// Specifics enrolment method parameters.
$instanceinfo->requiredparam = new stdClass();
$instanceinfo->requiredparam->passwordrequired = !empty($instance->password);
// If the plugin is enabled, return the URL for obtaining more information.
if ($instanceinfo->status) {
$instanceinfo->wsfunction = 'enrol_guest_get_instance_info';
}
return $instanceinfo;
}
/**
* Return an array of valid options for the status.
*
* @return array
*/
protected function get_status_options() {
$options = array(ENROL_INSTANCE_ENABLED => get_string('yes'),
ENROL_INSTANCE_DISABLED => get_string('no'));
return $options;
}
/**
* Add elements to the edit instance form.
*
* @param stdClass $instance
* @param MoodleQuickForm $mform
* @param context $context
* @return bool
*/
public function edit_instance_form($instance, MoodleQuickForm $mform, $context) {
global $CFG;
$options = $this->get_status_options();
$mform->addElement('select', 'status', get_string('status', 'enrol_guest'), $options);
$mform->addHelpButton('status', 'status', 'enrol_guest');
$mform->setDefault('status', $this->get_config('status'));
$mform->setAdvanced('status', $this->get_config('status_adv'));
$mform->addElement('passwordunmask', 'password', get_string('password', 'enrol_guest'));
$mform->addHelpButton('password', 'password', 'enrol_guest');
// If we have a new instance and the password is required - make sure it is set. For existing
// instances we do not force the password to be required as it may have been set to empty before
// the password was required. We check in the validation function whether this check is required
// for existing instances.
if (empty($instance->id) && $this->get_config('requirepassword')) {
$mform->addRule('password', get_string('required'), 'required', null);
}
}
/**
* We are a good plugin and don't invent our own UI/validation code path.
*
* @return boolean
*/
public function use_standard_editing_ui() {
return true;
}
/**
* Perform custom validation of the data used to edit the instance.
*
* @param array $data array of ("fieldname"=>value) of submitted data
* @param array $files array of uploaded files "element_name"=>tmp_file_path
* @param object $instance The instance loaded from the DB
* @param context $context The context of the instance we are editing
* @return array of "element_name"=>"error_description" if there are errors,
* or an empty array if everything is OK.
* @return void
*/
public function edit_instance_validation($data, $files, $instance, $context) {
$errors = array();
$checkpassword = false;
if ($data['id']) {
// Check the password if we are enabling the plugin again.
if (($instance->status == ENROL_INSTANCE_DISABLED) && ($data['status'] == ENROL_INSTANCE_ENABLED)) {
$checkpassword = true;
}
// Check the password if the instance is enabled and the password has changed.
if (($data['status'] == ENROL_INSTANCE_ENABLED) && ($instance->password !== $data['password'])) {
$checkpassword = true;
}
} else {
$checkpassword = true;
}
if ($checkpassword) {
$require = $this->get_config('requirepassword');
$policy = $this->get_config('usepasswordpolicy');
if ($require && trim($data['password']) === '') {
$errors['password'] = get_string('required');
} else if (!empty($data['password']) && $policy) {
$errmsg = '';
if (!check_password_policy($data['password'], $errmsg)) {
$errors['password'] = $errmsg;
}
}
}
$validstatus = array_keys($this->get_status_options());
$tovalidate = array(
'status' => $validstatus
);
$typeerrors = $this->validate_param_types($data, $tovalidate);
$errors = array_merge($errors, $typeerrors);
return $errors;
}
/**
* Check if enrolment plugin is supported in csv course upload.
*
* @return bool
*/
public function is_csv_upload_supported(): bool {
return true;
}
/**
* Finds matching instances for a given course.
*
* @param array $enrolmentdata enrolment data.
* @param int $courseid Course ID.
* @return stdClass|null Matching instance
*/
public function find_instance(array $enrolmentdata, int $courseid): ?stdClass {
$instances = enrol_get_instances($courseid, false);
$instance = null;
foreach ($instances as $i) {
if ($i->enrol == 'guest') {
// There can be only one guest enrol instance so find first available.
$instance = $i;
break;
}
}
return $instance;
}
/**
* Fill custom fields data for a given enrolment plugin.
*
* @param array $enrolmentdata enrolment data.
* @param int $courseid Course ID.
* @return array Updated enrolment data with custom fields info.
*/
public function fill_enrol_custom_fields(array $enrolmentdata, int $courseid): array {
return $enrolmentdata + ['password' => ''];
}
/**
* Updates enrol plugin instance with provided data.
* @param int $courseid Course ID.
* @param array $enrolmentdata enrolment data.
* @param stdClass $instance Instance to update.
*
* @return stdClass updated instance
*/
public function update_enrol_plugin_data(int $courseid, array $enrolmentdata, stdClass $instance): stdClass {
if (!empty($enrolmentdata['password'])) {
$instance->password = $enrolmentdata['password'];
}
return parent::update_enrol_plugin_data($courseid, $enrolmentdata, $instance);
}
/**
* Check if data is valid for a given enrolment plugin
*
* @param array $enrolmentdata enrolment data to validate.
* @param int|null $courseid Course ID.
* @return array Errors
*/
public function validate_enrol_plugin_data(array $enrolmentdata, ?int $courseid = null): array {
// If password is omitted or empty in csv it will be generated automatically if it is a required policy.
$errors = parent::validate_enrol_plugin_data($enrolmentdata, $courseid);
$policy = $this->get_config('usepasswordpolicy');
if (!empty($enrolmentdata['password']) && $policy) {
$errarray = get_password_policy_errors($enrolmentdata['password']);
foreach ($errarray as $i => $err) {
$errors['enrol_guest' . $i] = $err;
}
}
return $errors;
}
}
/**
* Get icon mapping for font-awesome.
*/
function enrol_guest_get_fontawesome_icon_map() {
return [
'enrol_guest:withpassword' => 'fa-key',
'enrol_guest:withoutpassword' => 'fa-unlock-alt',
];
}
+74
View File
@@ -0,0 +1,74 @@
<?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/>.
/**
* Guest access plugin implementation.
*
* @package enrol_guest
* @copyright 2010 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once("$CFG->libdir/formslib.php");
class enrol_guest_enrol_form extends moodleform {
protected $instance;
public function definition() {
$mform = $this->_form;
$instance = $this->_customdata;
$this->instance = $instance;
$plugin = enrol_get_plugin('guest');
$heading = $plugin->get_instance_name($instance);
$mform->addElement('header', 'guestheader', $heading);
$mform->addElement('password', 'guestpassword', get_string('password', 'enrol_guest'));
$this->add_action_buttons(false, get_string('submit'));
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->setDefault('id', $instance->courseid);
$mform->addElement('hidden', 'instance');
$mform->setType('instance', PARAM_INT);
$mform->setDefault('instance', $instance->id);
}
public function validation($data, $files) {
global $DB, $CFG;
$errors = parent::validation($data, $files);
$instance = $this->instance;
if ($instance->password !== '') {
if ($data['guestpassword'] !== $instance->password) {
$plugin = enrol_get_plugin('guest');
if ($plugin->get_config('showhint')) {
$hint = core_text::substr($instance->password, 0, 1);
$errors['guestpassword'] = get_string('passwordinvalidhint', 'enrol_guest', $hint);
} else {
$errors['guestpassword'] = get_string('passwordinvalid', 'enrol_guest');
}
}
}
return $errors;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

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