/** * Test that a record can be modified, and that the corresponding Moodle * user is modified. */ public function test_canupdaterecordandsynctomoodle() { global $DB; require_once elispm::lib('lib.php'); $this->load_csv_data(); // Read a record. $src = new user(103, null, array(), false, array()); $src->reset_custom_field_list(); // Modify the data. $src->firstname = 'Testuser'; $src->lastname = 'One'; $src->field_sometext = 'boo'; $src->field_sometextfrompm = 'bla'; $src->save(); // Read it back. $retr = new user($src->id, null, array(), false, array()); $this->assertEquals($src->firstname, $retr->firstname); $this->assertEquals($src->lastname, $retr->lastname); // Check the Moodle user. $retr = $DB->get_record('user', array('id' => 100)); profile_load_data($retr); fix_moodle_profile_fields($retr); $this->assertEquals($src->firstname, $retr->firstname); $this->assertEquals($src->lastname, $retr->lastname); // Check custom fields. $result = new moodle_recordset_phpunit_datatable('user_info_data', $DB->get_records('user_info_data', null, '', 'id, userid, fieldid, data')); $dataset = new PHPUnit_Extensions_Database_DataSet_CsvDataSet(); $dataset->addTable('user_info_data', elispm::file('tests/fixtures/user_info_data.csv')); $dataset = new PHPUnit_Extensions_Database_DataSet_ReplacementDataSet($dataset); // Only the second text field should be changed; everything else should be the same. $dataset->addFullReplacement('Second text entry field', 'bla'); $this->assertTablesEqual($dataset->getTable('user_info_data'), $result); }
/** * Migrate a single Moodle user to the Program Management system. Will * only do this for users who have an idnumber set. * * @param object $mu Moodle user object * @return boolean Whether user was synchronized or not */ function pm_moodle_user_to_pm($mu) { global $CFG, $DB; require_once $CFG->dirroot . '/lib/moodlelib.php'; require_once elis::lib('data/customfield.class.php'); require_once elispm::lib('data/user.class.php'); require_once elispm::lib('data/usermoodle.class.php'); require_once elis::lib('data/data_filter.class.php'); require_once $CFG->dirroot . '/user/profile/lib.php'; require_once elis::lib('lib.php'); if (!isset($mu->id)) { return true; } // re-fetch, in case this is from a stale event $mu = $DB->get_record('user', array('id' => $mu->id)); if (user_not_fully_set_up($mu) || !$mu->confirmed) { // Prevent the sync if a bare-bones user record is being created by create_user_record // or Moodle user has not yet been confirmed. return true; } //not going to be concerned with city or password for now if (empty($mu->idnumber) && elis::$config->local_elisprogram->auto_assign_user_idnumber) { //make sure the current user's username does not match up with some other user's //idnumber (necessary since usernames and idnumbers aren't bound to one another) if (!$DB->record_exists('user', array('idnumber' => $mu->username))) { $mu->idnumber = $mu->username; $DB->update_record('user', $mu); } } // skip user if no ID number set if (empty($mu->idnumber)) { return true; } // track whether we're syncing an idnumber change over to the PM system $idnumber_updated = false; // track whether an associated Moodle user is linked to the current PM user $moodle_user_exists = false; // determine if the user is already noted as having been associated to a PM user // this will join to Moodle user and PM user table to ensure data correctness $filters = array(); $filters[] = new join_filter('muserid', 'user', 'id'); $filters[] = new join_filter('cuserid', user::TABLE, 'id'); $filters[] = new field_filter('muserid', $mu->id); if ($um = usermoodle::find($filters)) { if ($um->valid()) { $um = $um->current(); //signal that an associated user already exists $moodle_user_exists = true; // determine if the Moodle user idnumber was updated if ($um->idnumber != $mu->idnumber) { //signal that the idnumber was synced over $idnumber_updated = true; // update the PM user with the new idnumber $cmuser = new user(); $cmuser->id = $um->cuserid; $cmuser->idnumber = $mu->idnumber; $cmuser->save(); // update the association table with the new idnumber $um->idnumber = $mu->idnumber; $um->save(); } } } // find the linked PM user //filter for the basic condition on the Moodle user id $condition_filter = new field_filter('id', $mu->id); //filter for joining the association table $association_filter = new join_filter('muserid', 'user', 'id', $condition_filter); //outermost filter $filter = new join_filter('id', usermoodle::TABLE, 'cuserid', $association_filter); $cu = user::find($filter); if ($cu->valid()) { $cu = $cu->current(); } else { // if a user with the same username but different idnumber exists, // we can't sync over because it will violate PM user uniqueness // constraints $cu = user::find(new field_filter('username', $mu->username)); if ($cu->valid()) { return true; } // if no such PM user exists, create a new one $cu = new user(); $cu->transfercredits = 0; $cu->timecreated = time(); } // synchronize standard fields $cu->username = $mu->username; $cu->password = $mu->password; // only need to update the idnumber if it wasn't handled above if (!$idnumber_updated) { $cu->idnumber = $mu->idnumber; } $cu->firstname = $mu->firstname; $cu->lastname = $mu->lastname; $cu->email = $mu->email; $cu->address = $mu->address; $cu->city = $mu->city; $cu->country = $mu->country; if (!empty($mu->phone1)) { $cu->phone = $mu->phone1; } if (!empty($mu->phone2)) { $cu->phone2 = $mu->phone2; } if (!empty($mu->lang)) { $cu->language = $mu->lang; } $cu->timemodified = time(); // synchronize custom profile fields profile_load_data($mu); fix_moodle_profile_fields($mu); $fields = field::get_for_context_level(CONTEXT_ELIS_USER); $fields = $fields ? $fields : array(); require_once elis::plugin_file('elisfields_moodleprofile', 'custom_fields.php'); foreach ($fields as $field) { $field = new field($field); if (!moodle_profile_can_sync($field->shortname)) { continue; } if (isset($field->owners['moodle_profile']) && isset($mu->{"profile_field_{$field->shortname}"})) { // check if should sync user profile field settings if ($field->owners['moodle_profile']->exclude == pm_moodle_profile::sync_from_moodle) { sync_profile_field_settings_from_moodle($field); } $fieldname = "field_{$field->shortname}"; $cu->{$fieldname} = $mu->{"profile_field_{$field->shortname}"}; } } //specifically tell the user save not to use the local_elisprogram_usr_mdl for syncing //because the record hasn't been inserted yet (see below) try { $cu->save(false); } catch (Exception $ex) { if (in_cron()) { mtrace(get_string('record_not_created_reason', 'local_elisprogram', array('message' => $ex->getMessage() . " [{$mu->id}]"))); return false; } else { throw new Exception($ex->getMessage()); } } // if no user association record exists, create one if (!$moodle_user_exists) { $um = new usermoodle(); $um->cuserid = $cu->id; $um->muserid = $mu->id; $um->idnumber = $mu->idnumber; $um->save(); } return true; }
/** * Validate new library method fix_moodle_profile_fields() */ public function test_fix_moodle_profile_fields() { global $CFG; require_once $CFG->dirroot . '/user/profile/lib.php'; require_once $CFG->dirroot . '/user/profile/definelib.php'; require_once $CFG->dirroot . '/user/profile/field/menu/define.class.php'; require_once $CFG->dirroot . '/user/profile/field/menu/field.class.php'; require_once $CFG->dirroot . '/user/profile/field/checkbox/define.class.php'; require_once $CFG->dirroot . '/user/profile/field/checkbox/field.class.php'; require_once elis::lib('lib.php'); require_once elis::lib('testlib.php'); // Create a couple Moodle profile fields, one menu-of-choices $profiledefinemenu = new profile_define_menu(); $data = new stdClass(); $data->datatype = 'menu'; $data->categoryid = 99999; $data->shortname = 'testfieldmenu'; $data->name = 'testfieldmenu'; $data->param1 = 'Option1 Option2'; $data->defaultdata = 'Option2'; $profiledefinemenu->define_save($data); $profiledefinecheckbox = new profile_define_checkbox(); $data = new stdClass(); $data->datatype = 'checkbox'; $data->categoryid = 99999; $data->shortname = 'testfieldcheckbox'; $data->name = 'testfieldcheckbox'; $profiledefinecheckbox->define_save($data); $testuser = get_test_user(); $testuser->profile_field_testfieldmenu = 'Option3'; // illegal value $testuser->profile_field_testfieldcheckbox = 0; fix_moodle_profile_fields($testuser); $this->assertTrue(!$testuser->profile_field_testfieldcheckbox); $this->assertTrue(!isset($testuser->profile_field_testfieldmenu)); $testuser->profile_field_testfieldmenu = 'Option1'; // legal value $testuser->profile_field_testfieldcheckbox = 1; fix_moodle_profile_fields($testuser); $this->assertTrue($testuser->profile_field_testfieldcheckbox == 1); $this->assertTrue($testuser->profile_field_testfieldmenu == 'Option1'); }
/** * Function to synchronize the curriculum data with the Moodle data. * * @param boolean $tomoodle Optional direction to synchronize the data. * @param boolean $strict_match Whether we should use the association table rather * than just check idnumbers when comparing to Moodle users * */ function synchronize_moodle_user($tomoodle = true, $createnew = false, $strict_match = true) { global $CFG; require_once $CFG->dirroot . '/admin/tool/uploaduser/locallib.php'; require_once elispm::lib('data/usermoodle.class.php'); require_once elis::lib('lib.php'); static $mu_loop_detect = array(); // Create a new Moodle user record to update with. if (!($muser = $this->get_moodleuser($strict_match)) && !$createnew) { return false; } $muserid = $muser ? $muser->id : false; if ($tomoodle) { // map PM user fields to Moodle user fields $mdlfieldmap = array('idnumber' => 'idnumber', 'username' => 'username', 'firstname' => 'firstname', 'lastname' => 'lastname', 'email' => 'email', 'address' => 'address', 'city' => 'city', 'country' => 'country', 'language' => 'lang'); // determine if the user is already noted as having been associated to a PM user if ($um = usermoodle::find(new field_filter('cuserid', $this->id))) { if ($um->valid()) { $um = $um->current(); // determine if the PM user idnumber was updated if ($um->idnumber != $this->idnumber) { // update the Moodle user with the new idnumber $muser = new stdClass(); $muser->id = $um->muserid; $muser->idnumber = $this->idnumber; $this->_db->update_record('user', $muser); // update the association table with the new idnumber $um->idnumber = $this->idnumber; $um->save(); } } } //try to update the idnumber of a matching Moodle user that //doesn't have an idnumber set yet $exists_params = array('username' => $this->username, 'mnethostid' => $CFG->mnet_localhost_id); if ($moodle_user = $this->_db->get_record('user', $exists_params)) { if (empty($moodle_user->idnumber)) { //potentially old data, so set the idnumber $moodle_user->idnumber = $this->idnumber; $this->_db->update_record('user', $moodle_user); $muserid = $moodle_user->id; } else { if ($this->idnumber != $moodle_user->idnumber) { //the username points to a pre-existing Moodle user //with a non-matching idnumber, so something horrible //happened return; } } } if ($createnew && !$muserid) { /// Add a new user $record = new stdClass(); foreach ($mdlfieldmap as $pmfield => $mdlfield) { if (isset($this->{$pmfield})) { $record->{$mdlfield} = $this->{$pmfield}; } } $record->password = $this->password === null ? '' : $this->password; $record->confirmed = 1; $record->mnethostid = $CFG->mnet_localhost_id; $record->timemodified = time(); $record->id = $this->_db->insert_record('user', $record); } else { if ($muserid) { /// Update an existing user $record = new stdClass(); $record->id = $muserid; foreach ($mdlfieldmap as $pmfield => $mdlfield) { if (isset($this->{$pmfield})) { $record->{$mdlfield} = $this->{$pmfield}; } } if (!empty($this->password)) { $record->password = $this->password; } $record->timemodified = time(); $this->_db->update_record('user', $record); } else { return true; } } // avoid update loops if (isset($mu_loop_detect[$this->id])) { return $record->id; } $mu_loop_detect[$this->id] = true; // synchronize profile fields $origrec = clone $record; profile_load_data($origrec); fix_moodle_profile_fields($origrec); $fields = field::get_for_context_level(CONTEXT_ELIS_USER); $mfields = $this->_db->get_records('user_info_field', array(), '', 'shortname'); $fields = $fields ? $fields : array(); $changed = false; require_once elis::plugin_file('elisfields_moodleprofile', 'custom_fields.php'); foreach ($fields as $field) { $field = new field($field); if (!moodle_profile_can_sync($field->shortname)) { continue; } if (isset($field->owners['moodle_profile']) && $field->owners['moodle_profile']->exclude == pm_moodle_profile::sync_to_moodle && isset($mfields[$field->shortname])) { $shortname = $field->shortname; $fieldname = "field_{$shortname}"; $mfieldname = "profile_{$fieldname}"; $mfieldvalue = isset($origrec->{$mfieldname}) ? $origrec->{$mfieldname} : null; if ($mfieldvalue != $this->{$fieldname}) { $record->{$mfieldname} = $this->{$fieldname}; $changed = true; sync_profile_field_settings_to_moodle($field); } } } $record = uu_pre_process_custom_profile_data($record); profile_save_data($record); if ($muserid) { if ($changed) { events_trigger('user_updated', $record); } } else { // if no user association record exists, create one $um = new usermoodle(); $um->cuserid = $this->id; $um->muserid = $record->id; $um->idnumber = $this->idnumber; $um->save(); events_trigger('user_created', $record); } unset($mu_loop_detect[$this->id]); return $record->id; } }
/** * Validate that the import does not create bogus profile field data on user update */ public function test_version1importvalidatesprofilefieldsonupdate() { global $CFG, $DB; // Run the "create user" import. $this->run_core_user_import(array()); $userid = $DB->get_field('user', 'id', array('username' => 'rlipusername', 'mnethostid' => $CFG->mnet_localhost_id)); // Create the category. $category = new stdClass(); $category->sortorder = $DB->count_records('user_info_category') + 1; $category->id = $DB->insert_record('user_info_category', $category); // Try to insert bogus checkbox data. $this->create_profile_field('rlipcheckbox', 'checkbox', $category->id); $params = array('action' => 'update', 'username' => 'rlipusername', 'profile_field_rlipcheckbox' => '2'); $this->run_core_user_import($params); $user = new stdClass(); $user->id = $userid; profile_load_data($user); fix_moodle_profile_fields($user); $this->assertEquals(isset($user->profile_field_rlipcheckbox), false); // Try to insert bogus datetime data. $this->create_profile_field('rlipdatetime', 'datetime', $category->id); $params = array('action' => 'update', 'username' => 'rlipusername', 'profile_field_rlipdatetime' => '1000000000'); $this->run_core_user_import($params); $user = new stdClass(); $user->id = $userid; profile_load_data($user); fix_moodle_profile_fields($user); $this->assertEquals(isset($user->profile_field_rlipcheckbox), false); // Try to insert bogus menu data. $this->create_profile_field('rlipmenu', 'menu', $category->id, "rlipoption1\nrlipoption1B"); $params = array('action' => 'update', 'username' => 'rlipusername', 'profile_field_rlipmenu' => 'rlipoption2'); $this->run_core_user_import($params); $user = new stdClass(); $user->id = $userid; profile_load_data($user); fix_moodle_profile_fields($user); $this->assertEquals(isset($user->profile_field_rlipcheckbox), false); }