--- /dev/null
+<?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/>.
+
+/**
+ * Validate that the current db structure matches the install.xml files.
+ *
+ * @package core
+ * @copyright 2014 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author Petr Skoda <petr.skoda@totaralms.com>
+ */
+
+define('CLI_SCRIPT', true);
+
+require(__DIR__ . '/../../config.php');
+require_once($CFG->libdir.'/clilib.php');
+
+$help = "Validate database structure
+
+Options:
+-h, --help Print out this help.
+
+Example:
+\$ sudo -u www-data /usr/bin/php admin/cli/check_database_schema.php
+";
+
+list($options, $unrecognized) = cli_get_params(
+ array(
+ 'help' => false,
+ ),
+ array(
+ 'h' => 'help',
+ )
+);
+
+if ($options['help']) {
+ echo $help;
+ exit(0);
+}
+
+if (empty($CFG->version)) {
+ echo "Database is not yet installed.\n";
+ exit(2);
+}
+
+$dbmanager = $DB->get_manager();
+$schema = $dbmanager->get_install_xml_schema();
+
+if (!$errors = $dbmanager->check_database_schema($schema)) {
+ echo "Database structure is ok.\n";
+ exit(0);
+}
+
+foreach ($errors as $table => $items) {
+ cli_separator();
+ echo "$table\n";
+ foreach ($items as $item) {
+ echo " * $item\n";
+ }
+}
+cli_separator();
+
+exit(1);
require($CFG->dirroot.'/version.php');
$CFG->target_release = $release;
-$_SESSION = array();
-$_SESSION['SESSION'] = new stdClass();
-$_SESSION['SESSION']->lang = $CFG->lang;
-$_SESSION['USER'] = new stdClass();
-$_SESSION['USER']->id = 0;
-$_SESSION['USER']->mnethostid = 1;
-
+\core\session\manager::init_empty_session();
global $SESSION;
global $USER;
-$SESSION = &$_SESSION['SESSION'];
-$USER = &$_SESSION['USER'];
global $COURSE;
$COURSE = new stdClass();
-<?PHP
+<?php
// Allows the admin to configure mnet stuff
array(
'context' => $context,
'objectid' => $roleid,
- 'courseid' => $courseid
)
);
$entry['other'] = serialize($entry['other']);
$entry['origin'] = $PAGE->requestorigin;
$entry['ip'] = $PAGE->requestip;
- $entry['realuserid'] = \core\session\manager::is_loggedinas() ? $_SESSION['USER']->realuser : null;
+ $entry['realuserid'] = \core\session\manager::is_loggedinas() ? $GLOBALS['USER']->realuser : null;
$this->buffer[] = $entry;
$this->count++;
array('context' => context_module::instance($module2->cmid), 'other' => array('sample' => 6, 'xx' => 9)));
$event2->trigger();
- $_SESSION['SESSION'] = new \stdClass();
- $this->setUser(0);
+ \core\session\manager::init_empty_session();
$this->assertFalse(\core\session\manager::is_loggedinas());
$logs = $DB->get_records('logstore_standard_log', array(), 'id ASC');
$event2->trigger();
logstore_standard_restore::hack_executing(0);
- $_SESSION['SESSION'] = new \stdClass();
- $this->setUser(0);
+ \core\session\manager::init_empty_session();
$this->assertFalse(\core\session\manager::is_loggedinas());
$logs = $DB->get_records('logstore_standard_log', array(), 'id ASC');
if ($doupdate or $existinguser->password !== $oldpw) {
// We want only users that were really updated.
- user_update_user($existinguser, false);
+ user_update_user($existinguser, false, false);
$upt->track('status', $struserupdated);
$usersupdated++;
}
}
+ // Trigger event.
+ \core\event\user_updated::create_from_userid($existinguser->id)->trigger();
+
} else {
// no user information changed
$upt->track('status', $struseruptodate);
$upt->track('password', '-', 'normal', false);
}
- $user->id = user_create_user($user, false);
+ $user->id = user_create_user($user, false, false);
$upt->track('username', html_writer::link(new moodle_url('/user/profile.php', array('id'=>$user->id)), s($user->username)), 'normal', false);
// pre-process custom profile menu fields data from csv file
set_user_preference('create_password', 1, $user);
}
+ // Trigger event.
+ \core\event\user_created::create_from_userid($user->id)->trigger();
+
$upt->track('status', $struseradded);
$upt->track('id', $user->id, 'normal', false);
$usersnew++;
}
}
- /**
- * Logout from the CAS
- *
- */
- function prelogout_hook() {
- global $CFG;
-
- if (!empty($this->config->logoutcas)) {
- $backurl = $CFG->wwwroot;
- $this->connectCAS();
- phpCAS::logoutWithURL($backurl);
- }
- }
/**
* Connect to the CAS (clientcas connection or proxycas connection)
}
}
}
+
+ /**
+ * Post logout hook.
+ *
+ * Note: this method replace the prelogout_hook method to avoid redirect to CAS logout
+ * before the event userlogout being triggered.
+ *
+ * @param stdClass $user clone of USER object object before the user session was terminated
+ */
+ public function postlogout_hook($user) {
+ global $CFG;
+ // Only redirect to CAS logout if the user is logged as a CAS user.
+ if (!empty($this->config->logoutcas) && $user->auth == $this->authtype) {
+ $backurl = $CFG->wwwroot;
+ $this->connectCAS();
+ phpCAS::logoutWithRedirectService($backurl);
+ }
+ }
}
$user->calendartype = $CFG->calendartype;
}
- $user->id = user_create_user($user, false);
+ $user->id = user_create_user($user, false, false);
// Save any custom profile field information.
profile_save_data($user);
+ // Trigger event.
+ \core\event\user_created::create_from_userid($user->id)->trigger();
+
if (! send_confirmation_email($user)) {
print_error('auth_emailnoemail','auth_email');
}
print_error('auth_ldap_create_error', 'auth_ldap');
}
- $user->id = user_create_user($user, false);
+ $user->id = user_create_user($user, false, false);
// Save any custom profile field information
profile_save_data($user);
$user = $DB->get_record('user', array('id'=>$user->id));
+ \core\event\user_created::create_from_userid($user->id)->trigger();
+
if (! send_confirmation_email($user)) {
print_error('noemail', 'auth_ldap');
}
}
}
}
- user_update_user($newuser, false);
+ user_update_user($newuser, false, false);
}
} else {
return false;
$this->assertEventLegacyLogData($expectedlog, $event);
}
+ /**
+ * Test logging in via LDAP calls a user_loggedin event.
+ */
+ public function test_ldap_user_signup() {
+ global $CFG, $DB;
+
+ // User to create.
+ $user = array(
+ 'username' => 'usersignuptest1',
+ 'password' => 'Moodle2014!',
+ 'idnumber' => 'idsignuptest1',
+ 'firstname' => 'First Name User Test 1',
+ 'lastname' => 'Last Name User Test 1',
+ 'middlename' => 'Middle Name User Test 1',
+ 'lastnamephonetic' => '最後のお名前のテスト一号',
+ 'firstnamephonetic' => 'お名前のテスト一号',
+ 'alternatename' => 'Alternate Name User Test 1',
+ 'email' => 'usersignuptest1@email.com',
+ 'description' => 'This is a description for user 1',
+ 'city' => 'Perth',
+ 'country' => 'au',
+ 'mnethostid' => $CFG->mnet_localhost_id,
+ 'auth' => 'ldap'
+ );
+
+ if (!extension_loaded('ldap')) {
+ $this->markTestSkipped('LDAP extension is not loaded.');
+ }
+
+ $this->resetAfterTest();
+
+ require_once($CFG->dirroot.'/auth/ldap/auth.php');
+ require_once($CFG->libdir.'/ldaplib.php');
+
+ if (!defined('TEST_AUTH_LDAP_HOST_URL') or !defined('TEST_AUTH_LDAP_BIND_DN') or !defined('TEST_AUTH_LDAP_BIND_PW') or !defined('TEST_AUTH_LDAP_DOMAIN')) {
+ $this->markTestSkipped('External LDAP test server not configured.');
+ }
+
+ // Make sure we can connect the server.
+ $debuginfo = '';
+ if (!$connection = ldap_connect_moodle(TEST_AUTH_LDAP_HOST_URL, 3, 'rfc2307', TEST_AUTH_LDAP_BIND_DN, TEST_AUTH_LDAP_BIND_PW, LDAP_DEREF_NEVER, $debuginfo, false)) {
+ $this->markTestSkipped('Can not connect to LDAP test server: '.$debuginfo);
+ }
+
+ $this->enable_plugin();
+
+ // Create new empty test container.
+ $topdn = 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN;
+
+ $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
+
+ $o = array();
+ $o['objectClass'] = array('dcObject', 'organizationalUnit');
+ $o['dc'] = 'moodletest';
+ $o['ou'] = 'MOODLETEST';
+ if (!ldap_add($connection, 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN, $o)) {
+ $this->markTestSkipped('Can not create test LDAP container.');
+ }
+
+ // Create a few users.
+ $o = array();
+ $o['objectClass'] = array('organizationalUnit');
+ $o['ou'] = 'users';
+ ldap_add($connection, 'ou='.$o['ou'].','.$topdn, $o);
+
+ // Configure the plugin a bit.
+ set_config('host_url', TEST_AUTH_LDAP_HOST_URL, 'auth/ldap');
+ set_config('start_tls', 0, 'auth/ldap');
+ set_config('ldap_version', 3, 'auth/ldap');
+ set_config('ldapencoding', 'utf-8', 'auth/ldap');
+ set_config('pagesize', '2', 'auth/ldap');
+ set_config('bind_dn', TEST_AUTH_LDAP_BIND_DN, 'auth/ldap');
+ set_config('bind_pw', TEST_AUTH_LDAP_BIND_PW, 'auth/ldap');
+ set_config('user_type', 'rfc2307', 'auth/ldap');
+ set_config('contexts', 'ou=users,'.$topdn, 'auth/ldap');
+ set_config('search_sub', 0, 'auth/ldap');
+ set_config('opt_deref', LDAP_DEREF_NEVER, 'auth/ldap');
+ set_config('user_attribute', 'cn', 'auth/ldap');
+ set_config('memberattribute', 'memberuid', 'auth/ldap');
+ set_config('memberattribute_isdn', 0, 'auth/ldap');
+ set_config('creators', 'cn=creators,'.$topdn, 'auth/ldap');
+ set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth/ldap');
+
+ set_config('field_map_email', 'mail', 'auth/ldap');
+ set_config('field_updatelocal_email', 'oncreate', 'auth/ldap');
+ set_config('field_updateremote_email', '0', 'auth/ldap');
+ set_config('field_lock_email', 'unlocked', 'auth/ldap');
+
+ set_config('field_map_firstname', 'givenName', 'auth/ldap');
+ set_config('field_updatelocal_firstname', 'oncreate', 'auth/ldap');
+ set_config('field_updateremote_firstname', '0', 'auth/ldap');
+ set_config('field_lock_firstname', 'unlocked', 'auth/ldap');
+
+ set_config('field_map_lastname', 'sn', 'auth/ldap');
+ set_config('field_updatelocal_lastname', 'oncreate', 'auth/ldap');
+ set_config('field_updateremote_lastname', '0', 'auth/ldap');
+ set_config('field_lock_lastname', 'unlocked', 'auth/ldap');
+ set_config('passtype', 'md5', 'auth/ldap');
+ set_config('create_context', 'ou=users,'.$topdn, 'auth/ldap');
+
+ $this->assertEquals(2, $DB->count_records('user'));
+ $this->assertEquals(0, $DB->count_records('role_assignments'));
+
+ /** @var auth_plugin_ldap $auth */
+ $auth = get_auth_plugin('ldap');
+
+ $sink = $this->redirectEvents();
+ $auth->user_signup((object)$user, false);
+ $this->assertDebuggingCalled('Not sending email due to $CFG->noemailever config setting');
+ $events = $sink->get_events();
+ $sink->close();
+
+ // Verify 2 events get generated.
+ $this->assertCount(2, $events);
+
+ // Get record from db.
+ $dbuser = $DB->get_record('user', array('username' => $user['username']));
+ $user['id'] = $dbuser->id;
+
+ // Last event is user_created.
+ $event = array_pop($events);
+ $this->assertInstanceOf('\core\event\user_created', $event);
+ $this->assertEquals($user['id'], $event->objectid);
+ $this->assertEquals('user_created', $event->get_legacy_eventname());
+ $this->assertEquals(context_user::instance($user['id']), $event->get_context());
+ $expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser));
+ $this->assertEventLegacyLogData($expectedlogdata, $event);
+
+ // First event is user_password_updated.
+ $event = array_pop($events);
+ $this->assertInstanceOf('\core\event\user_password_updated', $event);
+ $this->assertEventContextNotUsed($event);
+
+ // Delete user which we just created.
+ ldap_delete($connection, 'cn='.$user['username'].',ou=users,'.$topdn);
+ }
+
protected function create_ldap_user($connection, $topdn, $i) {
$o = array();
$o['objectClass'] = array('inetOrgPerson', 'organizationalPerson', 'person', 'posixAccount');
Example file:
--
-<?PHP
+<?php
// Set the zip code and the adress
if ($_SERVER[$this->config->field_map_address] != '')
self::apply_general_config_defaults($controller);
break;
case backup::MODE_AUTOMATED:
- // TODO: Move the loading from automatic stuff to here
+ // Load the automated defaults.
+ self::apply_auto_config_defaults($controller);
break;
default:
// Nothing to do for other modes (IMPORT/HUB...). Some day we
}
}
+ /**
+ * Sets the controller settings default values from the automated backup config.
+ *
+ * @param backup_controller $controller
+ */
+ private static function apply_auto_config_defaults(backup_controller $controller) {
+ $settings = array(
+ // Config name => Setting name.
+ 'backup_auto_users' => 'users',
+ 'backup_auto_role_assignments' => 'role_assignments',
+ 'backup_auto_activities' => 'activities',
+ 'backup_auto_blocks' => 'blocks',
+ 'backup_auto_filters' => 'filters',
+ 'backup_auto_comments' => 'comments',
+ 'backup_auto_badges' => 'badges',
+ 'backup_auto_userscompletion' => 'userscompletion',
+ 'backup_auto_logs' => 'logs',
+ 'backup_auto_histories' => 'grade_histories',
+ 'backup_auto_questionbank' => 'questionbank'
+ );
+ $plan = $controller->get_plan();
+ foreach ($settings as $config => $settingname) {
+ $value = get_config('backup', $config);
+ if ($value === false) {
+ // The setting is not set.
+ $controller->log('Could not find a value for the config ' . $config, BACKUP::LOG_DEBUG);
+ continue;
+ }
+ if ($plan->setting_exists($settingname)) {
+ $setting = $plan->get_setting($settingname);
+ $setting->set_value($value);
+ } else {
+ $controller->log('Unknown setting: ' . $settingname, BACKUP::LOG_DEBUG);
+ }
+ }
+ }
+
/**
* Sets the controller settings default values from the backup config.
*
// Ignore this because the config has not been set. get_config
// returns false if a setting doesn't exist, '0' is returned when
// the configuration is set to false.
+ $controller->log('Could not find a value for the config ' . $config, BACKUP::LOG_DEBUG);
continue;
}
$locked = (get_config('backup', $config.'_locked') == true);
$setting->set_status(base_setting::LOCKED_BY_CONFIG);
}
}
+ } else {
+ $controller->log('Unknown setting: ' . $setting, BACKUP::LOG_DEBUG);
}
}
}
// Even if a file has been deleted since the backup was made, the file metadata will remain in the
// files table, and the file will not be moved to the trashdir.
// Files are not cleared from the files table by cron until several days after deletion.
- if ($foundfiles = $DB->get_records('files', array('contenthash' => $file->contenthash))) {
+ if ($foundfiles = $DB->get_records('files', array('contenthash' => $file->contenthash), '', '*', 0, 1)) {
// Only grab one of the foundfiles - the file content should be the same for all entries.
$foundfile = reset($foundfiles);
$fs->create_file_from_storedfile($file_record, $foundfile->id);
$page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id), array('section'=>3));
$coursemodule = $DB->get_record('course_modules', array('id'=>$page->cmid));
- $this->moduleid = $coursemodule->id;
+ $this->moduleid = $page->cmid;
$this->sectionid = $DB->get_field("course_sections", 'id', array("section"=>$coursemodule->section, "course"=>$course->id));
$this->courseid = $coursemodule->course;
$this->userid = 2; // admin
$this->assertEquals(backup_controller_dbops::backup_includes_files($bc->get_backupid()), 0);
// A MODE_SAMESITE controller - should not include files
- $bc = new mock_backup_controller4dbops(backup::TYPE_1COURSE, $this->moduleid, backup::FORMAT_MOODLE,
+ $bc = new mock_backup_controller4dbops(backup::TYPE_1COURSE, $this->courseid, backup::FORMAT_MOODLE,
backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $this->userid);
$this->assertEquals(backup_controller_dbops::backup_includes_files($bc->get_backupid()), 0);
}
try {
- $settings = array(
- 'users' => 'backup_auto_users',
- 'role_assignments' => 'backup_auto_role_assignments',
- 'activities' => 'backup_auto_activities',
- 'blocks' => 'backup_auto_blocks',
- 'filters' => 'backup_auto_filters',
- 'comments' => 'backup_auto_comments',
- 'badges' => 'backup_auto_badges',
- 'completion_information' => 'backup_auto_userscompletion',
- 'logs' => 'backup_auto_logs',
- 'histories' => 'backup_auto_histories',
- 'questionbank' => 'backup_auto_questionbank'
- );
- foreach ($settings as $setting => $configsetting) {
- if ($bc->get_plan()->setting_exists($setting)) {
- if (isset($config->{$configsetting})) {
- $bc->get_plan()->get_setting($setting)->set_value($config->{$configsetting});
- }
- }
- }
-
// Set the default filename.
$format = $bc->get_format();
$type = $bc->get_type();
parent::execute();
$this->controller->set_status(backup::STATUS_FINISHED_OK);
- // Trigger a course restored event.
- $event = \core\event\course_restored::create(array(
- 'objectid' => $this->get_courseid(),
- 'userid' => $this->get_userid(),
- 'context' => context_course::instance($this->get_courseid()),
- 'other' => array('type' => $this->controller->get_type(),
- 'target' => $this->controller->get_target(),
- 'mode' => $this->controller->get_mode(),
- 'operation' => $this->controller->get_operation(),
- 'samesite' => $this->controller->is_samesite())
- ));
- $event->trigger();
+ // Check if we are restoring a course.
+ if ($this->controller->get_type() === backup::TYPE_1COURSE) {
+ // Trigger a course restored event.
+ $event = \core\event\course_restored::create(array(
+ 'objectid' => $this->get_courseid(),
+ 'userid' => $this->get_userid(),
+ 'context' => context_course::instance($this->get_courseid()),
+ 'other' => array('type' => $this->controller->get_type(),
+ 'target' => $this->controller->get_target(),
+ 'mode' => $this->controller->get_mode(),
+ 'operation' => $this->controller->get_operation(),
+ 'samesite' => $this->controller->is_samesite())
+ ));
+ $event->trigger();
+ }
}
/**
*/
class backup_structure_testcase extends advanced_testcase {
- protected $forumid; // To store the inserted forum->id
- protected $contextid; // Official contextid for these tests
+ /** @var int Store the inserted forum->id for use in test functions */
+ protected $forumid;
+ /** @var int Store the inserted discussion1->id for use in test functions */
+ protected $discussionid1;
+ /** @var int Store the inserted discussion2->id for use in test functions */
+ protected $discussionid2;
+ /** @var int Store the inserted post1->id for use in test functions */
+ protected $postid1;
+ /** @var int Store the inserted post2->id for use in test functions */
+ protected $postid2;
+ /** @var int Store the inserted post3->id for use in test functions */
+ protected $postid3;
+ /** @var int Store the inserted post4->id for use in test functions */
+ protected $postid4;
+ /** @var int Official contextid for these tests */
+ protected $contextid;
protected function setUp() {
// Create two discussions
$discussion1 = (object)array('course' => 1, 'forum' => $this->forumid, 'name' => 'd1', 'userid' => 100, 'groupid' => 200);
- $d1id = $DB->insert_record('forum_discussions', $discussion1);
+ $this->discussionid1 = $DB->insert_record('forum_discussions', $discussion1);
$discussion2 = (object)array('course' => 1, 'forum' => $this->forumid, 'name' => 'd2', 'userid' => 101, 'groupid' => 201);
- $d2id = $DB->insert_record('forum_discussions', $discussion2);
+ $this->discussionid2 = $DB->insert_record('forum_discussions', $discussion2);
// Create four posts
- $post1 = (object)array('discussion' => $d1id, 'userid' => 100, 'subject' => 'p1', 'message' => 'm1');
- $p1id = $DB->insert_record('forum_posts', $post1);
- $post2 = (object)array('discussion' => $d1id, 'parent' => $p1id, 'userid' => 102, 'subject' => 'p2', 'message' => 'm2');
- $p2id = $DB->insert_record('forum_posts', $post2);
- $post3 = (object)array('discussion' => $d1id, 'parent' => $p2id, 'userid' => 103, 'subject' => 'p3', 'message' => 'm3');
- $p3id = $DB->insert_record('forum_posts', $post3);
- $post4 = (object)array('discussion' => $d2id, 'userid' => 101, 'subject' => 'p4', 'message' => 'm4');
- $p4id = $DB->insert_record('forum_posts', $post4);
+ $post1 = (object)array('discussion' => $this->discussionid1, 'userid' => 100, 'subject' => 'p1', 'message' => 'm1');
+ $this->postid1 = $DB->insert_record('forum_posts', $post1);
+ $post2 = (object)array('discussion' => $this->discussionid1, 'parent' => $this->postid1, 'userid' => 102, 'subject' => 'p2', 'message' => 'm2');
+ $this->postid2 = $DB->insert_record('forum_posts', $post2);
+ $post3 = (object)array('discussion' => $this->discussionid1, 'parent' => $this->postid2, 'userid' => 103, 'subject' => 'p3', 'message' => 'm3');
+ $this->postid3 = $DB->insert_record('forum_posts', $post3);
+ $post4 = (object)array('discussion' => $this->discussionid2, 'userid' => 101, 'subject' => 'p4', 'message' => 'm4');
+ $this->postid4 = $DB->insert_record('forum_posts', $post4);
// With two related file
$f1_post1 = (object)array(
'contenthash' => 'testp1', 'contextid' => $this->contextid, 'component'=>'mod_forum',
- 'filearea' => 'post', 'filename' => 'tp1', 'itemid' => $p1id,
+ 'filearea' => 'post', 'filename' => 'tp1', 'itemid' => $this->postid1,
'filesize' => 123, 'timecreated' => 0, 'timemodified' => 0,
'pathnamehash' => 'testp1'
);
$DB->insert_record('files', $f1_post1);
$f1_post2 = (object)array(
'contenthash' => 'testp2', 'contextid' => $this->contextid, 'component'=>'mod_forum',
- 'filearea' => 'attachment', 'filename' => 'tp2', 'itemid' => $p2id,
+ 'filearea' => 'attachment', 'filename' => 'tp2', 'itemid' => $this->postid2,
'filesize' => 123, 'timecreated' => 0, 'timemodified' => 0,
'pathnamehash' => 'testp2'
);
// Create two ratings
$rating1 = (object)array(
- 'contextid' => $this->contextid, 'userid' => 104, 'itemid' => $p1id, 'rating' => 2,
+ 'contextid' => $this->contextid, 'userid' => 104, 'itemid' => $this->postid1, 'rating' => 2,
'scaleid' => -1, 'timecreated' => time(), 'timemodified' => time());
$r1id = $DB->insert_record('rating', $rating1);
$rating2 = (object)array(
- 'contextid' => $this->contextid, 'userid' => 105, 'itemid' => $p1id, 'rating' => 3,
+ 'contextid' => $this->contextid, 'userid' => 105, 'itemid' => $this->postid1, 'rating' => 3,
'scaleid' => -1, 'timecreated' => time(), 'timemodified' => time());
$r2id = $DB->insert_record('rating', $rating2);
// Create 1 reads
- $read1 = (object)array('userid' => 102, 'forumid' => $this->forumid, 'discussionid' => $d2id, 'postid' => $p4id);
+ $read1 = (object)array('userid' => 102, 'forumid' => $this->forumid, 'discussionid' => $this->discussionid2, 'postid' => $this->postid4);
$DB->insert_record('forum_read', $read1);
}
// Let's add 1 optigroup with 4 elements
$alternative1 = new backup_optigroup_element('alternative1',
- array('name', 'value'), '../../id', 1);
+ array('name', 'value'), '../../id', $this->postid1);
$alternative2 = new backup_optigroup_element('alternative2',
- array('name', 'value'), backup::VAR_PARENTID, 2);
+ array('name', 'value'), backup::VAR_PARENTID, $this->postid2);
$alternative3 = new backup_optigroup_element('alternative3',
- array('name', 'value'), '/forum/discussions/discussion/posts/post/id', 3);
+ array('name', 'value'), '/forum/discussions/discussion/posts/post/id', $this->postid3);
$alternative4 = new backup_optigroup_element('alternative4',
array('forumtype', 'forumname')); // Alternative without conditions
// Create the optigroup, adding one element
array(backup::VAR_PARENTID)
);
- $read->set_source_table('forum_read', array('id' => '../../id'));
+ $read->set_source_table('forum_read', array('forumid' => '../../id'));
$inventeds->set_source_array(array((object)array('reason' => 'I love Moodle', 'version' => '1.0'),
(object)array('reason' => 'I love Moodle', 'version' => '2.0'))); // 2 object array
$ratarr[$node->nodeName] = $node->nodeValue;
}
}
- $this->assertEquals($ratarr['userid'], $DB->get_field('rating', 'userid', array('id' => $ratarr['id'])));
- $this->assertEquals($ratarr['itemid'], $DB->get_field('rating', 'itemid', array('id' => $ratarr['id'])));
- $this->assertEquals($ratarr['post_rating'], $DB->get_field('rating', 'rating', array('id' => $ratarr['id'])));
+ $this->assertEquals($DB->get_field('rating', 'userid', array('id' => $ratarr['id'])), $ratarr['userid']);
+ $this->assertEquals($DB->get_field('rating', 'itemid', array('id' => $ratarr['id'])), $ratarr['itemid']);
+ $this->assertEquals($DB->get_field('rating', 'rating', array('id' => $ratarr['id'])), $ratarr['post_rating']);
}
// Check forum has "blockeperiod" with value 0 (was declared by object instead of name)
$query = '/forum[blockperiod="0"]';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 1);
+ $this->assertEquals(1, $result->length);
// Check forum is missing "completiondiscussions" (as we are using mock_skip_final_element)
$query = '/forum/completiondiscussions';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 0);
+ $this->assertEquals(0, $result->length);
// Check forum has "completionreplies" with value "original was 0, now changed" (because of mock_modify_final_element)
$query = '/forum[completionreplies="original was 0, now changed"]';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 1);
+ $this->assertEquals(1, $result->length);
// Check forum has "completionposts" with value "intercepted!" (because of mock_final_element_interceptor)
$query = '/forum[completionposts="intercepted!"]';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 1);
+ $this->assertEquals(1, $result->length);
// Check there isn't any alternative2 tag, as far as it hasn't source defined
$query = '//alternative2';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 0);
+ $this->assertEquals(0, $result->length);
// Check there are 4 "field1" elements
$query = '/forum/discussions/discussion/posts/post//field1';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 4);
+ $this->assertEquals(4, $result->length);
// Check first post has one name element with value "alternative1"
- $query = '/forum/discussions/discussion/posts/post[@id="1"][name="alternative1"]';
+ $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid1.'"][name="alternative1"]';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 1);
+ $this->assertEquals(1, $result->length);
// Check there are two "dupetest1" elements
$query = '/forum/discussions/discussion/posts/post//dupetest1';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 2);
+ $this->assertEquals(2, $result->length);
// Check second post has one name element with value "dupetest2"
- $query = '/forum/discussions/discussion/posts/post[@id="2"]/dupetest2';
+ $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid2.'"]/dupetest2';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 1);
+ $this->assertEquals(1, $result->length);
// Check element "dupetest2" of second post has one field1 element with value "2"
- $query = '/forum/discussions/discussion/posts/post[@id="2"]/dupetest2[field1="2"]';
+ $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid2.'"]/dupetest2[field1="2"]';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 1);
+ $this->assertEquals(1, $result->length);
// Check forth post has no name element
- $query = '/forum/discussions/discussion/posts/post[@id="4"]/name';
+ $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid4.'"]/name';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 0);
+ $this->assertEquals(0, $result->length);
// Check 1st, 2nd and 3rd posts have no forumtype element
- $query = '/forum/discussions/discussion/posts/post[@id="1"]/forumtype';
+ $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid1.'"]/forumtype';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 0);
- $query = '/forum/discussions/discussion/posts/post[@id="2"]/forumtype';
+ $this->assertEquals(0, $result->length);
+ $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid2.'"]/forumtype';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 0);
- $query = '/forum/discussions/discussion/posts/post[@id="3"]/forumtype';
+ $this->assertEquals(0, $result->length);
+ $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid3.'"]/forumtype';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 0);
+ $this->assertEquals(0, $result->length);
// Check 4th post has one forumtype element with value "general"
// (because it doesn't matches alternatives 1, 2, 3, then alternative 4,
// the one without conditions is being applied)
- $query = '/forum/discussions/discussion/posts/post[@id="4"][forumtype="general"]';
+ $query = '/forum/discussions/discussion/posts/post[@id="'.$this->postid4.'"][forumtype="general"]';
$result = $xpath->query($query);
- $this->assertEquals($result->length, 1);
+ $this->assertEquals(1, $result->length);
// Check annotations information against DB
// Count records in original tables
global $CFG, $PAGE;
require_once($CFG->dirroot.'/course/lib.php');
+ // These variables are used to check if the form using this function was submitted.
+ $target = optional_param('target', false, PARAM_INT);
+ $targetid = optional_param('targetid', null, PARAM_INT);
+
+ // Check if they submitted the form but did not provide all the data we need.
+ $missingdata = false;
+ if ($target and is_null($targetid)) {
+ $missingdata = true;
+ }
+
$nextstageurl->param('sesskey', sesskey());
- $form = html_writer::start_tag('form', array('method'=>'post', 'action'=>$nextstageurl->out_omit_querystring()));
+ $form = html_writer::start_tag('form', array('method' => 'post', 'action' => $nextstageurl->out_omit_querystring(),
+ 'class' => 'mform'));
foreach ($nextstageurl->params() as $key=>$value) {
$form .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>$key, 'value'=>$value));
}
$html .= html_writer::start_tag('div', array('class'=>'bcs-new-course backup-section'));
$html .= $this->output->heading(get_string('restoretonewcourse', 'backup'), 2, array('class'=>'header'));
$html .= $this->backup_detail_input(get_string('restoretonewcourse', 'backup'), 'radio', 'target', backup::TARGET_NEW_COURSE, array('checked'=>'checked'));
- $html .= $this->backup_detail_pair(get_string('selectacategory', 'backup'), $this->render($categories));
+ $selectacategoryhtml = $this->backup_detail_pair(get_string('selectacategory', 'backup'), $this->render($categories));
+ // Display the category selection as required if the form was submitted but this data was not supplied.
+ if ($missingdata && $target == backup::TARGET_NEW_COURSE) {
+ $html .= html_writer::span(get_string('required'), 'error');
+ $html .= html_writer::start_tag('fieldset', array('class' => 'error'));
+ $html .= $selectacategoryhtml;
+ $html .= html_writer::end_tag('fieldset');
+ } else {
+ $html .= $selectacategoryhtml;
+ }
$html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue'))));
$html .= html_writer::end_tag('div');
$html .= html_writer::end_tag('form');
if ($wholecourse) {
$html .= $this->backup_detail_input(get_string('restoretoexistingcourseadding', 'backup'), 'radio', 'target', backup::TARGET_EXISTING_ADDING, array('checked'=>'checked'));
$html .= $this->backup_detail_input(get_string('restoretoexistingcoursedeleting', 'backup'), 'radio', 'target', backup::TARGET_EXISTING_DELETING);
- $html .= $this->backup_detail_pair(get_string('selectacourse', 'backup'), $this->render($courses));
} else {
// We only allow restore adding to existing for now. Enforce it here.
$html .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'target', 'value'=>backup::TARGET_EXISTING_ADDING));
$courses->invalidate_results(); // Clean list of courses
$courses->set_include_currentcourse(); // Show current course in the list
- $html .= $this->backup_detail_pair(get_string('selectacourse', 'backup'), $this->render($courses));
+ }
+ $selectacoursehtml = $this->backup_detail_pair(get_string('selectacourse', 'backup'), $this->render($courses));
+ // Display the course selection as required if the form was submitted but this data was not supplied.
+ if ($missingdata && $target == backup::TARGET_EXISTING_ADDING) {
+ $html .= html_writer::span(get_string('required'), 'error');
+ $html .= html_writer::start_tag('fieldset', array('class' => 'error'));
+ $html .= $selectacoursehtml;
+ $html .= html_writer::end_tag('fieldset');
+ } else {
+ $html .= $selectacoursehtml;
}
$html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue'))));
$html .= html_writer::end_tag('div');
When I backup "Course 1" course using this options:
| Initial | Include calendar events | 0 |
| Initial | Include course logs | 1 |
- | Schema | setting_section_section_5_userinfo | 0 |
- | Schema | setting_section_section_5_included | 0 |
+ | Schema | Topic 5 | 0 |
| Confirmation | Filename | test_backup.mbz |
Then I should see "Restore"
And I click on "Restore" "link" in the "test_backup.mbz" "table_row"
@javascript
Scenario: Backup selecting just one section
When I backup "Course 2" course using this options:
- | Schema | setting_section_section_2_userinfo | 0 |
- | Schema | setting_section_section_2_userinfo | 0 |
- | Schema | setting_section_section_4_included | 0 |
- | Schema | setting_section_section_4_included | 0 |
+ | Schema | Test data | 0 |
+ | Schema | Topic 2 | 0 |
| Confirmation | Filename | test_backup.mbz |
Then I should see "Course backup area"
And I click on "Restore" "link" in the "test_backup.mbz" "table_row"
When I backup "Course 3" course using this options:
| Confirmation | Filename | test_backup.mbz |
And I restore "test_backup.mbz" backup into "Course 2" course using this options:
- | Schema | setting_section_section_3_included | 0 |
- | Schema | setting_section_section_3_userinfo | 0 |
- | Schema | setting_section_section_5_included | 0 |
- | Schema | setting_section_section_5_userinfo | 0 |
+ | Schema | Test database name | 0 |
+ | Schema | Section 2 | 0 |
Then I should see "Course 2"
And I should see "Test assign name"
And I should not see "Test database name"
| Description | Test forum post backup description |
And I follow "Restore"
And I merge "test_backup.mbz" backup into the current course after deleting it's contents using this options:
- | Schema | setting_section_section_8_userinfo | 0 |
- | Schema | setting_section_section_8_included | 0 |
+ | Schema | Section 3 | 0 |
Then I should see "Course 1"
And I should not see "Section 3"
And I should not see "Test forum post backup name"
if (isloggedin()) {
$PAGE->set_heading($badge->badgeclass['name']);
$PAGE->navbar->add($badge->badgeclass['name']);
- $url = new moodle_url('/badges/mybadges.php');
+ if ($badge->recipient->id == $USER->id) {
+ $url = new moodle_url('/badges/mybadges.php');
+ } else {
+ $url = new moodle_url($CFG->wwwroot);
+ }
+ navigation_node::override_active_url($url);
+} else {
+ $PAGE->set_heading($badge->badgeclass['name']);
+ $PAGE->navbar->add($badge->badgeclass['name']);
+ $url = new moodle_url($CFG->wwwroot);
navigation_node::override_active_url($url);
}
$PAGE->set_pagelayout('base');
$PAGE->set_title(get_string('issuedbadge', 'badges'));
+$PAGE->set_heading(s($badge->issued->assertion->badge->name));
+$PAGE->navbar->add(s($badge->issued->assertion->badge->name));
+if (isloggedin() && $USER->id == $userid) {
+ $url = new moodle_url('/badges/mybadges.php');
+} else {
+ $url = new moodle_url($CFG->wwwroot);
+}
+navigation_node::override_active_url($url);
echo $OUTPUT->header();
@javascript
Scenario: Setting badges settings
- Given I expand "Site administration" node
- And I expand "Badges" node
- And I follow "Badges settings"
+ Given I navigate to "Badges settings" node in "Site administration > Badges"
And I set the field "Default badge issuer name" to "Test Badge Site"
And I set the field "Default badge issuer contact details" to "testuser@test-badge-site.com"
And I press "Save changes"
@javascript
Scenario: Accessing the badges
- Given I expand "Site pages" node
- And I follow "Site badges"
+ Given I navigate to "Site badges" node in "Site pages"
Then I should see "There are no badges available."
@javascript
Scenario: Add a badge
- Given I expand "Site administration" node
- And I expand "Badges" node
- And I follow "Add a new badge"
+ Given I navigate to "Add a new badge" node in "Site administration > Badges"
And I set the following fields to these values:
| Name | Test Badge |
| Description | Test badge description |
| student2 | C1 | student |
And I log in as "teacher1"
And I follow "Course 1"
- And I click on "//span[text()='Badges']" "xpath_element" in the "Administration" "block"
+ And I navigate to "Add a new badge" node in "Course administration > Badges"
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge |
And I log out
And I log in as "teacher1"
And I follow "Course 1"
- And I click on "//span[text()='Badges']" "xpath_element" in the "Administration" "block"
+ And I navigate to "Add a new badge" node in "Course administration > Badges"
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge |
And I log out
And I log in as "teacher1"
And I follow "Course 1"
- And I click on "//span[text()='Badges']" "xpath_element" in the "Administration" "block"
+ And I navigate to "Add a new badge" node in "Course administration > Badges"
And I follow "Add a new badge"
And I set the following fields to these values:
| Name | Course Badge |
And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
And I press "Create badge"
And I set the field "type" to "Course completion"
- And I set the following fields to these values:
- | grade_2 | 0 |
+ And I set the field with xpath "//fieldset/input[1]" to "0"
And I press "Save"
And I press "Enable access"
When I press "Continue"
-<?PHP
+<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
\core\event\blog_association_created::create(array(
'contextid' => 1,
'objectid' => 3,
+ 'relateduserid' => 2,
'other' => array('associateid' => 2 , 'blogid' => 3, 'subject' => 'blog subject')));
} catch (coding_exception $e) {
$this->assertContains('The \'associatetype\' value must be set in other and be a valid type.', $e->getMessage());
\core\event\blog_association_created::create(array(
'contextid' => 1,
'objectid' => 3,
+ 'relateduserid' => 2,
'other' => array('associateid' => 2 , 'blogid' => 3, 'associatetype' => 'random', 'subject' => 'blog subject')));
} catch (coding_exception $e) {
$this->assertContains('The \'associatetype\' value must be set in other and be a valid type.', $e->getMessage());
\core\event\blog_association_created::create(array(
'contextid' => 1,
'objectid' => 3,
+ 'relateduserid' => 2,
'other' => array('blogid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject')));
} catch (coding_exception $e) {
$this->assertContains('The \'associateid\' value must be set in other.', $e->getMessage());
\core\event\blog_association_created::create(array(
'contextid' => 1,
'objectid' => 3,
+ 'relateduserid' => 2,
'other' => array('associateid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject')));
} catch (coding_exception $e) {
$this->assertContains('The \'blogid\' value must be set in other.', $e->getMessage());
\core\event\blog_association_created::create(array(
'contextid' => 1,
'objectid' => 3,
+ 'relateduserid' => 2,
'other' => array('blogid' => 3, 'associateid' => 3, 'associatetype' => 'course')));
} catch (coding_exception $e) {
$this->assertContains('The \'subject\' value must be set in other.', $e->getMessage());
* If you have configured your cache before setting $CFG->altcacheconfigpath you will need to copy it from moodledata/muc/config.php to the destination you specified.
* This allows you to share a cache config between sites.
* It also allows you to use unit tests to test your sites cache config.
+
+Please be aware that if you are using Memcache or Memcached it is recommended to use dedicated Memcached servers.
+When caches get purged the memcached servers you have configured get purged, any data stored within them whether it belongs to Moodle or not will be removed.
+If you are using Memcached for sessions as well as caching/testing and caches get purged your sessions will be removed prematurely and users will be need to start again.
\ No newline at end of file
$form->setType('prefix', PARAM_TEXT); // We set to text but we have a rule to limit to alphanumext.
$form->setDefault('prefix', 'mdl_');
$form->addRule('prefix', get_string('prefixinvalid', 'cachestore_memcache'), 'regex', '#^[a-zA-Z0-9\-_]+$#');
+
+ $form->addElement('header', 'clusteredheader', get_string('clustered', 'cachestore_memcache'));
+
+ $form->addElement('checkbox', 'clustered', get_string('clustered', 'cachestore_memcache'));
+ $form->setDefault('checkbox', false);
+ $form->addHelpButton('clustered', 'clustered', 'cachestore_memcache');
+
+ $form->addElement('textarea', 'setservers', get_string('setservers', 'cachestore_memcache'),
+ array('cols' => 75, 'rows' => 5));
+ $form->addHelpButton('setservers', 'setservers', 'cachestore_memcache');
+ $form->disabledIf('setservers', 'clustered');
+ $form->setType('setservers', PARAM_RAW);
+ }
+
+ /**
+ * Perform minimal validation on the settings form.
+ *
+ * @param array $data
+ * @param array $files
+ */
+ public function validation($data, $files) {
+ $errors = parent::validation($data, $files);
+
+ if (isset($data['clustered']) && ($data['clustered'] == 1)) {
+ // Set servers is required with in cluster mode.
+ if (!isset($data['setservers']) || empty(trim($data['setservers']))) {
+ $errors['setservers'] = get_string('required');
+ }
+
+ $validservers = false;
+ if (isset($data['servers'])) {
+ $servers = trim($data['servers']);
+ $servers = explode("\n", $servers);
+ if (count($servers) === 1) {
+ $validservers = true;
+ }
+ }
+
+ if (!$validservers) {
+ $errors['servers'] = get_string('serversclusterinvalid', 'cachestore_memcache');
+ }
+ }
+
+ return $errors;
}
}
\ No newline at end of file
defined('MOODLE_INTERNAL') || die();
+$string['clustered'] = 'Enable clustered servers';
+$string['clustered_help'] = 'This is used to allow read-one, set-multi functionality.
+
+The intended use case is to create an improved store for load-balanced configurations. The store will fetch from one server (usually localhost), but set to many (all the servers in the load-balance pool). For caches with very high read to set ratios, this saves a significant amount of network overhead.
+
+When this setting is enabled, the server listed above will be used for fetching.';
+$string['clusteredheader'] = 'Split servers';
$string['pluginname'] = 'Memcache';
$string['prefix'] = 'Key prefix';
$string['prefix_help'] = 'This prefix is used for all key names on the memcache server.
server.url.com
ipaddress:port
servername:port:weight
+</pre>
+
+If *Enable clustered servers* is enabled below, there must be only one server listed here. This would usually be a name that always resolves to the local manchine, like 127.0.0.1 or localhost.';
+$string['serversclusterinvalid'] = 'Exactly one server is required when clustering is enabled.';
+$string['setservers'] = 'Set Servers';
+$string['setservers_help'] = 'This is the list of servers that will updated when data is modified in the cache. Generally the fully qualified name of each server in the pool.
+It **must** include the server listed in *Servers* above, even if by a different hostname.
+Servers should be defined one per line and consist of a server address and optionally a port.
+If no port is provided then the default port (11211) is used.
+
+For example:
+<pre>
+server.url.com
+ipaddress:port
</pre>';
$string['testservers'] = 'Test servers';
$string['testservers_desc'] = 'The test servers get used for unit tests and for performance tests. It is entirely optional to set up test servers. Servers should be defined one per line and consist of a server address and optionally a port and weight.
*/
protected $definition;
+ /**
+ * Set to true when this store is clustered.
+ * @var bool
+ */
+ protected $clustered = false;
+
+ /**
+ * Array of servers to set when in clustered mode.
+ * @var array
+ */
+ protected $setservers = array();
+
+ /**
+ * The an array of memcache connections for the set servers, once established.
+ * @var array
+ */
+ protected $setconnections = array();
+
/**
* Default prefix for key names.
* @var string
}
$this->servers[] = $server;
}
+
+ $this->clustered = array_key_exists('clustered', $configuration) ? (bool)$configuration['clustered'] : false;
+
+ if ($this->clustered) {
+ if (!array_key_exists('setservers', $configuration) || (count($configuration['setservers']) < 1)) {
+ // Can't setup clustering without set servers.
+ return;
+ }
+ if (count($this->servers) !== 1) {
+ // Can only setup cluster with exactly 1 get server.
+ return;
+ }
+ foreach ($configuration['setservers'] as $server) {
+ // We do not use weights (3rd part) on these servers.
+ if (!is_array($server)) {
+ $server = explode(':', $server, 3);
+ }
+ if (!array_key_exists(1, $server)) {
+ $server[1] = 11211;
+ }
+ $this->setservers[] = $server;
+ }
+ }
+
if (empty($configuration['prefix'])) {
$this->prefix = self::DEFAULT_PREFIX;
} else {
foreach ($this->servers as $server) {
$this->connection->addServer($server[0], (int) $server[1], true, (int) $server[2]);
}
+
+ if ($this->clustered) {
+ foreach ($this->setservers as $setserver) {
+ // Since we will have a number of them with the same name, append server and port.
+ $connection = new Memcache;
+ $connection->addServer($setserver[0], $setserver[1]);
+ $this->setconnections[] = $connection;
+ }
+ }
+
// Test the connection to the pool of servers.
$this->isready = @$this->connection->set($this->parse_key('ping'), 'ping', MEMCACHE_COMPRESSED, 1);
}
* @return bool True if the operation was a success false otherwise.
*/
public function set($key, $data) {
+ if ($this->clustered) {
+ $status = true;
+ foreach ($this->setconnections as $connection) {
+ $status = $connection->set($this->parse_key($key), $data, MEMCACHE_COMPRESSED, $this->definition->get_ttl())
+ && $status;
+ }
+ return $status;
+ }
+
return $this->connection->set($this->parse_key($key), $data, MEMCACHE_COMPRESSED, $this->definition->get_ttl());
}
public function set_many(array $keyvaluearray) {
$count = 0;
foreach ($keyvaluearray as $pair) {
- if ($this->connection->set($this->parse_key($pair['key']), $pair['value'], MEMCACHE_COMPRESSED, $this->definition->get_ttl())) {
+ if ($this->set($pair['key'], $pair['value'])) {
$count++;
}
}
* @return bool Returns true if the operation was a success, false otherwise.
*/
public function delete($key) {
+ if ($this->clustered) {
+ $status = true;
+ foreach ($this->setconnections as $connection) {
+ $status = $connection->delete($this->parse_key($key)) && $status;
+ }
+ return $status;
+ }
+
return $this->connection->delete($this->parse_key($key));
}
*/
public function purge() {
if ($this->isready) {
- $this->connection->flush();
+ if ($this->clustered) {
+ foreach ($this->setconnections as $connection) {
+ $connection->flush();
+ }
+ } else {
+ $this->connection->flush();
+ }
}
return true;
}
$servers[] = explode(':', $line, 3);
}
+
+ $clustered = false;
+ if (isset($data->clustered)) {
+ $clustered = true;
+ }
+
+ $lines = explode("\n", $data->setservers);
+ $setservers = array();
+ foreach ($lines as $line) {
+ // Trim surrounding colons and default whitespace.
+ $line = trim(trim($line), ":");
+ if ($line === '') {
+ continue;
+ }
+ $setserver = explode(':', $line, 3);
+ // We don't use weights, so display a debug message.
+ if (count($setserver) > 2) {
+ debugging('Memcache Set Server '.$setserver[0].' has too many parameters.');
+ }
+ $setservers[] = $setserver;
+ }
+
return array(
'servers' => $servers,
'prefix' => $data->prefix,
+ 'clustered' => $clustered,
+ 'setservers' => $setservers
);
}
} else {
$data['prefix'] = self::DEFAULT_PREFIX;
}
+ if (isset($config['clustered'])) {
+ $data['clustered'] = (bool)$config['clustered'];
+ }
+ if (!empty($config['setservers'])) {
+ $servers = array();
+ foreach ($config['setservers'] as $server) {
+ $servers[] = join(":", $server);
+ }
+ $data['setservers'] = join("\n", $servers);
+ }
$editform->set_data($data);
}
$configuration = array();
$configuration['servers'] = explode("\n", $config->testservers);
+ if (!empty($config->testclustered)) {
+ $configuration['clustered'] = $config->testclustered;
+ }
+ if (!empty($config->testsetservers)) {
+ $configuration['setservers'] = explode("\n", $config->testsetservers);
+ }
$store = new cachestore_memcache('Test memcache', $configuration);
$store->initialise($definition);
'cachestore_memcache/testservers',
new lang_string('testservers', 'cachestore_memcache'),
new lang_string('testservers_desc', 'cachestore_memcache'),
- '', PARAM_RAW, 60, 3));
\ No newline at end of file
+ '', PARAM_RAW, 60, 3));
* Tests the valid keys to ensure they work.
*/
public function test_valid_keys() {
+ $this->resetAfterTest(true);
+
$definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcache', 'phpunit_test');
$instance = cachestore_memcache::initialise_unit_test_instance($definition);
// Underscores.
'a_1', '1_a', '_a1', 'a1_'
);
+
+ // Set some keys.
foreach ($keys as $key) {
$this->assertTrue($instance->set($key, $key), "Failed to set key `$key`");
}
+
+ // Get some keys.
foreach ($keys as $key) {
$this->assertEquals($key, $instance->get($key), "Failed to get key `$key`");
}
+
+ // Try get many.
$values = $instance->get_many($keys);
foreach ($values as $key => $value) {
$this->assertEquals($key, $value);
}
+
+ // Reset a key.
+ $this->assertTrue($instance->set($keys[0], 'New'), "Failed to reset key `$key`");
+ $this->assertEquals('New', $instance->get($keys[0]), "Failed to get reset key `$key`");
+
+ // Delete and check that we can't retrieve.
+ foreach ($keys as $key) {
+ $this->assertTrue($instance->delete($key), "Failed to delete key `$key`");
+ $this->assertFalse($instance->get($key), "Retrieved deleted key `$key`");
+ }
+
+ // Try set many, and check that count is correct.
+ $many = array();
+ foreach ($keys as $key) {
+ $many[] = array('key' => $key, 'value' => $key);
+ }
+ $returncount = $instance->set_many($many);
+ $this->assertEquals(count($many), $returncount, 'Set many count didn\'t match');
+
+ // Check keys retrieved with get_many.
+ $values = $instance->get_many($keys);
+ foreach ($keys as $key) {
+ $this->assertTrue(isset($values[$key]), "Failed to get_many key `$key`");
+ $this->assertEquals($key, $values[$key], "Failed to match get_many key `$key`");
+ }
+
+ // Delete many, make sure count matches.
+ $returncount = $instance->delete_many($keys);
+ $this->assertEquals(count($many), $returncount, 'Delete many count didn\'t match');
+
+ // Check that each key was deleted.
+ foreach ($keys as $key) {
+ $this->assertFalse($instance->get($key), "Retrieved many deleted key `$key`");
+ }
+
+ // Set the keys again.
+ $returncount = $instance->set_many($many);
+ $this->assertEquals(count($many), $returncount, 'Set many count didn\'t match');
+
+ // Purge.
+ $this->assertTrue($instance->purge(), 'Failure to purge');
+
+ // Delete and check that we can't retrieve.
+ foreach ($keys as $key) {
+ $this->assertFalse($instance->get($key), "Retrieved purged key `$key`");
+ }
+ }
+
+ /**
+ * Tests the clustering feature.
+ */
+ public function test_clustered() {
+ $this->resetAfterTest(true);
+
+ if (!defined('TEST_CACHESTORE_MEMCACHE_TESTSERVERS')) {
+ $this->markTestSkipped();
+ }
+
+ $testservers = explode("\n", trim(TEST_CACHESTORE_MEMCACHE_TESTSERVERS));
+
+ if (count($testservers) < 2) {
+ $this->markTestSkipped();
+ }
+
+ // User the first server as our primary.
+ set_config('testservers', $testservers[0], 'cachestore_memcache');
+ set_config('testsetservers', TEST_CACHESTORE_MEMCACHE_TESTSERVERS, 'cachestore_memcache');
+ set_config('testclustered', true, 'cachestore_memcache');
+
+ // First and instance that we can use to test the second server.
+ $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcache', 'phpunit_test');
+ $instance = cachestore_memcache::initialise_test_instance($definition);
+
+ if (!$instance) {
+ $this->markTestSkipped();
+ }
+
+ // Now we are going to setup a connection to each independent server.
+ set_config('testclustered', false, 'cachestore_memcache');
+ set_config('testsetservers', '', 'cachestore_memcache');
+ $checkinstances = array();
+ foreach ($testservers as $testserver) {
+ set_config('testservers', $testserver, 'cachestore_memcache');
+ $checkinstance = cachestore_memcache::initialise_test_instance($definition);
+ if (!$checkinstance) {
+ $this->markTestSkipped();
+ }
+ $checkinstances[] = $checkinstance;
+ }
+
+ $keys = array(
+ // Alphanumeric.
+ 'abc', 'ABC', '123', 'aB1', '1aB',
+ // Hyphens.
+ 'a-1', '1-a', '-a1', 'a1-',
+ // Underscores.
+ 'a_1', '1_a', '_a1', 'a1_'
+ );
+
+ // Set each key.
+ foreach ($keys as $key) {
+ $this->assertTrue($instance->set($key, $key), "Failed to set key `$key`");
+ }
+
+ // Check each key.
+ foreach ($keys as $key) {
+ $this->assertEquals($key, $instance->get($key), "Failed to get key `$key`");
+ foreach ($checkinstances as $id => $checkinstance) {
+ $this->assertEquals($key, $checkinstance->get($key), "Failed to get key `$key` from server $id");
+ }
+ }
+
+ // Reset a key.
+ $this->assertTrue($instance->set($keys[0], 'New'), "Failed to reset key `$key`");
+ $this->assertEquals('New', $instance->get($keys[0]), "Failed to get reset key `$key`");
+ foreach ($checkinstances as $id => $checkinstance) {
+ $this->assertEquals('New', $checkinstance->get($keys[0]), "Failed to get reset key `$key` from server $id");
+ }
+
+ // Delete and check that we can't retrieve.
+ foreach ($keys as $key) {
+ $this->assertTrue($instance->delete($key), "Failed to delete key `$key`");
+ $this->assertFalse($instance->get($key), "Retrieved deleted key `$key`");
+ foreach ($checkinstances as $id => $checkinstance) {
+ $this->assertFalse($checkinstance->get($key), "Retrieved deleted key `$key` from server $id");
+ }
+ }
+
+ // Try set many, and check that count is correct.
+ $many = array();
+ foreach ($keys as $key) {
+ $many[] = array('key' => $key, 'value' => $key);
+ }
+ $returncount = $instance->set_many($many);
+ $this->assertEquals(count($many), $returncount, 'Set many count didn\'t match');
+
+ // Check keys retrieved with get_many.
+ $values = $instance->get_many($keys);
+ foreach ($keys as $key) {
+ $this->assertTrue(isset($values[$key]), "Failed to get_many key `$key`");
+ $this->assertEquals($key, $values[$key], "Failed to match get_many key `$key`");
+ }
+ foreach ($checkinstances as $id => $checkinstance) {
+ $values = $checkinstance->get_many($keys);
+ foreach ($keys as $key) {
+ $this->assertTrue(isset($values[$key]), "Failed to get_many key `$key` from server $id");
+ $this->assertEquals($key, $values[$key], "Failed to get_many key `$key` from server $id");
+ }
+ }
+
+ // Delete many, make sure count matches.
+ $returncount = $instance->delete_many($keys);
+ $this->assertEquals(count($many), $returncount, 'Delete many count didn\'t match');
+
+ // Check that each key was deleted.
+ foreach ($keys as $key) {
+ $this->assertFalse($instance->get($key), "Retrieved many deleted key `$key`");
+ foreach ($checkinstances as $id => $checkinstance) {
+ $this->assertFalse($checkinstance->get($key), "Retrieved many deleted key `$key` from server $id");
+ }
+ }
+
+ // Set the keys again.
+ $returncount = $instance->set_many($many);
+ $this->assertEquals(count($many), $returncount, 'Set many count didn\'t match');
+
+ // Purge.
+ $this->assertTrue($instance->purge(), 'Failure to purge');
+
+ // Delete and check that we can't retrieve.
+ foreach ($keys as $key) {
+ $this->assertFalse($instance->get($key), "Retrieved purged key `$key`");
+ foreach ($checkinstances as $id => $checkinstance) {
+ $this->assertFalse($checkinstance->get($key), "Retrieved purged key `$key` from server 2");
+ }
+ }
}
}
$form->addHelpButton('bufferwrites', 'bufferwrites', 'cachestore_memcached');
$form->setDefault('bufferwrites', 0);
$form->setType('bufferwrites', PARAM_BOOL);
+
+ $form->addElement('header', 'clusteredheader', get_string('clustered', 'cachestore_memcached'));
+
+ $form->addElement('checkbox', 'clustered', get_string('clustered', 'cachestore_memcached'));
+ $form->setDefault('checkbox', false);
+ $form->addHelpButton('clustered', 'clustered', 'cachestore_memcached');
+
+ $form->addElement('textarea', 'setservers', get_string('setservers', 'cachestore_memcached'),
+ array('cols' => 75, 'rows' => 5));
+ $form->addHelpButton('setservers', 'setservers', 'cachestore_memcached');
+ $form->disabledIf('setservers', 'clustered');
+ $form->setType('setservers', PARAM_RAW);
+ }
+
+ /**
+ * Perform minimal validation on the settings form.
+ *
+ * @param array $data
+ * @param array $files
+ */
+ public function validation($data, $files) {
+ $errors = parent::validation($data, $files);
+
+ if (isset($data['clustered']) && ($data['clustered'] == 1)) {
+ // Set servers is required with in cluster mode.
+ if (!isset($data['setservers']) || empty(trim($data['setservers']))) {
+ $errors['setservers'] = get_string('required');
+ }
+
+ $validservers = false;
+ if (isset($data['servers'])) {
+ $servers = trim($data['servers']);
+ $servers = explode("\n", $servers);
+ if (count($servers) === 1) {
+ $validservers = true;
+ }
+ }
+
+ if (!$validservers) {
+ $errors['servers'] = get_string('serversclusterinvalid', 'cachestore_memcached');
+ }
+ }
+
+ return $errors;
}
}
$string['bufferwrites'] = 'Buffer writes';
$string['bufferwrites_help'] = 'Enables or disables buffered I/O. Enabling buffered I/O causes storage commands to "buffer" instead of being sent. Any action that retrieves data causes this buffer to be sent to the remote connection. Quitting the connection or closing down the connection will also cause the buffered data to be pushed to the remote connection.';
+$string['clustered'] = 'Enable clustered servers';
+$string['clustered_help'] = 'This is used to allow read-one, set-multi functionality.
+
+The intended use case is to create an improved store for load-balanced configurations. The store will fetch from one server (usually localhost), but set to many (all the servers in the load-balance pool). For caches with very high read to set ratios, this saves a significant amount of network overhead.
+
+When this setting is enabled, the server listed above will be used for fetching.';
+$string['clusteredheader'] = 'Split servers';
$string['hash'] = 'Hash method';
$string['hash_help'] = 'Specifies the hashing algorithm used for the item keys. Each hash algorithm has its advantages and its disadvantages. Go with the default if you don\'t know or don\'t care.';
$string['hash_default'] = 'Default (one-at-a-time)';
server.url.com
ipaddress:port
servername:port:weight
+</pre>
+
+If *Enable clustered servers* is enabled below, there must be only one server listed here. This would usually be a name that always resolves to the local manchine, like 127.0.0.1 or localhost.';
+$string['serversclusterinvalid'] = 'Exactly one server is required when clustering is enabled.';
+$string['setservers'] = 'Set Servers';
+$string['setservers_help'] = 'This is the list of servers that will updated when data is modified in the cache. Generally the fully qualified name of each server in the pool.
+It **must** include the server listed in *Servers* above, even if by a different hostname.
+Servers should be defined one per line and consist of a server address and optionally a port.
+If no port is provided then the default port (11211) is used.
+
+For example:
+<pre>
+server.url.com
+ipaddress:port
</pre>';
$string['testservers'] = 'Test servers';
$string['testservers_desc'] = 'The test servers get used for unit tests and for performance tests. It is entirely optional to set up test servers. Servers should be defined one per line and consist of a server address and optionally a port and weight.
*/
protected $definition;
+ /**
+ * Set to true when this store is clustered.
+ * @var bool
+ */
+ protected $clustered = false;
+
+ /**
+ * Array of servers to set when in clustered mode.
+ * @var array
+ */
+ protected $setservers = array();
+
+ /**
+ * The an array of memcache connections for the set servers, once established.
+ * @var array
+ */
+ protected $setconnections = array();
+
/**
* Constructs the store instance.
*
}
$this->servers[] = $server;
}
+
+ $this->clustered = array_key_exists('clustered', $configuration) ? (bool)$configuration['clustered'] : false;
+
+ if ($this->clustered) {
+ if (!array_key_exists('setservers', $configuration) || (count($configuration['setservers']) < 1)) {
+ // Can't setup clustering without set servers.
+ return;
+ }
+ if (count($this->servers) !== 1) {
+ // Can only setup cluster with exactly 1 get server.
+ return;
+ }
+ foreach ($configuration['setservers'] as $server) {
+ // We do not use weights (3rd part) on these servers.
+ if (!is_array($server)) {
+ $server = explode(':', $server, 3);
+ }
+ if (!array_key_exists(1, $server)) {
+ $server[1] = 11211;
+ }
+ $this->setservers[] = $server;
+ }
+ }
+
$this->options[Memcached::OPT_COMPRESSION] = $compression;
$this->options[Memcached::OPT_SERIALIZER] = $serialiser;
$this->options[Memcached::OPT_PREFIX_KEY] = $prefix;
}
$this->connection->addServers($this->servers);
}
- // Test the connection to the pool of servers.
+
+ if ($this->clustered) {
+ foreach ($this->setservers as $setserver) {
+ // Since we will have a number of them with the same name, append server and port.
+ $connection = new Memcached(crc32($this->name.$setserver[0].$setserver[1]));
+ foreach ($this->options as $key => $value) {
+ $connection->setOption($key, $value);
+ }
+ $connection->addServer($setserver[0], $setserver[1]);
+ $this->setconnections[] = $connection;
+ }
+ }
+
+ // Test the connection to the main connection.
$this->isready = @$this->connection->set("ping", 'ping', 1);
}
* @return bool True if the operation was a success false otherwise.
*/
public function set($key, $data) {
+ if ($this->clustered) {
+ $status = true;
+ foreach ($this->setconnections as $connection) {
+ $status = $connection->set($key, $data, $this->definition->get_ttl()) && $status;
+ }
+ return $status;
+ }
+
return $this->connection->set($key, $data, $this->definition->get_ttl());
}
foreach ($keyvaluearray as $pair) {
$pairs[$pair['key']] = $pair['value'];
}
- if ($this->connection->setMulti($pairs, $this->definition->get_ttl())) {
+
+ $status = true;
+ if ($this->clustered) {
+ foreach ($this->setconnections as $connection) {
+ $status = $connection->setMulti($pairs, $this->definition->get_ttl()) && $status;
+ }
+ } else {
+ $status = $this->connection->setMulti($pairs, $this->definition->get_ttl());
+ }
+
+ if ($status) {
return count($keyvaluearray);
}
return 0;
* @return bool Returns true if the operation was a success, false otherwise.
*/
public function delete($key) {
+ if ($this->clustered) {
+ $status = true;
+ foreach ($this->setconnections as $connection) {
+ $status = $connection->delete($key) && $status;
+ }
+ return $status;
+ }
+
return $this->connection->delete($key);
}
* @return int The number of items successfully deleted.
*/
public function delete_many(array $keys) {
+ if ($this->clustered) {
+ // Get the minimum deleted from any of the connections.
+ $count = count($keys);
+ foreach ($this->setconnections as $connection) {
+ $count = min($this->delete_many_connection($connection, $keys), $count);
+ }
+ return $count;
+ }
+
+ return $this->delete_many_connection($this->connection, $keys);
+ }
+
+ /**
+ * Deletes several keys from the cache in a single action for a specific connection.
+ *
+ * @param Memcached $connection The connection to work on.
+ * @param array $keys The keys to delete
+ * @return int The number of items successfully deleted.
+ */
+ protected function delete_many_connection(Memcached $connection, array $keys) {
$count = 0;
foreach ($keys as $key) {
- if ($this->connection->delete($key)) {
+ if ($connection->delete($key)) {
$count++;
}
}
*/
public function purge() {
if ($this->isready) {
- $this->connection->flush();
+ if ($this->clustered) {
+ foreach ($this->setconnections as $connection) {
+ $connection->flush();
+ }
+ } else {
+ $this->connection->flush();
+ }
}
return true;
}
$servers[] = explode(':', $line, 3);
}
+
+ $clustered = false;
+ if (isset($data->clustered)) {
+ $clustered = true;
+ }
+
+ $lines = explode("\n", $data->setservers);
+ $setservers = array();
+ foreach ($lines as $line) {
+ // Trim surrounding colons and default whitespace.
+ $line = trim(trim($line), ":");
+ if ($line === '') {
+ continue;
+ }
+ $setserver = explode(':', $line, 3);
+ // We don't use weights, so display a debug message.
+ if (count($setserver) > 2) {
+ debugging('Memcached Set Server '.$setserver[0].' has too many parameters.');
+ }
+ $setservers[] = $setserver;
+ }
+
return array(
'servers' => $servers,
'compression' => $data->compression,
'prefix' => $data->prefix,
'hash' => $data->hash,
'bufferwrites' => $data->bufferwrites,
+ 'clustered' => $clustered,
+ 'setservers' => $setservers
);
}
if (isset($config['bufferwrites'])) {
$data['bufferwrites'] = (bool)$config['bufferwrites'];
}
+ if (isset($config['clustered'])) {
+ $data['clustered'] = (bool)$config['clustered'];
+ }
+ if (!empty($config['setservers'])) {
+ $servers = array();
+ foreach ($config['setservers'] as $server) {
+ $servers[] = join(":", $server);
+ }
+ $data['setservers'] = join("\n", $servers);
+ }
$editform->set_data($data);
}
}
$configuration = array();
- $configuration['servers'] = $config->testservers;
+ $configuration['servers'] = explode("\n", $config->testservers);
if (!empty($config->testcompression)) {
$configuration['compression'] = $config->testcompression;
}
if (!empty($config->testbufferwrites)) {
$configuration['bufferwrites'] = $config->testbufferwrites;
}
+ if (!empty($config->testclustered)) {
+ $configuration['clustered'] = $config->testclustered;
+ }
+ if (!empty($config->testsetservers)) {
+ $configuration['setservers'] = explode("\n", $config->testsetservers);
+ }
+ if (!empty($config->testname)) {
+ $name = $config->testname;
+ } else {
+ $name = 'Test memcached';
+ }
- $store = new cachestore_memcached('Test memcached', $configuration);
+ $store = new cachestore_memcached($name, $configuration);
$store->initialise($definition);
return $store;
'cachestore_memcached/testservers',
new lang_string('testservers', 'cachestore_memcached'),
new lang_string('testservers_desc', 'cachestore_memcached'),
- '', PARAM_RAW, 60, 3));
\ No newline at end of file
+ '', PARAM_RAW, 60, 3));
* Tests the valid keys to ensure they work.
*/
public function test_valid_keys() {
+ $this->resetAfterTest(true);
+
$definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcached', 'phpunit_test');
$instance = cachestore_memcached::initialise_unit_test_instance($definition);
// Underscores.
'a_1', '1_a', '_a1', 'a1_'
);
+
+ // Set some keys.
foreach ($keys as $key) {
$this->assertTrue($instance->set($key, $key), "Failed to set key `$key`");
}
+
+ // Get some keys.
foreach ($keys as $key) {
$this->assertEquals($key, $instance->get($key), "Failed to get key `$key`");
}
+
+ // Try get many.
$values = $instance->get_many($keys);
foreach ($values as $key => $value) {
$this->assertEquals($key, $value);
}
+
+ // Reset a key.
+ $this->assertTrue($instance->set($keys[0], 'New'), "Failed to reset key `$key`");
+ $this->assertEquals('New', $instance->get($keys[0]), "Failed to get reset key `$key`");
+
+ // Delete and check that we can't retrieve.
+ foreach ($keys as $key) {
+ $this->assertTrue($instance->delete($key), "Failed to delete key `$key`");
+ $this->assertFalse($instance->get($key), "Retrieved deleted key `$key`");
+ }
+
+ // Try set many, and check that count is correct.
+ $many = array();
+ foreach ($keys as $key) {
+ $many[] = array('key' => $key, 'value' => $key);
+ }
+ $returncount = $instance->set_many($many);
+ $this->assertEquals(count($many), $returncount, 'Set many count didn\'t match');
+
+ // Check keys retrieved with get_many.
+ $values = $instance->get_many($keys);
+ foreach ($keys as $key) {
+ $this->assertTrue(isset($values[$key]), "Failed to get_many key `$key`");
+ $this->assertEquals($key, $values[$key], "Failed to match get_many key `$key`");
+ }
+
+ // Delete many, make sure count matches.
+ $returncount = $instance->delete_many($keys);
+ $this->assertEquals(count($many), $returncount, 'Delete many count didn\'t match');
+
+ // Check that each key was deleted.
+ foreach ($keys as $key) {
+ $this->assertFalse($instance->get($key), "Retrieved many deleted key `$key`");
+ }
+
+ // Set the keys again.
+ $returncount = $instance->set_many($many);
+ $this->assertEquals(count($many), $returncount, 'Set many count didn\'t match');
+
+ // Purge.
+ $this->assertTrue($instance->purge(), 'Failure to purge');
+
+ // Delete and check that we can't retrieve.
+ foreach ($keys as $key) {
+ $this->assertFalse($instance->get($key), "Retrieved purged key `$key`");
+ }
+ }
+
+ /**
+ * Tests the clustering feature.
+ */
+ public function test_clustered() {
+ $this->resetAfterTest(true);
+
+ if (!defined('TEST_CACHESTORE_MEMCACHED_TESTSERVERS')) {
+ $this->markTestSkipped();
+ }
+
+ $testservers = explode("\n", trim(TEST_CACHESTORE_MEMCACHED_TESTSERVERS));
+
+ if (count($testservers) < 2) {
+ $this->markTestSkipped();
+ }
+
+ // Use the first server as our primary.
+ // We need to set a prefix for all, otherwise it uses the name, which will not match between connections.
+ set_config('testprefix', 'pre', 'cachestore_memcached');
+ // We need to set a name, otherwise we get a reused connection.
+ set_config('testname', 'cluster', 'cachestore_memcached');
+ set_config('testservers', $testservers[0], 'cachestore_memcached');
+ set_config('testsetservers', TEST_CACHESTORE_MEMCACHED_TESTSERVERS, 'cachestore_memcached');
+ set_config('testclustered', true, 'cachestore_memcached');
+
+ // First and instance that we can use to test the second server.
+ $definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_memcached', 'phpunit_test');
+ $instance = cachestore_memcached::initialise_test_instance($definition);
+
+ if (!$instance) {
+ $this->markTestSkipped();
+ }
+
+ // Now we are going to setup a connection to each independent server.
+ set_config('testclustered', false, 'cachestore_memcached');
+ set_config('testsetservers', '', 'cachestore_memcached');
+ $checkinstances = array();
+ foreach ($testservers as $testserver) {
+ // We need to set a name, otherwise we get a reused connection.
+ set_config('testname', $testserver, 'cachestore_memcached');
+ set_config('testservers', $testserver, 'cachestore_memcached');
+ $checkinstance = cachestore_memcached::initialise_test_instance($definition);
+ if (!$checkinstance) {
+ $this->markTestSkipped();
+ }
+ $checkinstances[] = $checkinstance;
+ }
+
+ $keys = array(
+ // Alphanumeric.
+ 'abc', 'ABC', '123', 'aB1', '1aB',
+ // Hyphens.
+ 'a-1', '1-a', '-a1', 'a1-',
+ // Underscores.
+ 'a_1', '1_a', '_a1', 'a1_'
+ );
+
+ // Set each key.
+ foreach ($keys as $key) {
+ $this->assertTrue($instance->set($key, $key), "Failed to set key `$key`");
+ }
+
+ // Check each key.
+ foreach ($keys as $key) {
+ $this->assertEquals($key, $instance->get($key), "Failed to get key `$key`");
+ foreach ($checkinstances as $id => $checkinstance) {
+ $this->assertEquals($key, $checkinstance->get($key), "Failed to get key `$key` from server $id");
+ }
+ }
+
+ // Reset a key.
+ $this->assertTrue($instance->set($keys[0], 'New'), "Failed to reset key `$key`");
+ $this->assertEquals('New', $instance->get($keys[0]), "Failed to get reset key `$key`");
+ foreach ($checkinstances as $id => $checkinstance) {
+ $this->assertEquals('New', $checkinstance->get($keys[0]), "Failed to get reset key `$key` from server $id");
+ }
+
+ // Delete and check that we can't retrieve.
+ foreach ($keys as $key) {
+ $this->assertTrue($instance->delete($key), "Failed to delete key `$key`");
+ $this->assertFalse($instance->get($key), "Retrieved deleted key `$key`");
+ foreach ($checkinstances as $id => $checkinstance) {
+ $this->assertFalse($checkinstance->get($key), "Retrieved deleted key `$key` from server $id");
+ }
+ }
+
+ // Try set many, and check that count is correct.
+ $many = array();
+ foreach ($keys as $key) {
+ $many[] = array('key' => $key, 'value' => $key);
+ }
+ $returncount = $instance->set_many($many);
+ $this->assertEquals(count($many), $returncount, 'Set many count didn\'t match');
+
+ // Check keys retrieved with get_many.
+ $values = $instance->get_many($keys);
+ foreach ($keys as $key) {
+ $this->assertTrue(isset($values[$key]), "Failed to get_many key `$key`");
+ $this->assertEquals($key, $values[$key], "Failed to match get_many key `$key`");
+ }
+ foreach ($checkinstances as $id => $checkinstance) {
+ $values = $checkinstance->get_many($keys);
+ foreach ($keys as $key) {
+ $this->assertTrue(isset($values[$key]), "Failed to get_many key `$key` from server $id");
+ $this->assertEquals($key, $values[$key], "Failed to get_many key `$key` from server $id");
+ }
+ }
+
+ // Delete many, make sure count matches.
+ $returncount = $instance->delete_many($keys);
+ $this->assertEquals(count($many), $returncount, 'Delete many count didn\'t match');
+
+ // Check that each key was deleted.
+ foreach ($keys as $key) {
+ $this->assertFalse($instance->get($key), "Retrieved many deleted key `$key`");
+ foreach ($checkinstances as $id => $checkinstance) {
+ $this->assertFalse($checkinstance->get($key), "Retrieved many deleted key `$key` from server $id");
+ }
+ }
+
+ // Set the keys again.
+ $returncount = $instance->set_many($many);
+ $this->assertEquals(count($many), $returncount, 'Set many count didn\'t match');
+
+ // Purge.
+ $this->assertTrue($instance->purge(), 'Failure to purge');
+
+ // Delete and check that we can't retrieve.
+ foreach ($keys as $key) {
+ $this->assertFalse($instance->get($key), "Retrieved purged key `$key`");
+ foreach ($checkinstances as $id => $checkinstance) {
+ $this->assertFalse($checkinstance->get($key), "Retrieved purged key `$key` from server 2");
+ }
+ }
}
}
| Name | Test page name |
| Description | Restricted page, till grades in Grade assignment is at least 20% |
| Page content | Test page contents |
- | id | 2 |
+ | id | Grade assignment |
| minval | 20 |
And I press "Save and return to course"
And I log out
And I click on "Grade" "button" in the "Add restriction..." "dialogue"
And I click on "min" "checkbox"
And I set the following fields to these values:
- | id | 2 |
+ | id | Grade assignment |
| minval | 20 |
And I press "Save changes"
And I log out
-<?PHP
+<?php
///////////////////////////////////////////////////////////////////////////
// //
// Moodle configuration file //
// less reliable. Use memcached where possible or if you encounter
// session problems. **
//
+// Please be aware that when selecting either Memcached or Memcache for sessions that it is advised to use a dedicated
+// memcache server. The memcache and memcached extensions do not provide isolated environments for individual uses.
+// Using the same server for other purposes (MUC for example) can lead to sessions being prematurely removed should
+// the other uses of the server purge the cache.
+//
// Following setting allows you to alter how frequently is timemodified updated in sessions table.
// $CFG->session_update_timemodified_frequency = 20; // In seconds.
//
$mod = $info->get_cm($this->cm->id);
// Trigger course module created event.
- $event = \core\event\course_module_created::create(array(
- 'courseid' => $this->course->id,
- 'context' => context_module::instance($mod->id),
- 'objectid' => $mod->id,
- 'other' => array(
- 'modulename' => $mod->modname,
- 'name' => $mod->name,
- 'instanceid' => $instanceid
- )
- ));
+ $event = \core\event\course_module_created::create_from_cm($mod);
$event->trigger();
$this->send_response($mod);
}
/**
- * Duplicate a module on the course.
+ * Duplicate a module on the course for ajax.
*
+ * @see mod_duplicate_module()
* @param object $course The course
* @param object $cm The course module to duplicate
+ * @param int $sr The section to link back to (used for creating the links)
* @throws moodle_exception if the plugin doesn't support duplication
* @return Object containing:
* - fullcontent: The HTML markup for the created CM
* - redirect: Whether to trigger a redirect following this change
*/
function mod_duplicate_activity($course, $cm, $sr = null) {
- global $CFG, $USER, $PAGE, $DB;
+ global $PAGE;
+
+ $newcm = duplicate_module($course, $cm);
+
+ $resp = new stdClass();
+ if ($newcm) {
+ $courserenderer = $PAGE->get_renderer('core', 'course');
+ $completioninfo = new completion_info($course);
+ $modulehtml = $courserenderer->course_section_cm($course, $completioninfo,
+ $newcm, null, array());
+ $resp->fullcontent = $courserenderer->course_section_cm_list_item($course, $completioninfo, $newcm, $sr);
+ $resp->cmid = $newcm->id;
+ } else {
+ // Trigger a redirect.
+ $resp->redirect = true;
+ }
+ return $resp;
+}
+
+/**
+ * Api to duplicate a module.
+ *
+ * @param object $course course object.
+ * @param object $cm course module object to be duplicated.
+ * @since Moodle 2.8
+ *
+ * @throws Exception
+ * @throws coding_exception
+ * @throws moodle_exception
+ * @throws restore_controller_exception
+ *
+ * @return cm_info|null cminfo object if we sucessfully duplicated the mod and found the new cm.
+ */
+function duplicate_module($course, $cm) {
+ global $CFG, $DB, $USER;
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
require_once($CFG->libdir . '/filelib.php');
$a->modname = format_string($cm->name);
if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) {
- throw new moodle_exception('duplicatenosupport', 'error');
+ throw new moodle_exception('duplicatenosupport', 'error', '', $a);
}
- // backup the activity
+ // Backup the activity.
$bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE,
backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
$bc->destroy();
- // restore the backup immediately
+ // Restore the backup immediately.
$rc = new restore_controller($backupid, $course->id,
backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
$rc->execute_plan();
- // now a bit hacky part follows - we try to get the cmid of the newly
- // restored copy of the module
+ // Now a bit hacky part follows - we try to get the cmid of the newly
+ // restored copy of the module.
$newcmid = null;
$tasks = $rc->get_plan()->get_tasks();
foreach ($tasks as $task) {
- error_log("Looking at a task");
if (is_subclass_of($task, 'restore_activity_task')) {
- error_log("Looking at a restore_activity_task task");
if ($task->get_old_contextid() == $cmcontext->id) {
- error_log("Contexts match");
$newcmid = $task->get_moduleid();
break;
}
}
}
- // if we know the cmid of the new course module, let us move it
+ // If we know the cmid of the new course module, let us move it
// right below the original one. otherwise it will stay at the
- // end of the section
+ // end of the section.
if ($newcmid) {
$info = get_fast_modinfo($course);
$newcm = $info->get_cm($newcmid);
$section = $DB->get_record('course_sections', array('id' => $cm->section, 'course' => $cm->course));
moveto_module($newcm, $section, $cm);
moveto_module($cm, $section, $newcm);
+
+ // Trigger course module created event. We can trigger the event only if we know the newcmid.
+ $event = \core\event\course_module_created::create_from_cm($newcm);
+ $event->trigger();
}
rebuild_course_cache($cm->course);
fulldelete($backupbasepath);
}
- $resp = new stdClass();
- if ($newcm) {
- $courserenderer = $PAGE->get_renderer('core', 'course');
- $completioninfo = new completion_info($course);
- $modulehtml = $courserenderer->course_section_cm($course, $completioninfo,
- $newcm, null, array());
-
- $resp->fullcontent = $courserenderer->course_section_cm_list_item($course, $completioninfo, $newcm, $sr);
- $resp->cmid = $newcm->id;
- } else {
- // Trigger a redirect
- $resp->redirect = true;
- }
- return $resp;
+ return isset($newcm) ? $newcm : null;
}
/**
*/
require_once(dirname(dirname(__FILE__)) . '/config.php');
-require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
-require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
-require_once($CFG->libdir . '/filelib.php');
$cmid = required_param('cmid', PARAM_INT);
$courseid = required_param('course', PARAM_INT);
$output = $PAGE->get_renderer('core', 'backup');
+// Duplicate the module.
+$newcm = duplicate_module($course, $cm);
+
+echo $output->header();
+
$a = new stdClass();
$a->modtype = get_string('modulename', $cm->modname);
$a->modname = format_string($cm->name);
-if (!plugin_supports('mod', $cm->modname, FEATURE_BACKUP_MOODLE2)) {
- $url = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn));
- print_error('duplicatenosupport', 'error', $url, $a);
-}
-
-// backup the activity
-
-$bc = new backup_controller(backup::TYPE_1ACTIVITY, $cm->id, backup::FORMAT_MOODLE,
- backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id);
-
-$backupid = $bc->get_backupid();
-$backupbasepath = $bc->get_plan()->get_basepath();
-
-$bc->execute_plan();
-
-$bc->destroy();
-
-// restore the backup immediately
-
-$rc = new restore_controller($backupid, $courseid,
- backup::INTERACTIVE_NO, backup::MODE_IMPORT, $USER->id, backup::TARGET_CURRENT_ADDING);
-
-if (!$rc->execute_precheck()) {
- $precheckresults = $rc->get_precheck_results();
- if (is_array($precheckresults) && !empty($precheckresults['errors'])) {
- if (empty($CFG->keeptempdirectoriesonbackup)) {
- fulldelete($backupbasepath);
- }
-
- echo $output->header();
- echo $output->precheck_notices($precheckresults);
- $url = course_get_url($course, $cm->sectionnum, array('sr' => $sectionreturn));
- echo $output->continue_button($url);
- echo $output->footer();
- die();
- }
-}
-
-$rc->execute_plan();
-
-// now a bit hacky part follows - we try to get the cmid of the newly
-// restored copy of the module
-$newcmid = null;
-$tasks = $rc->get_plan()->get_tasks();
-foreach ($tasks as $task) {
- if (is_subclass_of($task, 'restore_activity_task')) {
- if ($task->get_old_contextid() == $cmcontext->id) {
- $newcmid = $task->get_moduleid();
- break;
- }
- }
-}
-
-// if we know the cmid of the new course module, let us move it
-// right below the original one. otherwise it will stay at the
-// end of the section
-if ($newcmid) {
- $newcm = get_coursemodule_from_id('', $newcmid, $course->id, true, MUST_EXIST);
- moveto_module($newcm, $section, $cm);
- moveto_module($cm, $section, $newcm);
-}
-
-$rc->destroy();
-
-if (empty($CFG->keeptempdirectoriesonbackup)) {
- fulldelete($backupbasepath);
-}
-
-echo $output->header();
-
-if ($newcmid) {
+if (!empty($newcm)) {
echo $output->confirm(
get_string('duplicatesuccess', 'core', $a),
new single_button(
- new moodle_url('/course/modedit.php', array('update' => $newcmid, 'sr' => $sectionreturn)),
+ new moodle_url('/course/modedit.php', array('update' => $newcm->id, 'sr' => $sectionreturn)),
get_string('duplicatecontedit'),
'get'),
new single_button(
$sectionid = course_add_cm_to_section($course, $moduleinfo->coursemodule, $moduleinfo->section);
// Trigger event based on the action we did.
- $event = \core\event\course_module_created::create(array(
- 'courseid' => $course->id,
- 'context' => $modcontext,
- 'objectid' => $moduleinfo->coursemodule,
- 'other' => array(
- 'modulename' => $moduleinfo->modulename,
- 'name' => $moduleinfo->name,
- 'instanceid' => $moduleinfo->instance
- )
- ));
+ // Api create_from_cm expects modname and id property, and we don't want to modify $moduleinfo since we are returning it.
+ $eventdata = clone $moduleinfo;
+ $eventdata->modname = $eventdata->modulename;
+ $eventdata->id = $eventdata->coursemodule;
+ $event = \core\event\course_module_created::create_from_cm($eventdata, $modcontext);
$event->trigger();
$moduleinfo = edit_module_post_actions($moduleinfo, $course);
*/
protected function add_modchoosertoggle() {
global $CFG;
- static $modchoosertoggleadded = false;
- // Add the module chooser toggle to the course page
- if ($modchoosertoggleadded || $this->page->state > moodle_page::STATE_PRINTING_HEADER ||
+
+ // Only needs to be done once per page.
+ if (!$this->page->requires->should_create_one_time_item_now('core_course_modchoosertoggle')) {
+ return;
+ }
+
+ if ($this->page->state > moodle_page::STATE_PRINTING_HEADER ||
$this->page->course->id == SITEID ||
!$this->page->user_is_editing() ||
!($context = context_course::instance($this->page->course->id)) ||
!course_ajax_enabled($this->page->course) ||
!($coursenode = $this->page->settingsnav->find('courseadmin', navigation_node::TYPE_COURSE)) ||
!($turneditingnode = $coursenode->get('turneditingonoff'))) {
- // too late or we are on site page or we could not find the adjacent nodes in course settings menu
- // or we are not allowed to edit
+ // Too late, or we are on site page, or we could not find the
+ // adjacent nodes in course settings menu, or we are not allowed to edit.
return;
}
- $modchoosertoggleadded = true;
+
if ($this->page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
// We are on the course page, retain the current page params e.g. section.
$modchoosertoggleurl = clone($this->page->url);
* @return string The composed HTML for the module
*/
public function course_modchooser($modules, $course) {
- static $isdisplayed = false;
- if ($isdisplayed) {
+ if (!$this->page->requires->should_create_one_time_item_now('core_course_modchooser')) {
return '';
}
- $isdisplayed = true;
// Add the module chooser
$this->page->requires->yui_module('moodle-course-modchooser',
* Make sure that javascript file for AJAX expanding of courses and categories content is included
*/
protected function coursecat_include_js() {
- global $CFG;
- static $jsloaded = false;
- if (!$jsloaded) {
- // We must only load this module once.
- $this->page->requires->yui_module('moodle-course-categoryexpander',
- 'Y.Moodle.course.categoryexpander.init');
- $jsloaded = true;
+ if (!$this->page->requires->should_create_one_time_item_now('core_course_categoryexpanderjsinit')) {
+ return;
}
+
+ // We must only load this module once.
+ $this->page->requires->yui_module('moodle-course-categoryexpander',
+ 'Y.Moodle.course.categoryexpander.init');
}
/**
$modinfo = $this->create_specific_module_test('assign');
$events = $sink->get_events();
$event = array_pop($events);
- $sink->close();
- $cm = $DB->get_record('course_modules', array('id' => $modinfo->coursemodule), '*', MUST_EXIST);
+ $cm = get_coursemodule_from_id('assign', $modinfo->coursemodule, 0, false, MUST_EXIST);
$mod = $DB->get_record('assign', array('id' => $modinfo->instance), '*', MUST_EXIST);
// Validate event data.
$this->assertEventLegacyLogData($arr, $event);
$this->assertEventContextNotUsed($event);
+ // Let us see if duplicating an activity results in a nice course module created event.
+ $sink->clear();
+ $course = get_course($mod->course);
+ $newcm = duplicate_module($course, $cm);
+ $events = $sink->get_events();
+ $event = array_pop($events);
+ $sink->close();
+
+ // Validate event data.
+ $this->assertInstanceOf('\core\event\course_module_created', $event);
+ $this->assertEquals($newcm->id, $event->objectid);
+ $this->assertEquals($USER->id, $event->userid);
+ $this->assertEquals($course->id, $event->courseid);
+ $url = new moodle_url('/mod/assign/view.php', array('id' => $newcm->id));
+ $this->assertEquals($url, $event->get_url());
}
/**
$this->assertEventLegacyLogData($expectedlegacydata, $event);
$this->assertEventContextNotUsed($event);
}
+
+ /**
+ * Test duplicate_module()
+ */
+ public function test_duplicate_module() {
+ $this->setAdminUser();
+ $this->resetAfterTest();
+ $course = self::getDataGenerator()->create_course();
+ $res = self::getDataGenerator()->create_module('resource', array('course' => $course));
+ $cm = get_coursemodule_from_id('resource', $res->cmid, 0, false, MUST_EXIST);
+
+ $newcm = duplicate_module($course, $cm);
+
+ // Make sure they are the same, except obvious id changes.
+ foreach ($cm as $prop => $value) {
+ if ($prop == 'id' || $prop == 'url' || $prop == 'instance' || $prop == 'added') {
+ // Ignore obviously different properties.
+ continue;
+ }
+ $this->assertEquals($value, $newcm->$prop);
+ }
+ }
}
$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';
$coursefield = trim($this->get_config('remotecoursefield'));
$userfield = trim($this->get_config('remoteuserfield'));
$rolefield = trim($this->get_config('remoterolefield'));
+ $otheruserfield = trim($this->get_config('remoteotheruserfield'));
// Lowercased versions - necessary because we normalise the resultset with array_change_key_case().
$coursefield_l = strtolower($coursefield);
$userfield_l = strtolower($userfield);
$rolefield_l = strtolower($rolefield);
+ $otheruserfieldlower = strtolower($otheruserfield);
$localrolefield = $this->get_config('localrolefield');
$localuserfield = $this->get_config('localuserfield');
$roles[$role->$localrolefield] = $role->id;
}
+ $roleassigns = array();
$enrols = array();
$instances = array();
$roleid = $roles[$fields[$rolefield_l]];
}
- if (empty($enrols[$course->id])) {
- $enrols[$course->id] = array();
+ $roleassigns[$course->id][$roleid] = $roleid;
+ if (empty($fields[$otheruserfieldlower])) {
+ $enrols[$course->id][$roleid] = $roleid;
}
- $enrols[$course->id][] = $roleid;
if ($instance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>'database'), '*', IGNORE_MULTIPLE)) {
$instances[$course->id] = $instance;
}
// Enrol user into courses and sync roles.
- foreach ($enrols as $courseid => $roles) {
+ foreach ($roleassigns as $courseid => $roles) {
if (!isset($instances[$courseid])) {
// Ignored.
continue;
}
$instance = $instances[$courseid];
- if ($e = $DB->get_record('user_enrolments', array('userid'=>$user->id, 'enrolid'=>$instance->id))) {
- // Reenable enrolment when previously disable enrolment refreshed.
- if ($e->status == ENROL_USER_SUSPENDED) {
- $this->update_user_enrol($instance, $user->id, ENROL_USER_ACTIVE);
+ if (isset($enrols[$courseid])) {
+ if ($e = $DB->get_record('user_enrolments', array('userid' => $user->id, 'enrolid' => $instance->id))) {
+ // Reenable enrolment when previously disable enrolment refreshed.
+ if ($e->status == ENROL_USER_SUSPENDED) {
+ $this->update_user_enrol($instance, $user->id, ENROL_USER_ACTIVE);
+ }
+ } else {
+ $roleid = reset($enrols[$courseid]);
+ $this->enrol_user($instance, $user->id, $roleid, 0, 0, ENROL_USER_ACTIVE);
}
- } else {
- $roleid = reset($roles);
- $this->enrol_user($instance, $user->id, $roleid, 0, 0, ENROL_USER_ACTIVE);
}
if (!$context = context_course::instance($instance->courseid, IGNORE_MISSING)) {
$existing = array();
foreach ($current as $r) {
- if (in_array($r->roleid, $roles)) {
+ if (isset($roles[$r->roleid])) {
$existing[$r->roleid] = $r->roleid;
} else {
role_unassign($r->roleid, $user->id, $context->id, 'enrol_database', $instance->id);
// Unenrol as necessary.
$sql = "SELECT e.*, c.visible AS cvisible, ue.status AS ustatus
FROM {enrol} e
- JOIN {user_enrolments} ue ON ue.enrolid = e.id
JOIN {course} c ON c.id = e.courseid
- WHERE ue.userid = :userid AND e.enrol = 'database'";
+ JOIN {role_assignments} ra ON ra.itemid = e.id
+ LEFT JOIN {user_enrolments} ue ON ue.enrolid = e.id
+ WHERE ra.userid = :userid AND e.enrol = 'database'";
$rs = $DB->get_recordset_sql($sql, array('userid'=>$user->id));
foreach ($rs as $instance) {
if (!$instance->cvisible and $ignorehidden) {
$this->update_user_enrol($instance, $user->id, ENROL_USER_SUSPENDED);
}
if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
+ if (!empty($roleassigns[$instance->courseid])) {
+ // We want this "other user" to keep their roles.
+ continue;
+ }
role_unassign_all(array('contextid'=>$context->id, 'userid'=>$user->id, 'component'=>'enrol_database', 'itemid'=>$instance->id));
}
}
$coursefield = trim($this->get_config('remotecoursefield'));
$userfield = trim($this->get_config('remoteuserfield'));
$rolefield = trim($this->get_config('remoterolefield'));
+ $otheruserfield = trim($this->get_config('remoteotheruserfield'));
// Lowercased versions - necessary because we normalise the resultset with array_change_key_case().
$coursefield_l = strtolower($coursefield);
$userfield_l = strtolower($userfield);
$rolefield_l = strtolower($rolefield);
+ $otheruserfieldlower = strtolower($otheruserfield);
$localrolefield = $this->get_config('localrolefield');
$localuserfield = $this->get_config('localuserfield');
if ($rolefield) {
$sqlfields[] = $rolefield;
}
+ if ($otheruserfield) {
+ $sqlfields[] = $otheruserfield;
+ }
foreach ($existing as $course) {
if ($ignorehidden and !$course->visible) {
continue;
$context = context_course::instance($course->id);
// Get current list of enrolled users with their roles.
- $current_roles = array();
- $current_status = array();
- $user_mapping = array();
- $sql = "SELECT u.$localuserfield AS mapping, u.id, ue.status, ue.userid, ra.roleid
+ $currentroles = array();
+ $currentenrols = array();
+ $currentstatus = array();
+ $usermapping = array();
+ $sql = "SELECT u.$localuserfield AS mapping, u.id AS userid, ue.status, ra.roleid
FROM {user} u
- JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)
- JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.itemid = ue.enrolid AND ra.component = 'enrol_database')
+ JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.component = 'enrol_database' AND ra.itemid = :enrolid)
+ LEFT JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = ra.itemid)
WHERE u.deleted = 0";
$params = array('enrolid'=>$instance->id);
if ($localuserfield === 'username') {
}
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $ue) {
- $current_roles[$ue->userid][$ue->roleid] = $ue->roleid;
- $current_status[$ue->userid] = $ue->status;
- $user_mapping[$ue->mapping] = $ue->userid;
+ $currentroles[$ue->userid][$ue->roleid] = $ue->roleid;
+ $usermapping[$ue->mapping] = $ue->userid;
+
+ if (isset($ue->status)) {
+ $currentenrols[$ue->userid][$ue->roleid] = $ue->roleid;
+ $currentstatus[$ue->userid] = $ue->status;
+ }
}
$rs->close();
// Get list of users that need to be enrolled and their roles.
- $requested_roles = array();
+ $requestedroles = array();
+ $requestedenrols = array();
$sql = $this->db_get_sql($table, array($coursefield=>$course->mapping), $sqlfields);
if ($rs = $extdb->Execute($sql)) {
if (!$rs->EOF) {
continue;
}
$mapping = $fields[$userfield_l];
- if (!isset($user_mapping[$mapping])) {
+ if (!isset($usermapping[$mapping])) {
$usersearch[$localuserfield] = $mapping;
if (!$user = $DB->get_record('user', $usersearch, 'id', IGNORE_MULTIPLE)) {
$trace->output("error: skipping unknown user $localuserfield '$mapping' in course '$course->mapping'", 1);
continue;
}
- $user_mapping[$mapping] = $user->id;
+ $usermapping[$mapping] = $user->id;
$userid = $user->id;
} else {
- $userid = $user_mapping[$mapping];
+ $userid = $usermapping[$mapping];
}
if (empty($fields[$rolefield_l]) or !isset($roles[$fields[$rolefield_l]])) {
if (!$defaultrole) {
$roleid = $roles[$fields[$rolefield_l]];
}
- $requested_roles[$userid][$roleid] = $roleid;
+ $requestedroles[$userid][$roleid] = $roleid;
+ if (empty($fields[$otheruserfieldlower])) {
+ $requestedenrols[$userid][$roleid] = $roleid;
+ }
}
}
$rs->Close();
$trace->output("error: skipping course '$course->mapping' - could not match with external database", 1);
continue;
}
- unset($user_mapping);
+ unset($usermapping);
// Enrol all users and sync roles.
- foreach ($requested_roles as $userid=>$userroles) {
+ foreach ($requestedenrols as $userid => $userroles) {
foreach ($userroles as $roleid) {
- if (empty($current_roles[$userid])) {
+ if (empty($currentenrols[$userid])) {
$this->enrol_user($instance, $userid, $roleid, 0, 0, ENROL_USER_ACTIVE);
- $current_roles[$userid][$roleid] = $roleid;
- $current_status[$userid] = ENROL_USER_ACTIVE;
+ $currentroles[$userid][$roleid] = $roleid;
+ $currentenrols[$userid][$roleid] = $roleid;
+ $currentstatus[$userid] = ENROL_USER_ACTIVE;
$trace->output("enrolling: $userid ==> $course->shortname as ".$allroles[$roleid]->shortname, 1);
}
}
+ // Reenable enrolment when previously disable enrolment refreshed.
+ if ($currentstatus[$userid] == ENROL_USER_SUSPENDED) {
+ $this->update_user_enrol($instance, $userid, ENROL_USER_ACTIVE);
+ $trace->output("unsuspending: $userid ==> $course->shortname", 1);
+ }
+ }
+
+ foreach ($requestedroles as $userid => $userroles) {
// Assign extra roles.
foreach ($userroles as $roleid) {
- if (empty($current_roles[$userid][$roleid])) {
+ if (empty($currentroles[$userid][$roleid])) {
role_assign($roleid, $userid, $context->id, 'enrol_database', $instance->id);
- $current_roles[$userid][$roleid] = $roleid;
+ $currentroles[$userid][$roleid] = $roleid;
$trace->output("assigning roles: $userid ==> $course->shortname as ".$allroles[$roleid]->shortname, 1);
}
}
// Unassign removed roles.
- foreach($current_roles[$userid] as $cr) {
+ foreach ($currentroles[$userid] as $cr) {
if (empty($userroles[$cr])) {
role_unassign($cr, $userid, $context->id, 'enrol_database', $instance->id);
- unset($current_roles[$userid][$cr]);
+ unset($currentroles[$userid][$cr]);
$trace->output("unsassigning roles: $userid ==> $course->shortname", 1);
}
}
- // Reenable enrolment when previously disable enrolment refreshed.
- if ($current_status[$userid] == ENROL_USER_SUSPENDED) {
- $this->update_user_enrol($instance, $userid, ENROL_USER_ACTIVE);
- $trace->output("unsuspending: $userid ==> $course->shortname", 1);
- }
+ unset($currentroles[$userid]);
+ }
+
+ foreach ($currentroles as $userid => $userroles) {
+ // These are roles that exist only in Moodle, not the external database
+ // so make sure the unenrol actions will handle them by setting status.
+ $currentstatus += array($userid => ENROL_USER_ACTIVE);
}
// Deal with enrolments removed from external table.
if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
if (!$preventfullunenrol) {
// Unenrol.
- foreach ($current_status as $userid=>$status) {
- if (isset($requested_roles[$userid])) {
+ foreach ($currentstatus as $userid => $status) {
+ if (isset($requestedenrols[$userid])) {
continue;
}
$this->unenrol_user($instance, $userid);
} else if ($unenrolaction == ENROL_EXT_REMOVED_SUSPEND or $unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
// Suspend enrolments.
- foreach ($current_status as $userid=>$status) {
- if (isset($requested_roles[$userid])) {
+ foreach ($currentstatus as $userid => $status) {
+ if (isset($requestedenrols[$userid])) {
continue;
}
if ($status != ENROL_USER_SUSPENDED) {
$trace->output("suspending: $userid ==> $course->shortname", 1);
}
if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
+ if (isset($requestedroles[$userid])) {
+ // We want this "other user" to keep their roles.
+ continue;
+ }
role_unassign_all(array('contextid'=>$context->id, 'userid'=>$userid, 'component'=>'enrol_database', 'itemid'=>$instance->id));
+
$trace->output("unsassigning all roles: $userid ==> $course->shortname", 1);
}
}
$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');
$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);
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);
set_config('newcoursecategory', 'category', 'enrol_database');
// Create some test users and courses.
- for($i=1;$i<=3;$i++) {
- self::$courses[$i] = $this->getDataGenerator()->create_course(array('fullname'=>'Test course '.$i, 'shortname'=>'tc'.$i, 'idnumber'=>'courseid'.$i));
+ 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'));
+ 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) {
$DB->delete_records('enrol_database_test_courses', array());
$plugin = enrol_get_plugin('database');
- $instances = $DB->get_records('enrol', array('enrol'=>'database'));
+ $instances = $DB->get_records('enrol', array('enrol' => 'database'));
foreach($instances as $instance) {
$plugin->delete_instance($instance);
}
}
- public function assertIsEnrolled($userindex, $courseindex, $status=null, $rolename = null) {
+ 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);
+ $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);
+ $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)));
+ $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)));
+ $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)));
}
}
- public function assertIsNotEnrolled($userindex, $courseindex) {
+ protected function assertIsNotEnrolled($userindex, $courseindex) {
global $DB;
- if (!$dbinstance = $DB->get_record('enrol', array('courseid'=>self::$courses[$courseindex]->id, 'enrol'=>'database'))) {
+ 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)));
+ $this->assertFalse($DB->record_exists('user_enrolments', array('enrolid' => $dbinstance->id, 'userid' => self::$users[$userindex]->id)));
}
public function test_sync_user_enrolments() {
$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'=>'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.
+ $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')));
+ $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->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[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->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->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'));
+ $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(2, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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(2, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(2, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(2, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(2, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(2, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(2, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(2, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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')));
+ $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));
+ $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->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->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));
+ $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->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->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));
+ $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->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->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'=>'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.
+ $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')));
+ $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(2, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(3, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(3, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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(3, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(3, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(3, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(3, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(3, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(3, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(5, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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'));
+ $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(3, $DB->count_records('enrol', array('enrol'=>'database')));
- $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
+ $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');
$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')));
+ $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));
+ $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->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->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));
+ $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->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->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));
+ $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->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();
- $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));
+ $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')));
+ $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')));
+ $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->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->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->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->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')));
+ $this->assertEquals(3, $DB->count_records('enrol', array('enrol' => 'database')));
+ $this->assertEquals(0, $DB->count_records('role_assignments', array('component' => 'enrol_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')));
+ $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);
+ $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);
+ $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);
// 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')));
+ $this->assertEquals(1, $DB->count_records('course', array('idnumber' => 'yy')));
+ $this->assertEquals(1, $DB->count_records('course', array('shortname' => 'xx')));
// Test category mapping via idnumber.
$plugin->set_config('localcategoryfield', 'idnumber');
- $course7 = array('fullname'=>'New course 7', 'shortname'=>'nc7', 'idnumber'=>'ncid7', 'category'=>'tcid1');
+ $course7 = array('fullname' => 'New course 7', 'shortname' => 'nc7', 'idnumber' => 'ncid7', 'category' => 'tcid1');
$DB->insert_record('enrol_database_test_courses', $course7);
$plugin->sync_courses($trace);
// Test course template.
- $template = $this->getDataGenerator()->create_course(array('numsections'=>666, 'shortname'=>'crstempl'));
+ $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);
+ $course8 = array('fullname' => 'New course 8', 'shortname' => 'nc8', 'idnumber' => 'ncid8', 'category' => null);
$DB->insert_record('enrol_database_test_courses', $course8);
$plugin->sync_courses($trace);
// Test invalid category.
- $course9 = array('fullname'=>'New course 9', 'shortname'=>'nc9', 'idnumber'=>'ncid9', 'category'=>'xxxxxxx');
+ $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')));
+ $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')));
+ $this->assertTrue($DB->record_exists('course', array('idnumber' => 'ncid9')));
// Final cleanup - remove extra tables, fixtures and caches.
* @return string HTML fragment
*/
public function render_form_filemanager($fm) {
- static $filemanagertemplateloaded;
$html = $this->fm_print_generallayout($fm);
$module = array(
'name'=>'form_filemanager',
array('confirmrenamefile', 'repository'), array('newfolder', 'repository'), array('edit', 'moodle')
)
);
- if (empty($filemanagertemplateloaded)) {
- $filemanagertemplateloaded = true;
+ if ($this->page->requires->should_create_one_time_item_now('core_file_managertemplate')) {
$this->page->requires->js_init_call('M.form_filemanager.set_templates',
array($this->filemanager_js_templates()), true, $module);
}
binary at <a href="http://moodle.org/download/mimetex/">
http://moodle.org/download/mimetex/</a>. You may then also need to
edit your moodle/filter/algebra/pix.php file to add
-<br /><?PHP echo "case "" . PHP_OS . "":" ;?><br ?> to the list of operating systems
+<br /><?php echo "case "" . PHP_OS . "":" ;?><br ?> to the list of operating systems
in the switch (PHP_OS) statement. Windows users may have a problem properly
unzipping mimetex.exe. Make sure that mimetex.exe is is <b>PRECISELY</b>
433152 bytes in size. If not, download fresh copy from