diff -ru -x .svn -x .DS_Store -x CVS trunk/core/auth/db/auth.php branches/cisco-3.3/auth/db/auth.php --- trunk/core/auth/db/auth.php 2008-09-10 13:38:35.000000000 -0700 +++ branches/cisco-3.3/auth/db/auth.php 2009-02-04 11:12:51.000000000 -0800 @@ -32,6 +32,16 @@ if (empty($this->config->extencoding)) { $this->config->extencoding = 'utf-8'; } + + $this->userfields = array_merge( + $this->userfields, + array( + 'maildigest', + 'autosubscribe', + 'trackforums', + 'password' + ) + ); } /** @@ -210,6 +220,22 @@ } /** + * Cron hook + * + * @return void + **/ + function cron() { + global $CFG; + + require_once($CFG->libdir.'/blocklib.php'); + require_once($CFG->dirroot.'/course/lib.php'); + require_once($CFG->dirroot.'/mod/resource/lib.php'); + require_once($CFG->dirroot.'/mod/forum/lib.php'); + + $this->sync_users(true); + } + + /** * syncronizes user fron external db to moodle user table * * Sync shouid be done by using idnumber attribute, not username. @@ -229,16 +255,27 @@ global $CFG; $pcfg = get_config('auth/db'); + $textlib = textlib_get_instance(); + /// list external users $userlist = $this->get_userlist(); - $quoteduserlist = implode("', '", addslashes_recursive($userlist)); - $quoteduserlist = "'$quoteduserlist'"; + + // Make into a quoted list and convert userlist to utf-8 + $quoteduserlist = array(); + foreach ($userlist as $key => $userinlist) { + $userinlist = $textlib->convert($userinlist, $this->config->extencoding, 'utf-8'); + $userlist[$key] = $userinlist; + $quoteduserlist[] = addslashes($userinlist); + } + $quoteduserlist = '\''.implode("', '", $quoteduserlist).'\''; /// delete obsolete internal users if (!empty($this->config->removeuser)) { // find obsolete users - if (count($userlist)) { + if (count($userlist) and count($userlist) <= 20000) { + // Cannot grab too many or the SQL will fail $sql = "SELECT u.id, u.username, u.email, u.auth FROM {$CFG->prefix}user u WHERE u.auth='db' AND u.deleted=0 AND u.username NOT IN ($quoteduserlist)"; @@ -247,13 +284,12 @@ FROM {$CFG->prefix}user u WHERE u.auth='db' AND u.deleted=0"; } - $remove_users = get_records_sql($sql); - - if (!empty($remove_users)) { - print_string('auth_dbuserstoremove','auth', count($remove_users)); echo "\n"; - - foreach ($remove_users as $user) { - if ($this->config->removeuser == 2) { + if ($rs = get_recordset_sql($sql)) { + while ($user = rs_fetch_next_record($rs)) { + if (in_array($user->username, $userlist)) { + // Sometimes we have to process all moodle users + continue; + } else if ($this->config->removeuser == 2) { if (delete_user($user)) { echo "\t"; print_string('auth_dbdeleteuser', 'auth', array($user->username, $user->id)); echo "\n"; } else { @@ -270,6 +306,7 @@ } } } + rs_close($rs); } unset($remove_users); // free mem! } @@ -300,21 +337,57 @@ // only go ahead if we actually // have fields to update locally if (!empty($updatekeys)) { - $sql = 'SELECT u.id, u.username - FROM ' . $CFG->prefix .'user u - WHERE u.auth=\'db\' AND u.deleted=\'0\' AND u.username IN (' . $quoteduserlist . ')'; - if ($update_users = get_records_sql($sql)) { - print "User entries to update: ". count($update_users). "\n"; - - foreach ($update_users as $user) { - echo "\t"; print_string('auth_dbupdatinguser', 'auth', array($user->username, $user->id)); - if (!$this->update_user_record(addslashes($user->username), $updatekeys)) { - echo " - ".get_string('skipped'); - } - echo "\n"; + print "User entries to update: ". count($userlist). "\n"; + + // Get our SQL select fields + $selectfields = $this->db_attributes(); + + // Map SQL select fields to local names + $select = array("username AS {$this->config->fielduser}"); + foreach ($selectfields as $localname => $externalname) { + if (in_array($localname, $updatekeys)) { + $select[] = "$externalname AS $localname"; + } + } + $select = implode(', ', $select); + + $userchunks = array_chunk($userlist, 20000); + + $authdb = $this->db_init(); + begin_sql(); + + foreach ($userchunks as $userchunk) { + $usernames = array(); + foreach ($userchunk as $username) { + $extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->extencoding); + $usernames[] = $this->ext_addslashes($extusername); } - unset($update_users); // free memory + $usernames = '\''.implode('\',\'',$usernames).'\''; + + $sql = "SELECT $select + FROM {$this->config->table} + WHERE {$this->config->fielduser} IN ($usernames)"; + + if ($rs = $authdb->Execute($sql)) { + while ($externaluser = rs_fetch_next_record($rs)) { + $user = array(); + foreach ($selectfields as $localname=>$externalname) { + if (in_array($localname, $updatekeys) or $localname == 'username') { + $user[$localname] = $textlib->convert($externaluser->{$localname}, $this->config->extencoding, 'utf-8'); + } + } + + if (!$this->update_user_record(addslashes($user['username']), $updatekeys, $user)) { + echo 'Failed to update user with username '.$user['username']; + } + echo "\n"; + } + rs_close($rs); + } } + commit_sql(); + $authdb->Close(); + unset($userchunks, $userchunk, $usernames); } } @@ -322,21 +395,8 @@ /// /// create missing accounts /// - // NOTE: this is very memory intensive - // and generally inefficient - $sql = 'SELECT u.id, u.username - FROM ' . $CFG->prefix .'user u - WHERE u.auth=\'db\' AND u.deleted=\'0\''; - - $users = get_records_sql($sql); - - // simplify down to usernames - $usernames = array(); - if (!empty($users)) { - foreach ($users as $user) { - array_push($usernames, $user->username); - } - unset($users); + if (!$usernames = get_records_select_menu('user', 'auth = \'db\' AND deleted = \'0\'', '', 'id, username')) { + $usernames = array(); } $add_users = array_diff($userlist, $usernames); @@ -344,40 +404,95 @@ if (!empty($add_users)) { print_string('auth_dbuserstoadd','auth',count($add_users)); echo "\n"; - begin_sql(); - foreach($add_users as $user) { - $username = $user; - $user = $this->get_userinfo_asobj($user); - - // prep a few params - $user->username = $username; - $user->modified = time(); - $user->confirmed = 1; - $user->auth = 'db'; - $user->mnethostid = $CFG->mnet_localhost_id; - if (empty($user->lang)) { - $user->lang = $CFG->lang; + + $authdb = $this->db_init(); + + // Get our SQL select fields + $selectfields = $this->db_attributes(); + if (!isset($selectfields['username'])) { + $selectfields['username'] = $this->config->fielduser; + } + + // Map SQL select fields to local names + $select = array(); + foreach ($selectfields as $localname => $externalname) { + $select[] = "$externalname AS $localname"; + } + $select = implode(', ', $select); + + // SQL query failed with 100,000 usernames in it + // so, to avoid that, we chunk it up, 20,000 seems good? + $userchunks = array_chunk($add_users, 20000); + + foreach ($userchunks as $userchunk) { + // Prep usernames for being used in external SQL query + $usernames = array(); + foreach ($userchunk as $username) { + $extusername = $textlib->convert(stripslashes($username), 'utf-8', $this->config->extencoding); + $usernames[] = $this->ext_addslashes($extusername); } + $usernames = '\''.implode('\',\'',$usernames).'\''; - $user = addslashes_object($user); - // maybe the user has been deleted before - if ($old_user = get_record('user', 'username', $user->username, 'deleted', 1, 'mnethostid', $user->mnethostid)) { - $user->id = $old_user->id; - set_field('user', 'deleted', 0, 'username', $user->username); - echo "\t"; print_string('auth_dbreviveuser', 'auth', array(stripslashes($user->username), $user->id)); echo "\n"; - } elseif ($id = insert_record ('user',$user)) { // it is truly a new user - echo "\t"; print_string('auth_dbinsertuser','auth',array(stripslashes($user->username), $id)); echo "\n"; - // if relevant, tag for password generation - if ($this->config->passtype === 'internal') { - set_user_preference('auth_forcepasswordchange', 1, $id); - set_user_preference('create_password', 1, $id); - } - } else { - echo "\t"; print_string('auth_dbinsertusererror', 'auth', $user->username); echo "\n"; + $sql = "SELECT $select + FROM {$this->config->table} + WHERE {$this->config->fielduser} IN ($usernames)"; + + if ($rs = $authdb->Execute($sql)) { + begin_sql(); + while ($externaluser = rs_fetch_next_record($rs)) { + $result = array(); + foreach ($selectfields as $localname=>$externalname) { + $result[$localname] = $textlib->convert($externaluser->{$localname}, $this->config->extencoding, 'utf-8'); + } + $user = $this->get_userinfo_asobj($result); + + // prep a few params + $user->modified = time(); + $user->confirmed = 1; + $user->auth = 'db'; + $user->mnethostid = $CFG->mnet_localhost_id; + if (empty($user->lang)) { + $user->lang = $CFG->lang; + } + $user = addslashes_object($user); + + // maybe the user has been deleted before + if ($old_user = get_record('user', 'username', $user->username, 'deleted', 1, 'mnethostid', $user->mnethostid)) { + $user->id = $old_user->id; + set_field('user', 'deleted', 0, 'username', $user->username); + echo "\t"; print_string('auth_dbreviveuser', 'auth', array(stripslashes($user->username), $user->id)); echo "\n"; + } elseif ($id = insert_record ('user',$user)) { // it is truly a new user + echo "\t"; print_string('auth_dbinsertuser','auth',array(stripslashes($user->username), $id)); echo "\n"; + // if relevant, tag for password generation + if ($this->config->passtype === 'internal') { + if($this->config->forcepasswordchange == 1){ + set_user_preference('auth_forcepasswordchange', 1, $id); + } + + if($this->config->createpassword == 1){ + //Set the create password preference is password to change is not blank and equals the user's password, or is blank. + if((!empty($this->config->passwordtochange) && $this->config->passwordtochange == $user->password) || + (empty($this->config->passwordtochange))){ + print "creating password \n"; + set_user_preference('create_password', 1, $id); + set_field('user', 'password', '', 'id', $id); + } else { + set_field('user', 'password', md5($user->password), 'id', $id); + } + } else { + set_field('user', 'password', md5($user->password), 'id', $id); + } + } + } else { + echo "\t"; print_string('auth_dbinsertusererror', 'auth', $user->username); echo "\n"; + } + } + commit_sql(); + rs_close($rs); } } - commit_sql(); - unset($add_users); // free mem + unset($add_users); + $authdb->Close(); } return true; } @@ -432,13 +547,14 @@ } /** - * reads userinformation from DB and return it in an object + * Returns user array as an object + * with all values truncated properly * - * @param string $username username (with system magic quotes) - * @return array + * @param array $user User array to truncate and convert + * @return object */ - function get_userinfo_asobj($username) { - $user_array = truncate_userinfo($this->get_userinfo($username)); + function get_userinfo_asobj($user) { + $user_array = truncate_userinfo($user); $user = new object(); foreach($user_array as $key=>$value) { $user->{$key} = $value; @@ -456,7 +572,7 @@ * * @param string $username username (with system magic quotes) */ - function update_user_record($username, $updatekeys=false) { + function update_user_record($username, $updatekeys=false, $newinfo = false) { global $CFG; //just in case check text case @@ -465,21 +581,26 @@ // get the current user record $user = get_record('user', 'username', $username, 'mnethostid', $CFG->mnet_localhost_id); if (empty($user)) { // trouble - error_log("Cannot update non-existent user: $username"); - print_error('auth_dbusernotexist','auth',$username); - die; + return false; } + echo "\t"; print_string('auth_dbupdatinguser', 'auth', array($user->username, $user->id)); + // Ensure userid is not overwritten $userid = $user->id; - if ($newinfo = $this->get_userinfo($username)) { + if ($newinfo or $newinfo = $this->get_userinfo($username)) { $newinfo = truncate_userinfo($newinfo); if (empty($updatekeys)) { // all keys? this does not support removing values $updatekeys = array_keys($newinfo); } + $update = new stdClass; + $update->id = $userid; + + $save = false; + foreach ($updatekeys as $key) { if (isset($newinfo[$key])) { $value = $newinfo[$key]; @@ -489,12 +610,18 @@ if (!empty($this->config->{'field_updatelocal_' . $key})) { if ($user->{$key} != $value) { // only update if it's changed - set_field('user', $key, addslashes($value), 'id', $userid); + $update->{$key} = addslashes($value); + $save = true; } } } + if ($save) { + if (!update_record('user', $update)) { + return false; + } + } } - return get_record_select('user', "id = $userid AND deleted = 0"); + return true; } /** @@ -670,6 +797,15 @@ if (!isset($config->changepasswordurl)) { $config->changepasswordurl = ''; } + if (!isset($config->createpassword)) { + $config->createpassword = 0; + } + if (!isset($config->forcepasswordchange)) { + $config->forcepasswordchange = 0; + } + if (!isset($config->passwordtochange)) { + $config->passwordtochange = ''; + } $config = stripslashes_recursive($config); // save settings @@ -688,6 +824,9 @@ set_config('debugauthdb', $config->debugauthdb, 'auth/db'); set_config('removeuser', $config->removeuser, 'auth/db'); set_config('changepasswordurl', trim($config->changepasswordurl), 'auth/db'); + set_config('createpassword',$config->createpassword, 'auth/db'); + set_config('forcepasswordchange', $config->forcepasswordchange, 'auth/db'); + set_config('passwordtochange', $config->passwordtochange, 'auth/db'); return true; } Only in branches/cisco-3.3/auth/db: auth.php.rej diff -ru -x .svn -x .DS_Store -x CVS trunk/core/auth/db/auth_db_sync_users.php branches/cisco-3.3/auth/db/auth_db_sync_users.php --- trunk/core/auth/db/auth_db_sync_users.php 2008-02-16 12:03:13.000000000 -0800 +++ branches/cisco-3.3/auth/db/auth_db_sync_users.php 2009-02-04 11:12:51.000000000 -0800 @@ -21,7 +21,6 @@ * */ - if (isset($_SERVER['REMOTE_ADDR'])) { error_log("should not be called from web server!"); exit; @@ -31,17 +30,14 @@ require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); // global moodle config file. -require_once($CFG->libdir.'/blocklib.php'); -require_once($CFG->dirroot.'/course/lib.php'); -require_once($CFG->dirroot.'/mod/resource/lib.php'); -require_once($CFG->dirroot.'/mod/forum/lib.php'); - if (!is_enabled_auth('db')) { echo "Plugin not enabled!"; die; } +set_time_limit(0); + $dbauth = get_auth_plugin('db'); -$dbauth->sync_users(true); +$dbauth->cron(); ?> \ No newline at end of file diff -ru -x .svn -x .DS_Store -x CVS trunk/core/auth/db/config.html branches/cisco-3.3/auth/db/config.html --- trunk/core/auth/db/config.html 2008-02-16 12:03:13.000000000 -0800 +++ branches/cisco-3.3/auth/db/config.html 2008-12-29 15:42:50.000000000 -0800 @@ -46,10 +46,40 @@ if (!isset($config->removeuser)) { $config->removeuser = 0; } + if (!isset($config->createpassword)) { + $config->createpassword = 0; + } + if (!isset($config->forcepasswordchange)) { + $config->forcepasswordchange = 0; + } + if (!isset($config->passwordtochange)) { + $config->passwordtochange = ''; + } $yesno = array( get_string('no'), get_string('yes') ); ?> + @@ -192,12 +222,38 @@ $passtype["md5"] = get_string("md5", "auth"); $passtype["sha1"] = get_string("sha1", "auth"); $passtype["internal"] = get_string("internal", "auth"); - choose_from_menu($passtype, "passtype", $config->passtype, ""); + choose_from_menu($passtype, "passtype", $config->passtype, "", 'passtypeChange()'); ?> + + + + + + + + + + + + + + + + @@ -272,4 +328,9 @@ print_auth_lock_options('db', $user_fields, get_string('auth_dbextrafields', 'auth'), true, true); ?> +
+ createpassword, ""); + ?> +
+ +
+ forcepasswordchange, ""); + ?> +
--- trunk/core/lang/en_utf8/moodle.php 2009-01-29 09:43:02.000000000 -0800 +++ branches/cisco-3.3/lang/en_utf8/moodle.php 2009-01-29 09:59:19.000000000 -0800 @@ -900,6 +900,7 @@ $string['logtoomanyusers'] = '[ url\">more ]'; $string['lookback'] = 'Look back'; $string['mailadmins'] = 'Inform admins'; +$string['maildigest'] = 'Mail digest'; $string['mailstudents'] = 'Inform students'; $string['mailteachers'] = 'Inform teachers'; $string['mainmenu'] = 'Main Menu'; --- trunk/core/lang/en_utf8/auth.php 2008-12-16 14:56:02.000000000 -0800 +++ branches/cisco-3.3/lang/en_utf8/auth.php 2008-12-29 15:02:42.000000000 -0800 @@ -111,6 +111,12 @@ $string['auth_dbdebugauthdb'] = 'Debug ADOdb'; $string['auth_dbdebugauthdbhelp'] = 'Debug ADOdb connection to external database - use when getting empty page during login. Not suitable for production sites.'; $string['auth_dbchangepasswordurl_key'] = 'Password-change URL'; +$string['auth_dbforcepasswordchange'] = 'Force Password Change'; +$string['auth_dbcreatepassword'] = 'Create Unique Password'; +$string['auth_dbforcepasswordchangehelp'] = 'If the password type is set to internal then this setting determines if the user is forced to change their password on their first login'; +$string['auth_dbcreatepasswordhelp'] = 'If the password type is set to internal then this setting determines if a unique password is created and emailed to the user\'s email address in the external datbase.'; +$string['auth_dbpasswordtochange'] = 'Only Change this Password'; +$string['auth_dbpasswordtochangehelp'] = 'Adding a value to this field causes moodle to only create a password for those users that have their password set to the value. For example changeme will cause all users with the password changeme to have a unique password created and emailed to them. Leave this blank if you want to change all users password when their acocunt is first created.'; // Email plugin $string['auth_emailchangecancel'] = 'Cancel email change';