diff --git a/enrol/manual/cli/sync.php b/enrol/manual/cli/sync.php new file mode 100644 index 00000000000..c18488e366c --- /dev/null +++ b/enrol/manual/cli/sync.php @@ -0,0 +1,65 @@ +. + +/** + * CLI update for manual enrolments expiration. + * + * 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_manual + * @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(dirname(dirname(dirname(dirname(__FILE__)))).'/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 = + "Execute manual enrolments expiration sync. + +Options: +-v, --verbose Print verbose progress information +-h, --help Print out this help + +Example: +\$sudo -u www-data /usr/bin/php enrol/self/manual/sync.php +"; + + echo $help; + die; +} + +$verbose = !empty($options['verbose']); + +$plugin = enrol_get_plugin('manual'); + +$result = $plugin->sync(null, $verbose); + +exit($result); diff --git a/enrol/manual/lang/en/enrol_manual.php b/enrol/manual/lang/en/enrol_manual.php index 9e51fd1f314..7abb510d69a 100644 --- a/enrol/manual/lang/en/enrol_manual.php +++ b/enrol/manual/lang/en/enrol_manual.php @@ -37,6 +37,8 @@ $string['editenrolment'] = 'Edit enrolment'; $string['editselectedusers'] = 'Edit selected user enrolments'; $string['enrolledincourserole'] = 'Enrolled in "{$a->course}" as "{$a->role}"'; $string['enrolusers'] = 'Enrol users'; +$string['expiredaction'] = 'Enrolment expiration 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['manual:config'] = 'Configure manual enrol instances'; $string['manual:enrol'] = 'Enrol users'; $string['manual:manage'] = 'Manage user enrolments'; diff --git a/enrol/manual/lib.php b/enrol/manual/lib.php index fb648e07a6f..d55c68c6dfa 100644 --- a/enrol/manual/lib.php +++ b/enrol/manual/lib.php @@ -257,6 +257,106 @@ class enrol_manual_plugin extends enrol_plugin { return $button; } + /** + * Enrol cron support. + * @return void + */ + public function cron() { + $this->sync(null, true); + } + + /** + * Sync all meta course links. + * + * @param int $courseid one course, empty mean all + * @param bool $verbose verbose CLI output + * @return int 0 means ok, 1 means error, 2 means plugin disabled + */ + public function sync($courseid = null, $verbose = false) { + global $DB; + + if (!enrol_is_enabled('manual')) { + return 2; + } + + // Unfortunately this may take a long time, execution can be interrupted safely here. + @set_time_limit(0); + raise_memory_limit(MEMORY_HUGE); + + if ($verbose) { + mtrace('Verifying manual enrolment expiration...'); + } + + $params = array('now'=>time(), 'useractive'=>ENROL_USER_ACTIVE, 'courselevel'=>CONTEXT_COURSE); + $coursesql = ""; + if ($courseid) { + $coursesql = "AND e.courseid = :courseid"; + $params['courseid'] = $courseid; + } + + // Deal with expired accounts. + $action = $this->get_config('expiredaction', ENROL_EXT_REMOVED_KEEP); + + if ($action == ENROL_EXT_REMOVED_UNENROL) { + $instances = array(); + $sql = "SELECT ue.*, e.courseid, c.id AS contextid + FROM {user_enrolments} ue + JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual') + JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel) + WHERE ue.timeend > 0 AND ue.timeend < :now + $coursesql"; + $rs = $DB->get_recordset_sql($sql, $params); + foreach ($rs as $ue) { + if (empty($instances[$ue->enrolid])) { + $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid)); + } + $instance = $instances[$ue->enrolid]; + // Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here. + role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0), true); + $this->unenrol_user($instance, $ue->userid); + if ($verbose) { + mtrace(" unenrolling expired user $ue->userid from course $instance->courseid"); + } + } + $rs->close(); + unset($instances); + + } else if ($action == ENROL_EXT_REMOVED_SUSPENDNOROLES) { + $instances = array(); + $sql = "SELECT ue.*, e.courseid, c.id AS contextid + FROM {user_enrolments} ue + JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'manual') + JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :courselevel) + WHERE ue.timeend > 0 AND ue.timeend < :now + AND ue.status = :useractive + $coursesql"; + $rs = $DB->get_recordset_sql($sql, $params); + foreach ($rs as $ue) { + if (empty($instances[$ue->enrolid])) { + $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid)); + } + $instance = $instances[$ue->enrolid]; + // Always remove all manually assigned roles here, this may break enrol_self roles but we do not want hardcoded hacks here. + role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$ue->contextid, 'component'=>'', 'itemid'=>0), true); + $this->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED); + if ($verbose) { + mtrace(" suspending expired user $ue->userid in course $instance->courseid"); + } + } + $rs->close(); + unset($instances); + + } else { + // ENROL_EXT_REMOVED_KEEP means no changes. + } + + if ($verbose) { + mtrace('...manual enrolment updates finished.'); + } + + return 0; + } + /** * Gets an array of the user enrolment actions. * diff --git a/enrol/manual/settings.php b/enrol/manual/settings.php index 79686aab792..f7934850e30 100644 --- a/enrol/manual/settings.php +++ b/enrol/manual/settings.php @@ -29,6 +29,15 @@ if ($ADMIN->fulltree) { //--- general settings ----------------------------------------------------------------------------------- $settings->add(new admin_setting_heading('enrol_manual_settings', '', get_string('pluginname_desc', 'enrol_manual'))); + // Note: let's reuse the ext sync constants and strings here, internally it is very similar, + // it describes what should happend when users are not supposed to be enerolled 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_manual/expiredaction', get_string('expiredaction', 'enrol_manual'), get_string('expiredaction_help', 'enrol_manual'), ENROL_EXT_REMOVED_KEEP, $options)); + //--- enrol instance defaults ---------------------------------------------------------------------------- $settings->add(new admin_setting_heading('enrol_manual_defaults', diff --git a/enrol/manual/tests/lib_test.php b/enrol/manual/tests/lib_test.php index 745bac07b65..6d712f75a8a 100644 --- a/enrol/manual/tests/lib_test.php +++ b/enrol/manual/tests/lib_test.php @@ -201,4 +201,104 @@ class enrol_manual_lib_testcase extends advanced_testcase { enrol_manual_migrate_plugin_enrolments('manual'); enrol_manual_migrate_plugin_enrolments('yyyy'); } + + public function test_expired() { + global $DB; + $this->resetAfterTest(); + + $manualplugin = enrol_get_plugin('manual'); + + $now = time(); + + // 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(); + $course3 = $this->getDataGenerator()->create_course(); + $context1 = context_course::instance($course1->id); + $context2 = context_course::instance($course2->id); + $context3 = context_course::instance($course3->id); + + $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'manual'))); + $instance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST); + $this->assertEquals($studentrole->id, $instance1->roleid); + $instance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST); + $this->assertEquals($studentrole->id, $instance2->roleid); + $instance3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST); + $this->assertEquals($studentrole->id, $instance3->roleid); + + $this->assertEquals(0, $DB->count_records('user_enrolments')); + $this->assertEquals(0, $DB->count_records('role_assignments')); + + $manualplugin->enrol_user($instance1, $user1->id, $studentrole->id); + $manualplugin->enrol_user($instance1, $user2->id, $studentrole->id); + $manualplugin->enrol_user($instance1, $user3->id, $studentrole->id, 0, $now-60); + + $manualplugin->enrol_user($instance3, $user1->id, $studentrole->id, 0, 0); + $manualplugin->enrol_user($instance3, $user2->id, $studentrole->id, 0, $now+60*60); + $manualplugin->enrol_user($instance3, $user3->id, $teacherrole->id, 0, $now-60*60); + + role_assign($managerrole->id, $user4->id, $context1->id); + + $this->assertEquals(6, $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))); + + // Execute tests. + + $this->assertEquals(ENROL_EXT_REMOVED_KEEP, $manualplugin->get_config('expiredaction')); + $manualplugin->sync(null, false); + $this->assertEquals(6, $DB->count_records('user_enrolments')); + $this->assertEquals(7, $DB->count_records('role_assignments')); + + + $manualplugin->set_config('expiredaction', ENROL_EXT_REMOVED_SUSPENDNOROLES); + $manualplugin->sync($course2->id, false); + $this->assertEquals(6, $DB->count_records('user_enrolments')); + $this->assertEquals(7, $DB->count_records('role_assignments')); + + $this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user3->id, 'roleid'=>$studentrole->id))); + $this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context3->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id))); + $manualplugin->sync(null, false); + $this->assertEquals(6, $DB->count_records('user_enrolments')); + $this->assertEquals(5, $DB->count_records('role_assignments')); + $this->assertEquals(4, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id))); + $this->assertEquals(0, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->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'=>$context3->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id))); + + + $manualplugin->set_config('expiredaction', ENROL_EXT_REMOVED_UNENROL); + + role_assign($studentrole->id, $user3->id, $context1->id); + role_assign($teacherrole->id, $user3->id, $context3->id); + $this->assertEquals(6, $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))); + + $manualplugin->sync(null, false); + $this->assertEquals(4, $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'=>$instance3->id, 'userid'=>$user3->id))); + $this->assertEquals(5, $DB->count_records('role_assignments')); + $this->assertEquals(4, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id))); + $this->assertEquals(0, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id))); + $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$managerrole->id))); + } } diff --git a/enrol/manual/version.php b/enrol/manual/version.php index 2107a671994..a878cb2b2be 100644 --- a/enrol/manual/version.php +++ b/enrol/manual/version.php @@ -24,6 +24,7 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2012061700; // The current plugin version (Date: YYYYMMDDXX) -$plugin->requires = 2012061700; // Requires this Moodle version +$plugin->version = 2012091500; // The current plugin version (Date: YYYYMMDDXX) +$plugin->requires = 2012091400; // Requires this Moodle version $plugin->component = 'enrol_manual'; // Full name of the plugin (used for diagnostics) +$plugin->cron = 600;