From 8bdb31ed54412f9de7f8d84830ac406a4d57c63a Mon Sep 17 00:00:00 2001 From: Petr Skoda Date: Sat, 26 Feb 2011 15:16:51 +0100 Subject: [PATCH] MDL-26564 fix regressions and other problems in csv user upload This patch fixes incorrect password creating, updating and resetting, updating of user fields, unsupported auth plugins are correctly identified, modification of mnethostid is prevented, fixed problem with email duplicates, new password is generated for users without email, etc. It also includes coding style improvements, more inline docs, future TODOs and license information. --- admin/uploaduser.php | 1104 +++++++++++++----------------------- admin/uploaduser_form.php | 172 ++++-- admin/uploaduserlib.php | 335 +++++++++++ auth/manual/db/upgrade.php | 49 ++ auth/manual/version.php | 29 + lang/en/admin.php | 5 +- lang/en/error.php | 1 + lang/en/moodle.php | 3 - lib/db/upgrade.php | 8 + lib/moodlelib.php | 5 +- login/change_password.php | 3 +- user/editadvanced.php | 1 + version.php | 2 +- 13 files changed, 923 insertions(+), 794 deletions(-) create mode 100644 admin/uploaduserlib.php create mode 100644 auth/manual/db/upgrade.php create mode 100644 auth/manual/version.php diff --git a/admin/uploaduser.php b/admin/uploaduser.php index 5ee0e8a15d2..c2be9f1a67a 100755 --- a/admin/uploaduser.php +++ b/admin/uploaduser.php @@ -1,40 +1,46 @@ . -/// Bulk user registration script from a comma separated file -/// Returns list of users with their user ids +/** + * Bulk user registration script from a comma separated file + * + * @package core + * @subpackage admin + * @copyright 2004 onwards Martin Dougiamas (http://dougiamas.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ require('../config.php'); require_once($CFG->libdir.'/adminlib.php'); require_once($CFG->libdir.'/csvlib.class.php'); require_once($CFG->dirroot.'/user/profile/lib.php'); require_once($CFG->dirroot.'/group/lib.php'); +require_once('uploaduserlib.php'); require_once('uploaduser_form.php'); $iid = optional_param('iid', '', PARAM_INT); $previewrows = optional_param('previewrows', 10, PARAM_INT); -$readcount = optional_param('readcount', 0, PARAM_INT); -$uploadtype = optional_param('uutype', 0, PARAM_INT); -define('UU_ADDNEW', 0); -define('UU_ADDINC', 1); -define('UU_ADD_UPDATE', 2); -define('UU_UPDATE', 3); - -$choices = array(UU_ADDNEW => get_string('uuoptype_addnew', 'admin'), - UU_ADDINC => get_string('uuoptype_addinc', 'admin'), - UU_ADD_UPDATE => get_string('uuoptype_addupdate', 'admin'), - UU_UPDATE => get_string('uuoptype_update', 'admin')); - -@set_time_limit(3600); // 1 hour should be enough -raise_memory_limit(MEMORY_EXTRA); +@set_time_limit(60*60); // 1 hour should be enough +raise_memory_limit(MEMORY_HUGE); require_login(); admin_externalpage_setup('uploadusers'); require_capability('moodle/site:uploadusers', get_context_instance(CONTEXT_SYSTEM)); -$textlib = textlib_get_instance(); -$systemcontext = get_context_instance(CONTEXT_SYSTEM); - $struserrenamed = get_string('userrenamed', 'admin'); $strusernotrenamedexists = get_string('usernotrenamedexists', 'error'); $strusernotrenamedmissing = get_string('usernotrenamedmissing', 'error'); @@ -46,6 +52,8 @@ $strusernotupdated = get_string('usernotupdatederror', 'error'); $strusernotupdatednotexists = get_string('usernotupdatednotexists', 'error'); $strusernotupdatedadmin = get_string('usernotupdatedadmin', 'error'); +$struseruptodate = get_string('useraccountuptodate', 'admin'); + $struseradded = get_string('newuser'); $strusernotadded = get_string('usernotaddedregistered', 'error'); $strusernotaddederror = get_string('usernotaddederror', 'error'); @@ -57,7 +65,6 @@ $strusernotdeletedoff = get_string('usernotdeletedoff', 'error'); $strusernotdeletedadmin = get_string('usernotdeletedadmin', 'error'); $strcannotassignrole = get_string('cannotassignrole', 'error'); -$strduplicateusername = get_string('duplicateusername', 'error'); $struserauthunsupported = get_string('userauthunsupported', 'error'); $stremailduplicate = get_string('useremailduplicate', 'error'); @@ -65,20 +72,23 @@ $stremailduplicate = get_string('useremailduplicate', 'error'); $strinvalidpasswordpolicy = get_string('invalidpasswordpolicy', 'error'); $errorstr = get_string('error'); -$returnurl = $CFG->wwwroot.'/'.$CFG->admin.'/uploaduser.php'; -$bulknurl = $CFG->wwwroot.'/'.$CFG->admin.'/user/user_bulk.php'; +$returnurl = new moodle_url('/admin/uploaduser.php'); +$bulknurl = new moodle_url('/admin/user/user_bulk.php'); $today = time(); $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0); // array of all valid fields for validation $STD_FIELDS = array('id', 'firstname', 'lastname', 'username', 'email', - 'city', 'country', 'lang', 'auth', 'timezone', 'mailformat', + 'city', 'country', 'lang', 'timezone', 'mailformat', 'maildisplay', 'maildigest', 'htmleditor', 'ajax', 'autosubscribe', - 'mnethostid', 'institution', 'department', 'idnumber', 'skype', + 'institution', 'department', 'idnumber', 'skype', 'msn', 'aim', 'yahoo', 'icq', 'phone1', 'phone2', 'address', - 'url', 'description', 'descriptionformat', 'oldusername', 'deleted', - 'password'); + 'url', 'description', 'descriptionformat', 'password', + 'auth', // watch out when changing auth type or using external auth plugins! + 'oldusername', // use when renaming users - this is the original username + 'deleted', // 1 means delete user + ); $PRF_FIELDS = array(); @@ -86,27 +96,28 @@ if ($prof_fields = $DB->get_records('user_info_field')) { foreach ($prof_fields as $prof_field) { $PRF_FIELDS[] = 'profile_field_'.$prof_field->shortname; } - unset($prof_fields); } +unset($prof_fields); if (empty($iid)) { - $mform = new admin_uploaduser_form1(); + $mform1 = new admin_uploaduser_form1(); - if ($formdata = $mform->get_data()) { + if ($formdata = $mform1->get_data()) { $iid = csv_import_reader::get_new_iid('uploaduser'); $cir = new csv_import_reader($iid, 'uploaduser'); - $content = $mform->get_file_content('userfile'); - $optype = $formdata->uutype; - $readcount = $cir->load_csv_content($content, $formdata->encoding, $formdata->delimiter_name, 'validate_user_upload_columns'); + $content = $mform1->get_file_content('userfile'); + + $readcount = $cir->load_csv_content($content, $formdata->encoding, $formdata->delimiter_name); unset($content); if ($readcount === false) { - //TODO: need more detailed error info print_error('csvloaderror', '', $returnurl); } else if ($readcount == 0) { print_error('csvemptyfile', 'error', $returnurl); } + // test if columns ok + $filecolumns = uu_validate_user_upload_columns($cir, $STD_FIELDS, $PRF_FIELDS, $returnurl); // continue to form2 } else { @@ -114,61 +125,56 @@ if (empty($iid)) { echo $OUTPUT->heading_with_help(get_string('uploadusers', 'admin'), 'uploadusers', 'admin'); - $mform->display(); + $mform1->display(); echo $OUTPUT->footer(); die; } } else { $cir = new csv_import_reader($iid, 'uploaduser'); + $filecolumns = uu_validate_user_upload_columns($cir, $STD_FIELDS, $PRF_FIELDS, $returnurl); } -if (!$columns = $cir->get_columns()) { - print_error('cannotreadtmpfile', 'error', $returnurl); -} -$mform = new admin_uploaduser_form2(null, $columns); -// get initial date from form1 -$mform->set_data(array('iid'=>$iid, 'previewrows'=>$previewrows, 'readcount'=>$readcount, 'uutypelabel'=>$choices[$uploadtype], 'uutype'=>$uploadtype)); +$mform2 = new admin_uploaduser_form2(null, array('columns'=>$filecolumns, 'data'=>array('iid'=>$iid, 'previewrows'=>$previewrows))); // If a file has been uploaded, then process it -if ($formdata = $mform->is_cancelled()) { +if ($formdata = $mform2->is_cancelled()) { $cir->cleanup(true); redirect($returnurl); -} else if ($formdata = $mform->get_data()) { +} else if ($formdata = $mform2->get_data()) { // Print the header echo $OUTPUT->header(); echo $OUTPUT->heading(get_string('uploadusersresult', 'admin')); $optype = $formdata->uutype; - $createpasswords = (!empty($formdata->uupasswordnew) and $optype != UU_UPDATE); - $updatepasswords = (!empty($formdata->uupasswordold) and $optype != UU_ADDNEW and $optype != UU_ADDINC); - $allowrenames = (!empty($formdata->uuallowrenames) and $optype != UU_ADDNEW and $optype != UU_ADDINC); - $allowdeletes = (!empty($formdata->uuallowdeletes) and $optype != UU_ADDNEW and $optype != UU_ADDINC); $updatetype = isset($formdata->uuupdatetype) ? $formdata->uuupdatetype : 0; + $createpasswords = (!empty($formdata->uupasswordnew) and $optype != UU_USER_UPDATE); + $updatepasswords = (!empty($formdata->uupasswordold) and $optype != UU_USER_ADDNEW and $optype != UU_USER_ADDINC and ($updatetype == UU_UPDATE_FILEOVERRIDE or $updatetype == UU_UPDATE_ALLOVERRIDE)); + $allowrenames = (!empty($formdata->uuallowrenames) and $optype != UU_USER_ADDNEW and $optype != UU_USER_ADDINC); + $allowdeletes = (!empty($formdata->uuallowdeletes) and $optype != UU_USER_ADDNEW and $optype != UU_USER_ADDINC); $bulk = $formdata->uubulk; $noemailduplicates = $formdata->uunoemailduplicates; + $standardusernames = $formdata->uustandardusernames; + $resetpasswords = isset($formdata->uuforcepasswordchange) ? $formdata->uuforcepasswordchange : UU_PWRESET_NONE; // verification moved to two places: after upload and into form2 - $usersnew = 0; - $usersupdated = 0; - $userserrors = 0; - $deletes = 0; - $deleteerrors = 0; - $renames = 0; - $renameerrors = 0; - $usersskipped = 0; + $usersnew = 0; + $usersupdated = 0; + $usersuptodate = 0; //not printed yet anywhere + $userserrors = 0; + $deletes = 0; + $deleteerrors = 0; + $renames = 0; + $renameerrors = 0; + $usersskipped = 0; $weakpasswords = 0; // caches - $ccache = array(); // course cache - do not fetch all courses here, we will not probably use them all anyway! - $rolecache = uu_allowed_roles_cache(); // roles lookup cache - $manualcache = array(); // cache of used manual enrol plugins in each course - - $allowedauths = uu_allowed_auths(); - $allowedauths = array_keys($allowedauths); - $availableauths = get_plugin_list('auth'); - $availableauths = array_keys($availableauths); + $ccache = array(); // course cache - do not fetch all courses here, we will not probably use them all anyway! + $rolecache = uu_allowed_roles_cache(); // roles lookup cache + $manualcache = array(); // cache of used manual enrol plugins in each course + $supportedauths = uu_supported_auths(); // officially supported plugins that are enabled // we use only manual enrol plugin here, if it is disabled no enrol is done if (enrol_is_enabled('manual')) { @@ -188,7 +194,7 @@ if ($formdata = $mform->is_cancelled()) { // init upload progress tracker $upt = new uu_progress_tracker(); - $upt->init(); // start table + $upt->start(); // start table while ($line = $cir->next()) { $upt->flush(); @@ -196,37 +202,43 @@ if ($formdata = $mform->is_cancelled()) { $upt->track('line', $linenum); - $forcechangepassword = false; - $user = new stdClass(); - // by default, use the local mnet id (this may be changed in the file) - $user->mnethostid = $CFG->mnet_localhost_id; - // add fields to user object - foreach ($line as $key => $value) { - if ($value !== '') { - $key = $columns[$key]; - $user->$key = $value; - if (in_array($key, $upt->columns)) { - $upt->track($key, $value); - } - } else { - $user->$columns[$key] = ''; - } - } - // get username, first/last name now - we need them in templates!! - if ($optype == UU_UPDATE) { - // when updating only username is required - if (!isset($user->username)) { - $upt->track('status', get_string('missingfield', 'error', 'username'), 'error'); - $upt->track('username', $errorstr, 'error'); - $userserrors++; + // add fields to user object + foreach ($line as $keynum => $value) { + if (!isset($filecolumns[$keynum])) { + // this should not happen continue; } + $key = $filecolumns[$keynum]; + if (strpos($key, 'profile_field_') === 0) { + //NOTE: bloody mega hack alert!! + if (isset($USER->$key) and is_array($USER->$key)) { + // this must be some hacky field that is abusing arrays to store content and format + $user->$key = array(); + $user->$key['text'] = $value; + $user->$key['format'] = FORMAT_MOODLE; + } else { + $user->$key = $value; + } + } else { + $user->$key = $value; + } - } else { + if (in_array($key, $upt->columns)) { + // default value in progress tracking table, can be changed later + $upt->track($key, s($value), 'normal'); + } + } + if (!isset($user->username)) { + // prevent warnings bellow + $user->username = ''; + } + + if ($optype == UU_USER_ADDNEW or $optype == UU_USER_ADDINC) { + // user creation is a special case - the username may be constructed from templates using firstname and lastname + // better never try this in mixed update types $error = false; - // when all other ops need firstname and lastname if (!isset($user->firstname) or $user->firstname === '') { $upt->track('status', get_string('missingfield', 'error', 'firstname'), 'error'); $upt->track('firstname', $errorstr, 'error'); @@ -242,43 +254,50 @@ if ($formdata = $mform->is_cancelled()) { continue; } // we require username too - we might use template for it though - if (!isset($user->username)) { - if (!isset($formdata->username) or $formdata->username === '') { - $upt->track('status', get_string('missingfield', 'error', 'username'), 'error'); - $upt->track('username', $errorstr, 'error'); - $userserrors++; - continue; - } else { - $user->username = process_template($formdata->username, $user); - $upt->track('username', $user->username); - } + if (empty($user->username) and !empty($formdata->username)) { + $user->username = uu_process_template($formdata->username, $user); + $upt->track('username', s($user->username)); } } // normalize username - $user->username = clean_param($user->username, PARAM_USERNAME); + $originalusername = $user->username; + if ($standardusernames) { + $user->username = clean_param($user->username, PARAM_USERNAME); + } + // make sure we really have username if (empty($user->username)) { $upt->track('status', get_string('missingfield', 'error', 'username'), 'error'); $upt->track('username', $errorstr, 'error'); $userserrors++; continue; + } else if ($user->username === 'guest') { + $upt->track('status', get_string('guestnoeditprofileother', 'error'), 'error'); + $userserrors++; + continue; } - if ($existinguser = $DB->get_record('user', array('username'=>$user->username, 'mnethostid'=>$user->mnethostid))) { + if ($existinguser = $DB->get_record('user', array('username'=>$user->username, 'mnethostid'=>$CFG->mnet_localhost_id))) { $upt->track('id', $existinguser->id, 'normal', false); } // find out in username incrementing required - if ($existinguser and $optype == UU_ADDINC) { - $oldusername = $user->username; - $user->username = increment_username($user->username, $user->mnethostid); - $upt->track('username', '', 'normal', false); // clear previous - $upt->track('username', $oldusername.'-->'.$user->username, 'info'); + if ($existinguser and $optype == UU_USER_ADDINC) { + $user->username = uu_increment_username($user->username); $existinguser = false; } + // notify about nay username changes + if ($originalusername !== $user->username) { + $upt->track('username', '', 'normal', false); // clear previous + $upt->track('username', s($originalusername).'-->'.s($user->username), 'info'); + } else { + $upt->track('username', s($user->username), 'normal', false); + } + // add default values for remaining fields + $formdefaults = array(); foreach ($STD_FIELDS as $field) { if (isset($user->$field)) { continue; @@ -286,7 +305,8 @@ if ($formdata = $mform->is_cancelled()) { // all validation moved to form2 if (isset($formdata->$field)) { // process templates - $user->$field = process_template($formdata->$field, $user); + $user->$field = uu_process_template($formdata->$field, $user); + $formdefaults[$field] = true; } } foreach ($PRF_FIELDS as $field) { @@ -295,13 +315,8 @@ if ($formdata = $mform->is_cancelled()) { } if (isset($formdata->$field)) { // process templates - if (is_array($formdata->$field)) { - foreach ($formdata->$field as $k=>$v) { - $user->$field[$k] = process_template($v, $user); - } - } else { - $user->$field = process_template($formdata->$field, $user); - } + $user->$field = uu_process_template($formdata->$field, $user); + $formdefaults[$field] = true; } } @@ -336,7 +351,6 @@ if ($formdata = $mform->is_cancelled()) { // renaming requested? if (!empty($user->oldusername) ) { - $oldusername = $textlib->strtolower($user->oldusername); if (!$allowrenames) { $usersskipped++; $upt->track('status', $strusernotrenamedoff, 'warning'); @@ -349,7 +363,20 @@ if ($formdata = $mform->is_cancelled()) { continue; } - if ($olduser = $DB->get_record('user', array('username'=>$oldusername, 'mnethostid'=>$user->mnethostid))) { + if ($user->username === 'guest') { + $upt->track('status', get_string('guestnoeditprofileother', 'error'), 'error'); + $renameerrors++; + continue; + } + + if ($standardusernames) { + $oldusername = clean_param($user->oldusername, PARAM_USERNAME); + } else { + $oldusername = $user->oldusername; + } + + // no guessing when looking for old username, it must be exact match + if ($olduser = $DB->get_record('user', array('username'=>$oldusername, 'mnethostid'=>$CFG->mnet_localhost_id))) { $upt->track('id', $olduser->id, 'normal', false); if (is_siteadmin($olduser->id)) { $upt->track('status', $strusernotrenamedadmin, 'error'); @@ -358,7 +385,7 @@ if ($formdata = $mform->is_cancelled()) { } $DB->set_field('user', 'username', $user->username, array('id'=>$olduser->id)); $upt->track('username', '', 'normal', false); // clear previous - $upt->track('username', $oldusername.'-->'.$user->username, 'info'); + $upt->track('username', s($oldusername).'-->'.s($user->username), 'info'); $upt->track('status', $struserrenamed); $renames++; } else { @@ -373,7 +400,7 @@ if ($formdata = $mform->is_cancelled()) { // can we process with update or insert? $skip = false; switch ($optype) { - case UU_ADDNEW: + case UU_USER_ADDNEW: if ($existinguser) { $usersskipped++; $upt->track('status', $strusernotadded, 'warning'); @@ -381,25 +408,29 @@ if ($formdata = $mform->is_cancelled()) { } break; - case UU_ADDINC: + case UU_USER_ADDINC: if ($existinguser) { //this should not happen! $upt->track('status', $strusernotaddederror, 'error'); $userserrors++; - continue; + $skip = true; } break; - case UU_ADD_UPDATE: + case UU_USER_ADD_UPDATE: break; - case UU_UPDATE: + case UU_USER_UPDATE: if (!$existinguser) { $usersskipped++; $upt->track('status', $strusernotupdatednotexists, 'warning'); $skip = true; } break; + + default: + // unknown type + $skip = true; } if ($skip) { @@ -409,201 +440,179 @@ if ($formdata = $mform->is_cancelled()) { if ($existinguser) { $user->id = $existinguser->id; + $upt->track('username', html_writer::link(new moodle_url('/user/profile.php', array('id'=>$existinguser->id)), s($existinguser->username)), 'normal', false); + if (is_siteadmin($user->id)) { $upt->track('status', $strusernotupdatedadmin, 'error'); $userserrors++; continue; } - if (!$updatetype) { - // no updates of existing data at all - } else { - $existinguser->timemodified = time(); - if (empty($existinguser->timecreated)) { - if (empty($existinguser->firstaccess)) { - $existinguser->timecreated = time(); - } else { - $existinguser->timecreated = $existinguser->firstaccess; + $existinguser->timemodified = time(); + // do NOT mess with timecreated or firstaccess here! + + //load existing profile data + profile_load_data($existinguser); + + $upt->track('auth', $existinguser->auth, 'normal', false); + + $doupdate = false; + + if ($updatetype != UU_UPDATE_NOCHANGES) { + if (!empty($user->auth) and $user->auth !== $existinguser->auth) { + $upt->track('auth', s($existinguser->auth).'-->'.s($user->auth), 'info', false); + $existinguser->auth = $user->auth; + if (!isset($supportedauths[$user->auth])) { + $upt->track('auth', $struserauthunsupported, 'warning'); } } - - //load existing profile data - profile_load_data($existinguser); - - $allowed = array(); - if ($updatetype == 1) { - $allowed = $columns; - } else if ($updatetype == 2 or $updatetype == 3) { - $allowed = array_merge($STD_FIELDS, $PRF_FIELDS); - } - foreach ($allowed as $column) { - $temppasswordhandler = ''; - if ($column == 'username') { + $allcolumns = array_merge($STD_FIELDS, $PRF_FIELDS); + foreach ($allcolumns as $column) { + if ($column === 'username' or $column === 'password' or $column === 'auth') { + // these can not be changed here continue; } - if ((property_exists($existinguser, $column) and property_exists($user, $column)) or in_array($column, $PRF_FIELDS)) { - if ($updatetype == 3 && $existinguser->$column !== '') { - //missing == non-empty only + if (!property_exists($user, $column) or !property_exists($existinguser, $column)) { + // this should never happen + continue; + } + if ($updatetype == UU_UPDATE_MISSING) { + if (!is_null($existinguser->$column) and $existinguser->$column !== '') { continue; } - if ($existinguser->$column !== $user->$column) { - if ($column == 'email') { - if ($DB->record_exists('user', array('email'=>$user->email))) { - if ($noemailduplicates) { - $upt->track('email', $stremailduplicate, 'error'); - $upt->track('status', $strusernotupdated, 'error'); - $userserrors++; - continue 2; - } else { - $upt->track('email', $stremailduplicate, 'warning'); - } - } - } + } else if ($updatetype == UU_UPDATE_ALLOVERRIDE) { + // we override everything - if ($column == 'password') { - $temppasswordhandler = $existinguser->password; - } - - if ($column == 'auth') { - if (isset($user->auth) && empty($user->auth)) { - $user->auth = 'manual'; - } - - $existinguserauth = get_auth_plugin($existinguser->auth); - $existingisinternalauth = $existinguserauth->is_internal(); - - $userauth = get_auth_plugin($user->auth); - $isinternalauth = $userauth->is_internal(); - - if ($isinternalauth === $existingisinternalauth) { - if ($updatepasswords) { - if (empty($user->password)) { - $forcechangepassword = true; - } - } - } else if ($isinternalauth) { - $existinguser->password = ''; - $forcechangepassword = true; - } - } - - $upt->track($column, '', 'normal', false); // clear previous - if ($column != 'password' && in_array($column, $upt->columns)) { - $upt->track($column, $existinguser->$column.'-->'.$user->$column, 'info'); - } - $existinguser->$column = $user->$column; - - if (!isset($user->auth) && !$updatepasswords) { - $existinguser->password = $temppasswordhandler; - } + } else if ($updatetype == UU_UPDATE_FILEOVERRIDE) { + if (!empty($formdefaults[$column])) { + // do not override with form defaults + continue; } } - } + if ($existinguser->$column !== $user->$column) { + if ($column === 'email') { + if ($DB->record_exists('user', array('email'=>$user->email))) { + if ($noemailduplicates) { + $upt->track('email', $stremailduplicate, 'error'); + $upt->track('status', $strusernotupdated, 'error'); + $userserrors++; + continue 2; + } else { + $upt->track('email', $stremailduplicate, 'warning'); + } + } + if (!validate_email($user->email)) { + $upt->track('email', get_string('invalidemail'), 'warning'); + } + } - // do not update record if new auth plugin does not exist! - if (!in_array($existinguser->auth, $availableauths)) { - $upt->track('auth', get_string('userautherror', 'error', $existinguser->auth), 'error'); - $upt->track('status', $strusernotupdated, 'error'); - $userserrors++; - continue; - } else if (!in_array($existinguser->auth, $allowedauths)) { - $upt->track('auth', $struserauthunsupported, 'warning'); + if (in_array($column, $upt->columns)) { + $upt->track($column, s($existinguser->$column).'-->'.s($user->$column), 'info', false); + } + $existinguser->$column = $user->$column; + $doupdate = true; + } } + } + try { $auth = get_auth_plugin($existinguser->auth); - $isinternalauth = $auth->is_internal(); + } catch (Exception $e) { + $upt->track('auth', get_string('userautherror', 'error', s($existinguser->auth)), 'error'); + $upt->track('status', $strusernotupdated, 'error'); + $userserrors++; + continue; + } + $isinternalauth = $auth->is_internal(); - if ($isinternalauth && $updatepasswords && !check_password_policy($user->password, $errmsg)) { - $upt->track('password', get_string('internalauthpassworderror', 'error', $existinguser->password), 'error'); - $upt->track('status', $strusernotupdated, 'error'); - $userserrors++; - continue; - } else { - $forcechangepassword = true; - } + // changing of passwords is a special case + // do not force password changes for external auth plugins! + $oldpw = $existinguser->password; + if (!$isinternalauth) { + $existinguser->password = 'not cached'; + $upt->track('password', '-', 'normal', false); + // clean up prefs + unset_user_preference('create_password', $existinguser); + unset_user_preference('auth_forcepasswordchange', $existinguser); - if (!$isinternalauth) { - $existinguser->password = 'not cached'; - $upt->track('password', 'not cached'); - $forcechangepassword = false; - } else if ($updatepasswords){ - $existinguser->password = hash_internal_user_password($existinguser->password); + } else if (!empty($user->password)) { + if ($updatepasswords) { + $errmsg = null; + $weak = check_password_policy($user->password, $errmsg); + if ($resetpasswords == UU_PWRESET_ALL or ($resetpasswords == UU_PWRESET_WEAK and $weak)) { + if ($weak) { + $weakpasswords++; + $upt->track('password', $strinvalidpasswordpolicy, 'warning'); + } + set_user_preference('auth_forcepasswordchange', 1, $existinguser); + } else { + unset_user_preference('auth_forcepasswordchange', $existinguser); + } + unset_user_preference('create_password', $existinguser); // no need to create password any more + $existinguser->password = hash_internal_user_password($user->password); } else { - $existinguser->password = $temppasswordhandler; + // do not print password when not changed + $upt->track('password', '', 'normal', false); } + } + + if ($doupdate or $existinguser->password !== $oldpw) { + // we want only users that were really updated $DB->update_record('user', $existinguser); - //remove user preference - - if (get_user_preferences('create_password', false, $existinguser)) { - unset_user_preference('create_password', $existinguser); - } - if (get_user_preferences('auth_forcepasswordchange', false, $existinguser)) { - unset_user_preference('auth_forcepasswordchange', $existinguser); - } - - if ($isinternalauth && $updatepasswords) { - if (empty($existinguser->password)) { - set_user_preference('create_password', 1, $existinguser->id); - set_user_preference('auth_forcepasswordchange', 1, $existinguser->id); - $upt->track('password', get_string('new')); - } else if ($forcechangepassword) { - set_user_preference('auth_forcepasswordchange', 1, $existinguser->id); - } - } $upt->track('status', $struserupdated); $usersupdated++; // save custom profile fields data from csv file profile_save_data($existinguser); events_trigger('user_updated', $existinguser); - } - if ($bulk == 2 or $bulk == 3) { - if (!in_array($user->id, $SESSION->bulk_users)) { - $SESSION->bulk_users[] = $user->id; + if ($bulk == UU_BULK_UPDATED or $bulk == UU_BULK_ALL) { + if (!in_array($user->id, $SESSION->bulk_users)) { + $SESSION->bulk_users[] = $user->id; + } + } + + } else { + // no user information changed + $upt->track('status', $struseruptodate); + $usersuptodate++; + + if ($bulk == UU_BULK_ALL) { + if (!in_array($user->id, $SESSION->bulk_users)) { + $SESSION->bulk_users[] = $user->id; + } } } } else { - // save the user to the database - $user->confirmed = 1; + // save the new user to the database + $user->confirmed = 1; $user->timemodified = time(); - $user->timecreated = time(); + $user->timecreated = time(); + $user->mnethostid = $CFG->mnet_localhost_id; // we support ONLY local accounts here, sorry - if (isset($user->auth) && empty($user->auth)) { + if (empty($user->auth)) { $user->auth = 'manual'; } - $auth = get_auth_plugin($user->auth); + $upt->track('auth', $user->auth, 'normal', false); + + // do not insert record if new auth plugin does not exist! + try { + $auth = get_auth_plugin($user->auth); + } catch (Exception $e) { + $upt->track('auth', get_string('userautherror', 'error', s($user->auth)), 'error'); + $upt->track('status', $strusernotaddederror, 'error'); + $userserrors++; + continue; + } + if (!isset($supportedauths[$user->auth])) { + $upt->track('auth', $struserauthunsupported, 'warning'); + } + $isinternalauth = $auth->is_internal(); - if (!$createpasswords && $isinternalauth) { - if (empty($user->password)) { - $upt->track('password', get_string('missingfield', 'error', 'password'), 'error'); - $upt->track('status', $strusernotaddederror, 'error'); - $userserrors++; - continue; - } else if ($forcechangepassword) { - $upt->track('password', $strinvalidpasswordpolicy); - $upt->track('status', $strusernotaddederror, 'error'); - $userserrors++; - continue; - } - } - - // do not insert record if new auth plguin does not exist! - if (isset($user->auth)) { - if (!in_array($user->auth, $availableauths)) { - $upt->track('auth', get_string('userautherror', 'error', $user->auth), 'error'); - $upt->track('status', $strusernotaddederror, 'error'); - $userserrors++; - continue; - } else if (!in_array($user->auth, $allowedauths)) { - $upt->track('auth', $struserauthunsupported, 'warning'); - } - } - if ($DB->record_exists('user', array('email'=>$user->email))) { if ($noemailduplicates) { $upt->track('email', $stremailduplicate, 'error'); @@ -614,38 +623,66 @@ if ($formdata = $mform->is_cancelled()) { $upt->track('email', $stremailduplicate, 'warning'); } } - if (!$isinternalauth) { - $user->password = 'not cached'; - $upt->track('password', 'not cached'); - } else { - $user->password = hash_internal_user_password($user->password); + if (!validate_email($user->email)) { + $upt->track('email', get_string('invalidemail'), 'warning'); } - $user->id = $DB->insert_record('user', $user); - $info = ': ' . $user->username .' (ID = ' . $user->id . ')'; - $upt->track('status', $struseradded); - $upt->track('id', $user->id, 'normal', false); - $usersnew++; - if ($createpasswords && $isinternalauth) { - if (empty($user->password) || $forcechangepassword) { - // passwords will be created and sent out on cron - set_user_preference('create_password', 1, $user->id); - set_user_preference('auth_forcepasswordchange', 1, $user->id); - $upt->track('password', get_string('new')); + $forcechangepassword = false; + + if ($isinternalauth) { + if (empty($user->password)) { + if ($createpasswords) { + $user->password = 'to be generated'; + $upt->track('password', '', 'normal', false); + $upt->track('password', get_string('uupasswordcron', 'admin'), 'warning', false); + } else { + $upt->track('password', '', 'normal', false); + $upt->track('password', get_string('missingfield', 'error', 'password'), 'error'); + $upt->track('status', $strusernotaddederror, 'error'); + $userserrors++; + continue; + } } else { - set_user_preference('auth_forcepasswordchange', 1, $user->id); + $errmsg = null; + $weak = check_password_policy($user->password, $errmsg); + if ($resetpasswords == UU_PWRESET_ALL or ($resetpasswords == UU_PWRESET_WEAK and $weak)) { + if ($weak) { + $weakpasswords++; + $upt->track('password', $strinvalidpasswordpolicy, 'warning'); + } + $forcechangepassword = true; + } + $user->password = hash_internal_user_password($user->password); } + } else { + $user->password = 'not cached'; + $upt->track('password', '-', 'normal', false); } + // create user - insert_record ignores any extra properties + $user->id = $DB->insert_record('user', $user); + $upt->track('username', html_writer::link(new moodle_url('/user/profile.php', array('id'=>$user->id)), s($user->username)), 'normal', false); + // save custom profile fields data profile_save_data($user); + if ($forcechangepassword) { + set_user_preference('auth_forcepasswordchange', 1, $user); + } + if ($user->password === 'to be generated') { + set_user_preference('create_password', 1, $user); + } + + $upt->track('status', $struseradded); + $upt->track('id', $user->id, 'normal', false); + $usersnew++; + // make sure user context exists get_context_instance(CONTEXT_USER, $user->id); events_trigger('user_created', $user); - if ($bulk == 1 or $bulk == 3) { + if ($bulk == UU_BULK_NEW or $bulk == UU_BULK_ALL) { if (!in_array($user->id, $SESSION->bulk_users)) { $SESSION->bulk_users[] = $user->id; } @@ -653,7 +690,8 @@ if ($formdata = $mform->is_cancelled()) { } // find course enrolments, groups, roles/types and enrol periods - foreach ($columns as $column) { + // this is again a special case, we always do this for any updated or created users + foreach ($filecolumns as $column) { if (!preg_match('/^course\d+$/', $column)) { continue; } @@ -665,7 +703,7 @@ if ($formdata = $mform->is_cancelled()) { $shortname = $user->{'course'.$i}; if (!array_key_exists($shortname, $ccache)) { if (!$course = $DB->get_record('course', array('shortname'=>$shortname), 'id, shortname')) { - $upt->track('enrolments', get_string('unknowncourse', 'error', $shortname), 'error'); + $upt->track('enrolments', get_string('unknowncourse', 'error', s($shortname)), 'error'); continue; } $ccache[$shortname] = $course; @@ -696,7 +734,7 @@ if ($formdata = $mform->is_cancelled()) { if (array_key_exists($addrole, $rolecache)) { $rid = $rolecache[$addrole]->id; } else { - $upt->track('enrolments', get_string('unknownrole', 'error', $addrole), 'error'); + $upt->track('enrolments', get_string('unknownrole', 'error', s($addrole)), 'error'); continue; } @@ -720,9 +758,9 @@ if ($formdata = $mform->is_cancelled()) { // find duration $timeend = 0; if (!empty($user->{'enrolperiod'.$i})) { - $duration = (int)$user->{'enrolperiod'.$i} * 86400; // convert days to seconds + $duration = (int)$user->{'enrolperiod'.$i} * 60*60*24; // convert days to seconds if ($duration > 0) { // sanity check - $timeend = $today + $duration; + $timeend = $today + $duration; } } @@ -751,9 +789,9 @@ if ($formdata = $mform->is_cancelled()) { $ccache[$shortname]->groups[$gid]->id = $gid; $ccache[$shortname]->groups[$gid]->name = $group->name; if (!is_numeric($group->name)) { // only non-numeric names are supported!!! - $ccache[$shortname]->groups[$group->name] = new stdClass(); - $ccache[$shortname]->groups[$group->name]->id = $gid; - $ccache[$shortname]->groups[$group->name]->name = $group->name; + $ccache[$shortname]->groups[$group->name] = new stdClass(); + $ccache[$shortname]->groups[$group->name]->id = $gid; + $ccache[$shortname]->groups[$group->name]->name = $group->name; } } } @@ -768,7 +806,7 @@ if ($formdata = $mform->is_cancelled()) { if ($ccache[$shortname]->groups[$addgroup]->id = groups_create_group($newgroupdata)){ $ccache[$shortname]->groups[$addgroup]->name = $newgroupdata->name; } else { - $upt->track('enrolments', get_string('unknowngroup', 'error', $addgroup), 'error'); + $upt->track('enrolments', get_string('unknowngroup', 'error', s($addgroup)), 'error'); continue; } } @@ -777,18 +815,17 @@ if ($formdata = $mform->is_cancelled()) { try { if (groups_add_member($gid, $user->id)) { - $upt->track('enrolments', get_string('addedtogroup', '', $gname)); + $upt->track('enrolments', get_string('addedtogroup', '', s($gname))); } else { - $upt->track('enrolments', get_string('addedtogroupnot', '', $gname), 'error'); + $upt->track('enrolments', get_string('addedtogroupnot', '', s($gname)), 'error'); } } catch (moodle_exception $e) { - $upt->track('enrolments', get_string('addedtogroupnot', '', $gname), 'error'); + $upt->track('enrolments', get_string('addedtogroupnot', '', s($gname)), 'error'); continue; } } } } - $upt->flush(); $upt->close(); // close table $cir->close(); @@ -796,10 +833,10 @@ if ($formdata = $mform->is_cancelled()) { echo $OUTPUT->box_start('boxwidthnarrow boxaligncenter generalbox', 'uploadresults'); echo '

'; - if ($optype != UU_UPDATE) { + if ($optype != UU_USER_UPDATE) { echo get_string('userscreated', 'admin').': '.$usersnew.'
'; } - if ($optype == UU_UPDATE or $optype == UU_ADD_UPDATE) { + if ($optype == UU_USER_UPDATE or $optype == UU_USER_ADD_UPDATE) { echo get_string('usersupdated', 'admin').': '.$usersupdated.'
'; } if ($allowdeletes) { @@ -829,451 +866,70 @@ if ($formdata = $mform->is_cancelled()) { // Print the header echo $OUTPUT->header(); -/// Print the form - echo $OUTPUT->heading(get_string('uploaduserspreview', 'admin')); +// NOTE: this is JUST csv processing preview, we must not prevent import from here if there is something in the file!! +// this was intended for validation of csv formatting and encoding, not filtering the data!!!! +// we definitely must not process the whole file! + +// preview table data +$data = array(); $cir->init(); -$availableauths = get_plugin_list('auth'); -$availableauths = array_keys($availableauths); -$contents = array(); -while ($fields = $cir->next()) { - $errormsg = array(); +$linenum = 1; //column header is first line +while ($linenum <= $previewrows and $fields = $cir->next()) { + $linenum++; $rowcols = array(); - foreach($fields as $key =>$field) { - $rowcols[$columns[$key]] = $field; + $rowcols['line'] = $linenum; + foreach($fields as $key => $field) { + $rowcols[$filecolumns[$key]] = s($field); } + $rowcols['status'] = array(); - $usernameexist = $DB->record_exists('user', array('username'=>$rowcols['username'])); - $emailexist = $DB->record_exists('user', array('email'=>$rowcols['email'])); - $cleanusername = clean_param($rowcols['username'], PARAM_USERNAME); - $validusername = strcmp($rowcols['username'], $cleanusername); - $validemail = validate_email($rowcols['email']); - - if ($validusername != 0 || !$validemail) { - if ($validusername != 0) { - $errormsg['username'] = get_string('invalidusernameupload'); + if (isset($rowcols['username'])) { + $stdusername = clean_param($rowcols['username'], PARAM_USERNAME); + if ($rowcols['username'] !== $stdusername) { + $rowcols['status'][] = get_string('invalidusernameupload'); } - if (!$validemail) { - $errormsg['email'] = get_string('invalidemail'); + if ($userid = $DB->get_field('user', 'id', array('username'=>$stdusername, 'mnethostid'=>$CFG->mnet_localhost_id))) { + $rowcols['username'] = html_writer::link(new moodle_url('/user/profile.php', array('id'=>$userid)), $rowcols['username']); } + } else { + $rowcols['status'][] = get_string('missingusername'); } - //check password column - if (array_key_exists('auth', $rowcols)) { - if (isset($rowcols['auth']) && empty($rowcols['auth'])) { - $rowcols['auth'] = 'manual'; - } - $rowauth = get_auth_plugin($rowcols['auth']); - $rowisinternalauth = $rowauth->is_internal(); - if (!$rowisinternalauth) { - if (array_key_exists('password', $rowcols) && !empty($rowcols['password'])) { - $errormsg['password'] = get_string('externalauthpassworderror', 'error'); - } - } - - if (!in_array($rowcols['auth'], $availableauths)) { - $errormsg['auth'] = get_string('userautherror', 'error'); - } + if (!validate_email($rowcols['email'])) { + $rowcols['status'][] = get_string('invalidemail'); } - - if (empty($optype) ) { - $optype = $uploadtype; - } - - switch($optype) { - case UU_ADDNEW: - if ($usernameexist || $emailexist ) { - $rowcols['action'] = 'skipped'; - } else { - $rowcols['action'] = 'create'; - } - break; - - case UU_ADDINC: - if (!$usernameexist && !$emailexist) { - $rowcols['action'] = 'create'; - } else if ($usernameexist && !$emailexist) { - $rowcols['action'] = 'addcountertousername'; - $rowcols['username'] = increment_username($rowcols['username'], $CFG->mnet_localhost_id); - } else { - $rowcols['action'] = 'skipped'; - } - break; - - case UU_ADD_UPDATE: - $oldusernameexist = ''; - if (isset($rowcols['oldusername'])) { - $oldusernameexist = $DB->record_exists('user', array('username'=>$rowcols['oldusername'])); - } - if ($usernameexist || $emailexist || $oldusernameexist ) { - $rowcols['action'] = 'update'; - } else { - $rowcols['action'] = 'create'; - } - break; - - case UU_UPDATE: - $oldusernameexist = ''; - if (isset($rowcols['oldusername'])) { - $oldusernameexist = $DB->record_exists('user', array('username'=>$rowcols['oldusername'])); - } - - if ($usernameexist || $emailexist || !empty($oldusernameexist)) { - $rowcols['action'] = 'update'; - } else { - $rowcols['action'] = "skipped"; - } - break; - } - - if (!empty($errormsg)){ - $rowcols['error'] = array(); - $rowcols['error'] = $errormsg; - } - if ($rowcols['action'] != 'skipped') { - $contents[] = $rowcols; + if ($DB->record_exists('user', array('email'=>$rowcols['email']))) { + $rowcols['status'][] = $stremailduplicate; } + $rowcols['status'] = implode('
', $rowcols['status']); + $data[] = $rowcols; +} +if ($fields = $cir->next()) { + $data[] = array_fill(0, count($fields) + 2, '...'); } $cir->close(); -//get heading -$headings = array(); -foreach ($contents as $content) { - foreach($content as $key => $value) { - if (!in_array($key, $headings)) { - $headings[] = $key; - } - } -} - $table = new html_table(); $table->id = "uupreview"; $table->attributes['class'] = 'generaltable'; $table->tablealign = 'center'; $table->summary = get_string('uploaduserspreview', 'admin'); $table->head = array(); -$table->data = array(); +$table->data = $data; -//print heading -foreach ($headings as $heading) { - $table->head[] = s($heading); +$table->head[] = get_string('uucsvline', 'admin'); +foreach ($filecolumns as $column) { + $table->head[] = $column; } +$table->head[] = get_string('status'); -$haserror = false; -$countcontent = 0; -if (in_array('error', $headings)) { - //print error - $haserror = true; - - foreach ($contents as $content) { - if (array_key_exists('error', $content)) { - $rows = new html_table_row(); - foreach ($content as $key => $value) { - $cells = new html_table_cell(); - $errclass = ''; - if (array_key_exists($key, $content['error'])) { - $errclass = 'uuerror'; - } - if ($key == 'error') { - $value = join('
', $content['error']); - } - if ($key == 'action') { - $value = get_string($content[$key]); - } - $cells->text = $value; - $cells->attributes['class'] = $errclass; - $rows->cells[] = $cells; - } - $countcontent++; - $table->data[] = $rows; - } - } - $mform = new admin_uploaduser_form3(); - $mform->set_data(array('uutype'=>$uploadtype)); -} else if (empty($contents)) { - $mform = new admin_uploaduser_form3(); - $mform->set_data(array('uutype'=>$uploadtype)); -} else { - //print content - foreach ($contents as $content) { - $rows = new html_table_row(); - if ($countcontent >= $previewrows) { - foreach ($content as $con) { - $cells = new html_table_cell(); - $cells->text = '...'; - } - $rows->cells[] = $cells; - $table->data[] = $rows; - break; - } - foreach ($headings as $heading) { - $cells = new html_table_cell(); - if(array_key_exists($heading, $content)) { - if ($heading == 'action') { - $content[$heading] = get_string($content[$heading]); - } - $cells->text = $content[$heading]; - } else { - $cells->text = ''; - } - $rows->cells[] = $cells; - } - $table->data[] = $rows; - $countcontent++; - } -} echo html_writer::tag('div', html_writer::table($table), array('class'=>'flexible-wrap')); -if ($haserror) { - echo $OUTPUT->container(get_string('useruploadtype', 'moodle', $choices[$uploadtype]), 'centerpara'); - echo $OUTPUT->container(get_string('uploadinvalidpreprocessedcount', 'moodle', $countcontent), 'centerpara'); - echo $OUTPUT->container(get_string('invalidusername', 'moodle'), 'centerpara'); - echo $OUTPUT->container(get_string('uploadfilecontainerror', 'moodle'), 'centerpara'); -} else if (empty($contents)) { - echo $OUTPUT->container(get_string('uupreprocessedcount', 'admin', $countcontent), 'centerpara'); - echo $OUTPUT->container(get_string('uploadfilecontentsnovaliddata'), 'centerpara'); -} else { - echo $OUTPUT->container(get_string('uupreprocessedcount', 'admin', $countcontent), 'centerpara'); -} +/// Print the form -$mform->display(); +$mform2->display(); echo $OUTPUT->footer(); die; -///////////////////////////////////// -/// Utility functions and classes /// -///////////////////////////////////// - -class uu_progress_tracker { - var $_row; - var $columns = array('status', 'line', 'id', 'username', 'firstname', 'lastname', 'email', 'password', 'auth', 'enrolments', 'deleted'); - - function uu_progress_tracker() { - } - - function init() { - $ci = 0; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - $this->_row = null; - } - - function flush() { - if (empty($this->_row) or empty($this->_row['line']['normal'])) { - $this->_row = array(); - foreach ($this->columns as $col) { - $this->_row[$col] = array('normal'=>'', 'info'=>'', 'warning'=>'', 'error'=>''); - } - return; - } - $ci = 0; - $ri = 1; - echo ''; - foreach ($this->_row as $key=>$field) { - foreach ($field as $type=>$content) { - if ($field[$type] !== '') { - if ($key == 'username' && $type == 'normal') { - $field[$type] = clean_param($field[$type], PARAM_USERNAME); - } - $field[$type] = ''.$field[$type].''; - } else { - unset($field[$type]); - } - } - echo ''; - } - echo ''; - foreach ($this->columns as $col) { - $this->_row[$col] = array('normal'=>'', 'info'=>'', 'warning'=>'', 'error'=>''); - } - } - - function track($col, $msg, $level='normal', $merge=true) { - if (empty($this->_row)) { - $this->flush(); //init arrays - } - if (!in_array($col, $this->columns)) { - debugging('Incorrect column:'.$col); - return; - } - if ($merge) { - if ($this->_row[$col][$level] != '') { - $this->_row[$col][$level] .='
'; - } - $this->_row[$col][$level] .= s($msg); - } else { - $this->_row[$col][$level] = s($msg); - } - } - - function close() { - echo '
'.get_string('status').''.get_string('uucsvline', 'admin').'ID'.get_string('username').''.get_string('firstname').''.get_string('lastname').''.get_string('email').''.get_string('password').''.get_string('authentication').''.get_string('enrolments', 'enrol').''.get_string('delete').'
'; - if (!empty($field)) { - echo implode('
', $field); - } else { - echo ' '; - } - echo '
'; - } -} - -/** - * Validation callback function - verified the column line of csv file. - * Converts column names to lowercase too. - */ -function validate_user_upload_columns(&$columns) { - global $STD_FIELDS, $PRF_FIELDS; - - if (count($columns) < 2) { - return get_string('csvfewcolumns', 'error'); - } - - // test columns - $processed = array(); - foreach ($columns as $key=>$unused) { - $columns[$key] = strtolower($columns[$key]); // no unicode expected here, ignore case - $field = $columns[$key]; - if (!in_array($field, $STD_FIELDS) && !in_array($field, $PRF_FIELDS) &&// if not a standard field and not an enrolment field, then we have an error - !preg_match('/^course\d+$/', $field) && !preg_match('/^group\d+$/', $field) && - !preg_match('/^type\d+$/', $field) && !preg_match('/^role\d+$/', $field) && - !preg_match('/^enrolperiod\d+$/', $field)) { - return get_string('invalidfieldname', 'error', $field); - } - if (in_array($field, $processed)) { - return get_string('csvcolumnduplicates', 'error'); - } - $processed[] = $field; - } - return true; -} - -/** - * Increments username - increments trailing number or adds it if not present. - * Varifies that the new username does not exist yet - * @param string $username - * @return incremented username which does not exist yet - */ -function increment_username($username, $mnethostid) { - global $DB; - - if (!preg_match_all('/(.*?)([0-9]+)$/', $username, $matches)) { - $username = $username.'2'; - } else { - $username = $matches[1][0].($matches[2][0]+1); - } - - if ($DB->record_exists('user', array('username'=>$username, 'mnethostid'=>$mnethostid))) { - return increment_username($username, $mnethostid); - } else { - return $username; - } -} - -/** - * Check if default field contains templates and apply them. - * @param string template - potential tempalte string - * @param object user object- we need username, firstname and lastname - * @return string field value - */ -function process_template($template, $user) { - if (strpos($template, '%') === false) { - return $template; - } - - // very very ugly hack! - global $template_globals; - $template_globals = new stdClass(); - $template_globals->username = isset($user->username) ? $user->username : ''; - $template_globals->firstname = isset($user->firstname) ? $user->firstname : ''; - $template_globals->lastname = isset($user->lastname) ? $user->lastname : ''; - - $result = preg_replace_callback('/(?username; break; - case 'f': $repl = $template_globals->firstname; break; - case 'l': $repl = $template_globals->lastname; break; - } - switch ($block[1]) { - case '+': $repl = $textlib->strtoupper($repl); break; - case '-': $repl = $textlib->strtolower($repl); break; - case '~': $repl = $textlib->strtotitle($repl); break; - } - if (!empty($block[2])) { - $repl = $textlib->substr($repl, 0 , $block[2]); - } - - return $repl; -} - -/** - * Returns list of auth plugins that are enabled and known to work. - */ -function uu_allowed_auths() { - global $CFG; - - // only following plugins are guaranteed to work properly - // TODO: add support for more plugins in 2.0 - $whitelist = array('manual', 'nologin', 'none', 'email'); - $plugins = get_enabled_auth_plugins(); - $choices = array(); - foreach ($plugins as $plugin) { - $choices[$plugin] = get_string('pluginname', "auth_{$plugin}"); - } - - return $choices; -} - -/** - * Returns list of roles that are assignable in courses - */ -function uu_allowed_roles() { - // let's cheat a bit, frontpage is guaranteed to exist and has the same list of roles ;-) - $roles = get_assignable_roles(get_context_instance(CONTEXT_COURSE, SITEID), ROLENAME_ORIGINALANDSHORT); - return array_reverse($roles, true); -} - -function uu_allowed_roles_cache() { - $allowedroles = get_assignable_roles(get_context_instance(CONTEXT_COURSE, SITEID), ROLENAME_SHORT); - foreach ($allowedroles as $rid=>$rname) { - $rolecache[$rid] = new stdClass(); - $rolecache[$rid]->id = $rid; - $rolecache[$rid]->name = $rname; - if (!is_numeric($rname)) { // only non-numeric shortnames are supported!!! - $rolecache[$rname] = new stdClass(); - $rolecache[$rname]->id = $rid; - $rolecache[$rname]->name = $rname; - } - } - return $rolecache; -} diff --git a/admin/uploaduser_form.php b/admin/uploaduser_form.php index 956ecdb6ade..fe5311698ab 100644 --- a/admin/uploaduser_form.php +++ b/admin/uploaduser_form.php @@ -1,16 +1,44 @@ . -if (!defined('MOODLE_INTERNAL')) { - die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page -} +/** + * Bulk user upload forms + * + * @package core + * @subpackage admin + * @copyright 2007 Dan Poltawski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); require_once $CFG->libdir.'/formslib.php'; -class admin_uploaduser_form1 extends moodleform { - function definition (){ - global $CFG, $USER; - $mform =& $this->_form; +/** + * Upload a file CVS file with user information. + * + * @package core + * @subpackage admin + * @copyright 2007 Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class admin_uploaduser_form1 extends moodleform { + function definition () { + $mform = $this->_form; $mform->addElement('header', 'settingsheader', get_string('upload')); @@ -36,69 +64,90 @@ class admin_uploaduser_form1 extends moodleform { $mform->addElement('select', 'previewrows', get_string('rowpreviewnum', 'admin'), $choices); $mform->setType('previewrows', PARAM_INT); - $choices = array(UU_ADDNEW => get_string('uuoptype_addnew', 'admin'), - UU_ADDINC => get_string('uuoptype_addinc', 'admin'), - UU_ADD_UPDATE => get_string('uuoptype_addupdate', 'admin'), - UU_UPDATE => get_string('uuoptype_update', 'admin')); - $mform->addElement('select', 'uutype', get_string('uuoptype', 'admin'), $choices); - $this->add_action_buttons(false, get_string('uploadusers', 'admin')); } } + +/** + * Specify user upload details + * + * @package core + * @subpackage admin + * @copyright 2007 Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class admin_uploaduser_form2 extends moodleform { - function definition (){ + function definition () { global $CFG, $USER; - $mform =& $this->_form; - $columns =& $this->_customdata; + $mform = $this->_form; + $columns = $this->_customdata['columns']; + $data = $this->_customdata['data']; // I am the template user, why should it be the administrator? we have roles now, other ppl may use this script ;-) $templateuser = $USER; // upload settings and file $mform->addElement('header', 'settingsheader', get_string('settings')); - $mform->addElement('static', 'uutypelabel', get_string('uuoptype', 'admin') ); + + $choices = array(UU_USER_ADDNEW => get_string('uuoptype_addnew', 'admin'), + UU_USER_ADDINC => get_string('uuoptype_addinc', 'admin'), + UU_USER_ADD_UPDATE => get_string('uuoptype_addupdate', 'admin'), + UU_USER_UPDATE => get_string('uuoptype_update', 'admin')); + $mform->addElement('select', 'uutype', get_string('uuoptype', 'admin'), $choices); $choices = array(0 => get_string('infilefield', 'auth'), 1 => get_string('createpasswordifneeded', 'auth')); $mform->addElement('select', 'uupasswordnew', get_string('uupasswordnew', 'admin'), $choices); $mform->setDefault('uupasswordnew', 1); - $mform->disabledIf('uupasswordnew', 'uutype', 'eq', UU_UPDATE); + $mform->disabledIf('uupasswordnew', 'uutype', 'eq', UU_USER_UPDATE); - $choices = array(0 => get_string('nochanges', 'admin'), - 1 => get_string('uuupdatefromfile', 'admin'), - 2 => get_string('uuupdateall', 'admin'), - 3 => get_string('uuupdatemissing', 'admin')); + $choices = array(UU_UPDATE_NOCHANGES => get_string('nochanges', 'admin'), + UU_UPDATE_FILEOVERRIDE => get_string('uuupdatefromfile', 'admin'), + UU_UPDATE_ALLOVERRIDE => get_string('uuupdateall', 'admin'), + UU_UPDATE_MISSING => get_string('uuupdatemissing', 'admin')); $mform->addElement('select', 'uuupdatetype', get_string('uuupdatetype', 'admin'), $choices); - $mform->setDefault('uuupdatetype', 0); - $mform->disabledIf('uuupdatetype', 'uutype', 'eq', UU_ADDNEW); - $mform->disabledIf('uuupdatetype', 'uutype', 'eq', UU_ADDINC); + $mform->setDefault('uuupdatetype', UU_UPDATE_NOCHANGES); + $mform->disabledIf('uuupdatetype', 'uutype', 'eq', UU_USER_ADDNEW); + $mform->disabledIf('uuupdatetype', 'uutype', 'eq', UU_USER_ADDINC); $choices = array(0 => get_string('nochanges', 'admin'), 1 => get_string('update')); $mform->addElement('select', 'uupasswordold', get_string('uupasswordold', 'admin'), $choices); $mform->setDefault('uupasswordold', 0); - $mform->disabledIf('uupasswordold', 'uutype', 'eq', UU_ADDNEW); - $mform->disabledIf('uupasswordold', 'uutype', 'eq', UU_ADDINC); + $mform->disabledIf('uupasswordold', 'uutype', 'eq', UU_USER_ADDNEW); + $mform->disabledIf('uupasswordold', 'uutype', 'eq', UU_USER_ADDINC); $mform->disabledIf('uupasswordold', 'uuupdatetype', 'eq', 0); $mform->disabledIf('uupasswordold', 'uuupdatetype', 'eq', 3); + $choices = array(UU_PWRESET_WEAK => get_string('usersweakpassword', 'admin'), + UU_PWRESET_NONE => get_string('none'), + UU_PWRESET_ALL => get_string('all')); + if (empty($CFG->passwordpolicy)) { + unset($choices[UU_PWRESET_WEAK]); + } + $mform->addElement('select', 'uuforcepasswordchange', get_string('forcepasswordchange', 'core'), $choices); + + $mform->addElement('selectyesno', 'uuallowrenames', get_string('allowrenames', 'admin')); $mform->setDefault('uuallowrenames', 0); - $mform->disabledIf('uuallowrenames', 'uutype', 'eq', UU_ADDNEW); - $mform->disabledIf('uuallowrenames', 'uutype', 'eq', UU_ADDINC); + $mform->disabledIf('uuallowrenames', 'uutype', 'eq', UU_USER_ADDNEW); + $mform->disabledIf('uuallowrenames', 'uutype', 'eq', UU_USER_ADDINC); $mform->addElement('selectyesno', 'uuallowdeletes', get_string('allowdeletes', 'admin')); $mform->setDefault('uuallowdeletes', 0); - $mform->disabledIf('uuallowdeletes', 'uutype', 'eq', UU_ADDNEW); - $mform->disabledIf('uuallowdeletes', 'uutype', 'eq', UU_ADDINC); + $mform->disabledIf('uuallowdeletes', 'uutype', 'eq', UU_USER_ADDNEW); + $mform->disabledIf('uuallowdeletes', 'uutype', 'eq', UU_USER_ADDINC); $mform->addElement('selectyesno', 'uunoemailduplicates', get_string('uunoemailduplicates', 'admin')); $mform->setDefault('uunoemailduplicates', 1); - $choices = array(0 => get_string('no'), - 1 => get_string('uubulknew', 'admin'), - 2 => get_string('uubulkupdated', 'admin'), - 3 => get_string('uubulkall', 'admin')); + $mform->addElement('selectyesno', 'uustandardusernames', get_string('uustandardusernames', 'admin')); + $mform->setDefault('uustandardusernames', 1); + + $choices = array(UU_BULK_NONE => get_string('no'), + UU_BULK_NEW => get_string('uubulknew', 'admin'), + UU_BULK_UPDATED => get_string('uubulkupdated', 'admin'), + UU_BULK_ALL => get_string('uubulkall', 'admin')); $mform->addElement('select', 'uubulk', get_string('uubulk', 'admin'), $choices); $mform->setDefault('uubulk', 0); @@ -152,18 +201,22 @@ class admin_uploaduser_form2 extends moodleform { // default values $mform->addElement('header', 'defaultheader', get_string('defaultvalues', 'admin')); - $mform->addElement('text', 'username', get_string('username'), 'size="20"'); + $mform->addElement('text', 'username', get_string('uuusernametemplate', 'admin'), 'size="20"'); $mform->addRule('username', get_string('requiredtemplate', 'admin'), 'required', null, 'client'); + $mform->disabledIf('username', 'uutype', 'eq', UU_USER_ADD_UPDATE); + $mform->disabledIf('username', 'uutype', 'eq', UU_USER_UPDATE); + + $mform->addElement('text', 'email', get_string('email'), 'maxlength="100" size="30"'); + $mform->disabledIf('email', 'uutype', 'eq', UU_USER_ADD_UPDATE); + $mform->disabledIf('email', 'uutype', 'eq', UU_USER_UPDATE); // only enabled and known to work plugins - $choices = uu_allowed_auths(); + $choices = uu_supported_auths(); $mform->addElement('select', 'auth', get_string('chooseauthmethod','auth'), $choices); $mform->setDefault('auth', 'manual'); // manual is a sensible backwards compatible default $mform->addHelpButton('auth', 'chooseauthmethod', 'auth'); $mform->setAdvanced('auth'); - $mform->addElement('text', 'email', get_string('email'), 'maxlength="100" size="30"'); - $choices = array(0 => get_string('emaildisplayno'), 1 => get_string('emaildisplayyes'), 2 => get_string('emaildisplaycourse')); $mform->addElement('select', 'maildisplay', get_string('emaildisplay'), $choices); $mform->setDefault('maildisplay', 2); @@ -272,27 +325,30 @@ class admin_uploaduser_form2 extends moodleform { $mform->addElement('hidden', 'previewrows'); $mform->setType('previewrows', PARAM_INT); - $mform->addElement('hidden', 'readcount'); - $mform->setType('readcount', PARAM_INT); - - $mform->addElement('hidden', 'uutype'); - $mform->setType('uutype', PARAM_INT); - $this->add_action_buttons(true, get_string('uploadusers', 'admin')); + + $this->set_data($data); } /** * Form tweaks that depend on current data. */ function definition_after_data() { - $mform =& $this->_form; - $columns =& $this->_customdata; + $mform = $this->_form; + $columns = $this->_customdata['columns']; foreach ($columns as $column) { if ($mform->elementExists($column)) { $mform->removeElement($column); } } + + if (!in_array('password', $columns)) { + // password resetting makes sense only if password specified in csv file + if ($mform->elementExists('uuforcepasswordchange')) { + $mform->removeElement('uuforcepasswordchange'); + } + } } /** @@ -300,19 +356,19 @@ class admin_uploaduser_form2 extends moodleform { */ function validation($data, $files) { $errors = parent::validation($data, $files); - $columns =& $this->_customdata; + $columns = $this->_customdata['columns']; $optype = $data['uutype']; // detect if password column needed in file if (!in_array('password', $columns)) { switch ($optype) { - case UU_UPDATE: + case UU_USER_UPDATE: if (!empty($data['uupasswordold'])) { $errors['uupasswordold'] = get_string('missingfield', 'error', 'password'); } break; - case UU_ADD_UPDATE: + case UU_USER_ADD_UPDATE: if (empty($data['uupasswordnew'])) { $errors['uupasswordnew'] = get_string('missingfield', 'error', 'password'); } @@ -321,12 +377,12 @@ class admin_uploaduser_form2 extends moodleform { } break; - case UU_ADDNEW: + case UU_USER_ADDNEW: if (empty($data['uupasswordnew'])) { $errors['uupasswordnew'] = get_string('missingfield', 'error', 'password'); } break; - case UU_ADDINC: + case UU_USER_ADDINC: if (empty($data['uupasswordnew'])) { $errors['uupasswordnew'] = get_string('missingfield', 'error', 'password'); } @@ -335,7 +391,7 @@ class admin_uploaduser_form2 extends moodleform { } // look for other required data - if ($optype != UU_UPDATE) { + if ($optype != UU_USER_UPDATE) { if (!in_array('firstname', $columns)) { $errors['uutype'] = get_string('missingfield', 'error', 'firstname'); } @@ -369,7 +425,7 @@ class admin_uploaduser_form2 extends moodleform { function get_data() { $data = parent::get_data(); - if ($data !== null) { + if ($data !== null and isset($data->description)) { $data->descriptionformat = $data->description['format']; $data->description = $data->description['text']; } @@ -377,11 +433,3 @@ class admin_uploaduser_form2 extends moodleform { return $data; } } - -class admin_uploaduser_form3 extends moodleform { - function definition (){ - global $CFG, $USER; - $mform =& $this->_form; - $this->add_action_buttons(false, get_string('uploadnewfile')); - } -} diff --git a/admin/uploaduserlib.php b/admin/uploaduserlib.php new file mode 100644 index 00000000000..7df9290268f --- /dev/null +++ b/admin/uploaduserlib.php @@ -0,0 +1,335 @@ +. + +/** + * Bulk user registration functions + * + * @package core + * @subpackage admin + * @copyright 2004 onwards Martin Dougiamas (http://dougiamas.com) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +define('UU_USER_ADDNEW', 0); +define('UU_USER_ADDINC', 1); +define('UU_USER_ADD_UPDATE', 2); +define('UU_USER_UPDATE', 3); + +define('UU_UPDATE_NOCHANGES', 0); +define('UU_UPDATE_FILEOVERRIDE', 1); +define('UU_UPDATE_ALLOVERRIDE', 2); +define('UU_UPDATE_MISSING', 3); + +define('UU_BULK_NONE', 0); +define('UU_BULK_NEW', 1); +define('UU_BULK_UPDATED', 2); +define('UU_BULK_ALL', 3); + +define('UU_PWRESET_NONE', 0); +define('UU_PWRESET_WEAK', 1); +define('UU_PWRESET_ALL', 2); + +/** + * Tracking of processed users. + * + * This class prints user information into a html table. + * + * @package core + * @subpackage admin + * @copyright 2007 Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class uu_progress_tracker { + private $_row; + public $columns = array('status', 'line', 'id', 'username', 'firstname', 'lastname', 'email', 'password', 'auth', 'enrolments', 'deleted'); + + /** + * Print table header. + * @return void + */ + public function start() { + $ci = 0; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + $this->_row = null; + } + + /** + * Flush previous line and start a new one. + * @return void + */ + public function flush() { + if (empty($this->_row) or empty($this->_row['line']['normal'])) { + // Nothing to print - each line has to have at least number + $this->_row = array(); + foreach ($this->columns as $col) { + $this->_row[$col] = array('normal'=>'', 'info'=>'', 'warning'=>'', 'error'=>''); + } + return; + } + $ci = 0; + $ri = 1; + echo ''; + foreach ($this->_row as $key=>$field) { + foreach ($field as $type=>$content) { + if ($field[$type] !== '') { + $field[$type] = ''.$field[$type].''; + } else { + unset($field[$type]); + } + } + echo ''; + } + echo ''; + foreach ($this->columns as $col) { + $this->_row[$col] = array('normal'=>'', 'info'=>'', 'warning'=>'', 'error'=>''); + } + } + + /** + * Add tracking info + * @param string $col name of column + * @param string $msg message + * @param string $level 'normal', 'warning' or 'error' + * @param bool $merge true means add as new line, false means override all previous text of the same type + * @return void + */ + public function track($col, $msg, $level = 'normal', $merge = true) { + if (empty($this->_row)) { + $this->flush(); //init arrays + } + if (!in_array($col, $this->columns)) { + debugging('Incorrect column:'.$col); + return; + } + if ($merge) { + if ($this->_row[$col][$level] != '') { + $this->_row[$col][$level] .='
'; + } + $this->_row[$col][$level] .= $msg; + } else { + $this->_row[$col][$level] = $msg; + } + } + + /** + * Print the table end + * @return void + */ + public function close() { + $this->flush(); + echo '
'.get_string('status').''.get_string('uucsvline', 'admin').'ID'.get_string('username').''.get_string('firstname').''.get_string('lastname').''.get_string('email').''.get_string('password').''.get_string('authentication').''.get_string('enrolments', 'enrol').''.get_string('delete').'
'; + if (!empty($field)) { + echo implode('
', $field); + } else { + echo ' '; + } + echo '
'; + } +} + +/** + * Validation callback function - verified the column line of csv file. + * Converts column names to lowercase too. + * @param csv_import_reader $cir + * @param array standard user fields + * @param array custom profile fields + * @param moodle_url $returnurl return url in case of any error + * @return array list of fields + */ +function uu_validate_user_upload_columns(csv_import_reader $cir, $stdfields, $frofilefields, moodle_url $returnurl) { + $columns = $cir->get_columns(); + + if (empty($columns)) { + $cir->close(); + $cir->cleanup(); + print_error('cannotreadtmpfile', 'error', $returnurl); + } + if (count($columns) < 2) { + $cir->close(); + $cir->cleanup(); + print_error('csvfewcolumns', 'error', $returnurl); + } + + // test columns + $processed = array(); + foreach ($columns as $key=>$unused) { + $field = strtolower($columns[$key]); // no unicode expected here, ignore case + if (!in_array($field, $stdfields) && !in_array($field, $frofilefields) &&// if not a standard field and not an enrolment field, then we have an error + !preg_match('/^course\d+$/', $field) && !preg_match('/^group\d+$/', $field) && + !preg_match('/^type\d+$/', $field) && !preg_match('/^role\d+$/', $field) && + !preg_match('/^enrolperiod\d+$/', $field)) { + print_error('invalidfieldname', 'error', $returnurl, $field); + } + if (in_array($field, $processed)) { + $cir->close(); + $cir->cleanup(); + print_error('duplicatefieldname', 'error', $returnurl, $field); + } + $processed[$key] = $field; + } + + return $processed; +} + +/** + * Increments username - increments trailing number or adds it if not present. + * Varifies that the new username does not exist yet + * @param string $username + * @return incremented username which does not exist yet + */ +function uu_increment_username($username) { + global $DB, $CFG; + + if (!preg_match_all('/(.*?)([0-9]+)$/', $username, $matches)) { + $username = $username.'2'; + } else { + $username = $matches[1][0].($matches[2][0]+1); + } + + if ($DB->record_exists('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id))) { + return uu_increment_username($username); + } else { + return $username; + } +} + +/** + * Check if default field contains templates and apply them. + * @param string template - potential tempalte string + * @param object user object- we need username, firstname and lastname + * @return string field value + */ +function uu_process_template($template, $user) { + if (is_array($template)) { + // hack for for support of text editors with format + $t = $template['text']; + } else { + $t = $template; + } + if (strpos($t, '%') === false) { + return $template; + } + + $username = isset($user->username) ? $user->username : ''; + $firstname = isset($user->firstname) ? $user->firstname : ''; + $lastname = isset($user->lastname) ? $user->lastname : ''; + + $callback = partial('uu_process_template_callback', $username, $firstname, $lastname); + + $result = preg_replace_callback('/(?strtoupper($repl); break; + case '-': $repl = $textlib->strtolower($repl); break; + case '~': $repl = $textlib->strtotitle($repl); break; + } + if (!empty($block[2])) { + $repl = $textlib->substr($repl, 0 , $block[2]); + } + + return $repl; +} + +/** + * Returns list of auth plugins that are enabled and known to work. + * + * If ppl want to use some other auth type they have to include it + * in the CSV file next on each line. + * + * @return array type=>name + */ +function uu_supported_auths() { + // only following plugins are guaranteed to work properly + $whitelist = array('manual', 'nologin', 'none', 'email'); + $plugins = get_enabled_auth_plugins(); + $choices = array(); + foreach ($plugins as $plugin) { + if (!in_array($plugin, $whitelist)) { + continue; + } + $choices[$plugin] = get_string('pluginname', "auth_{$plugin}"); + } + + return $choices; +} + +/** + * Returns list of roles that are assignable in courses + * @return array + */ +function uu_allowed_roles() { + // let's cheat a bit, frontpage is guaranteed to exist and has the same list of roles ;-) + $roles = get_assignable_roles(get_context_instance(CONTEXT_COURSE, SITEID), ROLENAME_ORIGINALANDSHORT); + return array_reverse($roles, true); +} + +/** + * Returns mapping of all roles using short role name as index. + * @return array + */ +function uu_allowed_roles_cache() { + $allowedroles = get_assignable_roles(get_context_instance(CONTEXT_COURSE, SITEID), ROLENAME_SHORT); + foreach ($allowedroles as $rid=>$rname) { + $rolecache[$rid] = new stdClass(); + $rolecache[$rid]->id = $rid; + $rolecache[$rid]->name = $rname; + if (!is_numeric($rname)) { // only non-numeric shortnames are supported!!! + $rolecache[$rname] = new stdClass(); + $rolecache[$rname]->id = $rid; + $rolecache[$rname]->name = $rname; + } + } + return $rolecache; +} diff --git a/auth/manual/db/upgrade.php b/auth/manual/db/upgrade.php new file mode 100644 index 00000000000..dcfe7bd21ff --- /dev/null +++ b/auth/manual/db/upgrade.php @@ -0,0 +1,49 @@ +. + +/** + * Manual authentication plugin upgrade code + * + * @package auth + * @subpackage manual + * @copyright 2011 Petr Skoda (http://skodak.org) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * @param int $oldversion the version we are upgrading from + * @return bool result + */ +function xmldb_auth_manual_upgrade($oldversion) { + global $CFG, $DB, $OUTPUT; + + if ($oldversion < 2011022700) { + // force creation of missing passwords + $createpassword = hash_internal_user_password(''); + $rs = $DB->get_recordset('user', array('password'=>$createpassword, 'auth'=>'manual')); + foreach ($rs as $user) { + if (validate_email($user->email)) { + $DB->set_field('user', 'password', 'to be created', array('id'=>$user->id)); + unset_user_preference('auth_forcepasswordchange', $user); + set_user_preference('create_password', 1, $user); + } + } + $rs->close(); + upgrade_plugin_savepoint(true, 2011022700, 'auth', 'manual'); + } + + return true; +} diff --git a/auth/manual/version.php b/auth/manual/version.php new file mode 100644 index 00000000000..a8edb8e94cf --- /dev/null +++ b/auth/manual/version.php @@ -0,0 +1,29 @@ +. + +/** + * Manual authentication plugin version information + * + * @package auth + * @subpackage manual + * @copyright 2011 Petr Skoda (http://skodak.org) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2011022700; +$plugin->component = 'auth_manual'; \ No newline at end of file diff --git a/lang/en/admin.php b/lang/en/admin.php index 02fde935601..d11e5ff52d9 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -1053,6 +1053,7 @@ $string['useexternalyui'] = 'Use online YUI libraries'; $string['usehtmleditor'] = 'Use HTML editor'; $string['user'] = 'User'; $string['useraccountupdated'] = 'User updated'; +$string['useraccountuptodate'] = 'User up-to-date'; $string['userbulk'] = 'Bulk user actions'; $string['userdeleted'] = 'User deleted'; $string['userlist'] = 'Browse list of users'; @@ -1082,13 +1083,15 @@ $string['uuoptype_addinc'] = 'Add all, append number to usernames if needed'; $string['uuoptype_addnew'] = 'Add new only, skip existing users'; $string['uuoptype_addupdate'] = 'Add new and update existing users'; $string['uuoptype_update'] = 'Update existing users only'; +$string['uupasswordcron'] = 'Generated in cron'; $string['uupasswordnew'] = 'New user password'; $string['uupasswordold'] = 'Existing user password'; -$string['uupreprocessedcount'] = 'Number of preprocessed records: {$a}'; +$string['uustandardusernames'] = 'Standardise usernames'; $string['uuupdateall'] = 'Override with file and defaults'; $string['uuupdatefromfile'] = 'Override with file'; $string['uuupdatemissing'] = 'Fill in missing from file and defaults'; $string['uuupdatetype'] = 'Existing user details'; +$string['uuusernametemplate'] = 'Username template'; $string['validateerror'] = 'This value was not valid:'; $string['verifychangedemail'] = 'Restrict domains when changing email'; $string['warningcurrentsetting'] = 'Invalid current value: {$a}'; diff --git a/lang/en/error.php b/lang/en/error.php index 2a8df114210..cc945cff311 100755 --- a/lang/en/error.php +++ b/lang/en/error.php @@ -200,6 +200,7 @@ $string['dmltransactionexception'] = 'Database transaction error'; $string['dmlwriteexception'] = 'Error writing to database'; $string['downgradedcore'] = 'ERROR!!! The code you are using is OLDER than the version that made these databases!'; $string['downloadedfilecheckfailed'] = 'Downloaded file check failed'; +$string['duplicatefieldname'] = 'Duplicate field name "{$a}" detected'; $string['duplicateparaminsql'] = 'ERROR: duplicate parameter name in query'; $string['duplicaterolename'] = 'There is already a role with this name!'; $string['duplicateroleshortname'] = 'There is already a role with this short name!'; diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 05b7957c6a6..80b3222ae6c 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1672,11 +1672,8 @@ $string['uploadedfileto'] = 'Uploaded {$a->file} to {$a->directory}'; $string['uploadedfiletoobig'] = 'Sorry, but that file is too big (limit is {$a} bytes)'; $string['uploadextension'] = 'File upload stopped by extension'; $string['uploadfailednotrecovering'] = 'Your file upload has failed because there was a problem with one of the files, {$a->name}.
Here is a log of the problems:
{$a->problem}
Not recovering.'; -$string['uploadfilecontainerror'] = 'The uploaded file has not been processed due to the error(s) specified. Please amend the file before uploading it again.'; -$string['uploadfilecontentsnovaliddata'] = 'The uploaded file contains no valid data.'; $string['uploadfilelog'] = 'Upload log for file {$a}'; $string['uploadformlimit'] = 'Uploaded file exceeded the maximum size limit set by the form'; -$string['uploadinvalidpreprocessedcount'] = 'Number of invalid preprocessed records: {$a}'; $string['uploadlabel'] = 'Title:'; $string['uploadnewfile'] = 'Upload new file'; $string['uploadnofilefound'] = 'No file was found - are you sure you selected one to upload?'; diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 4fe7651c05c..abdb8153912 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -6043,6 +6043,14 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL"); upgrade_main_savepoint(true, 2011020900.08); } + if ($oldversion < 2011022100.01) { + // hack alert: inject missing version of manual auth_plugin, + // we need to do it so that we may use upgrade.php there + + set_config('version', 2011022100, 'auth_manual'); + upgrade_main_savepoint(true, 2011022100.01); + } + return true; } diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 41d32983127..e1d5a6e3f9d 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -4831,8 +4831,7 @@ function setnew_password_and_mail($user) { /** * Resets specified user's password and send the new password to the user via email. * - * @global object - * @param user $user A {@link $USER} object + * @param stdClass $user A {@link $USER} object * @return bool Returns true if mail was sent OK and false if there was an error. */ function reset_password_and_mail($user) { @@ -4866,6 +4865,8 @@ function reset_password_and_mail($user) { $subject = format_string($site->fullname) .': '. get_string('changedpassword'); + unset_user_preference('create_password', $user); // prevent cron from generating the password + //directly email rather than using the messaging system to ensure its not routed to a popup or jabber return email_to_user($user, $supportuser, $subject, $message); diff --git a/login/change_password.php b/login/change_password.php index 2bdb401320e..2608079a004 100644 --- a/login/change_password.php +++ b/login/change_password.php @@ -111,7 +111,8 @@ if ($mform->is_cancelled()) { } // register success changing password - unset_user_preference('auth_forcepasswordchange', $USER->id); + unset_user_preference('auth_forcepasswordchange', $USER); + unset_user_preference('create_password', $USER); $strpasswordchanged = get_string('passwordchanged'); diff --git a/user/editadvanced.php b/user/editadvanced.php index bc99881a2b5..4a1f4ecd8d5 100644 --- a/user/editadvanced.php +++ b/user/editadvanced.php @@ -165,6 +165,7 @@ if ($usernew = $userform->get_data()) { if (!$authplugin->user_update_password($usernew, $usernew->newpassword)){ print_error('cannotupdatepasswordonextauth', '', '', $usernew->auth); } + unset_user_preference('create_password', $usernew); // prevent cron from generating the password } } $usercreated = false; diff --git a/version.php b/version.php index 0b05d606446..7287adaf4fe 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2011022100.00; // YYYYMMDD = date of the last version bump +$version = 2011022100.01; // YYYYMMDD = date of the last version bump // XX = daily increments $release = '2.0.2+ (Build: 20110223)'; // Human-friendly version name