/** * Create or update Phorum users. * * This function can be used for both creating and updating Phorum users. * If the user_id in the user data is NULL, a new user will be created. * If a user_id is provided, then the existing user will be updated or a * new user with that user_id is created. * * Often when calling this function yourself, you will be doing that for * synchronizing a user from some external system with the Phorum database. * For those cases, the most basic use of this API function can be found * in the examples below. * <code> * $user = array( * "user_id" => 1234, * "username" => 'johndoe', * "password" => '#barbar#', * "email" => '*****@*****.**', * "admin" => 0, * "active" => PHORUM_USER_ACTIVE * ); * phorum_api_user_save($user); * </code> * * If you do not have the plain text password available, but only an MD5 * hash for the password, then you can use the following code instead. * <code> * $user = array( * "user_id" => 1234, * "username" => 'johndoe', * "password" => '5d61ed116ffdecf2d29cd1ed9bd9d4cb', * "email" => '*****@*****.**', * "admin" => 0, * "active" => PHORUM_USER_ACTIVE * ); * phorum_api_user_save($user, PHORUM_FLAG_RAW_PASSWORD); * </code> * * @param array $user * An array containing user data. This array should at least contain * a field "user_id". This field can be NULL to create a new user * with an automatically assigned user_id. It can also be set to a * user_id to either update an existing user or to create a new user * with the provided user_id. * If a new user is created, then all user fields must be provided * in the user data. * * @param int $flags * If the flag {@link PHORUM_FLAG_RAW_PASSWORD} is set, then the * password fields ("password" and "password_temp") are considered to be * MD5 encrypted already. So this can be used to feed Phorum existing MD5 * encrypted passwords. * * @return int * The user_id of the user. For new users, the newly assigned user_id * will be returned. */ function phorum_api_user_save($user, $flags = 0) { global $PHORUM; include_once './include/api/custom_profile_fields.php'; // $user must be an array. if (!is_array($user)) { trigger_error('phorum_api_user_save(): $user argument is not an array', E_USER_ERROR); return NULL; } // We need at least the user_id field. if (!array_key_exists('user_id', $user)) { trigger_error('phorum_api_user_save(): missing field "user_id" in user data array', E_USER_ERROR); return NULL; } if ($user['user_id'] !== NULL && !is_numeric($user['user_id'])) { trigger_error('phorum_api_user_save(): field "user_id" not NULL or numerical', E_USER_ERROR); return NULL; } // Check if we are handling an existing or new user. $existing = NULL; if ($user['user_id'] !== NULL) { $existing = phorum_api_user_get($user['user_id'], TRUE, TRUE, TRUE); } // Create a user data array that is understood by the database layer. // We start out with the existing record, if we have one. $dbuser = $existing === NULL ? array() : $existing; // Merge in the fields from the $user argument. foreach ($user as $fld => $val) { $dbuser[$fld] = $val; } // Initialize storage for custom profile field data. $user_data = array(); // Check and format the user data fields. foreach ($dbuser as $fld => $val) { // Determine the field type that we are handling. $fldtype = NULL; $custom = NULL; // Check if we are handling a custom profile field. We asume that any // field that is not in the user_fields array is a custom profile // field. If we find that it isn't such field, then we will ignore // the field (it's either a field that was manually added to the // user table or a custom profile field that was just deleted). if (!array_key_exists($fld, $PHORUM['API']['user_fields'])) { $custom = phorum_api_custom_profile_field_byname($fld); if ($custom === NULL) { $fldtype = 'ignore_field'; } else { $fldtype = 'custom_profile_field'; } } else { $fldtype = $PHORUM['API']['user_fields'][$fld]; } switch ($fldtype) { // A field that has to be fully ignored. case NULL: break; case 'int': $dbuser[$fld] = $val === NULL ? NULL : (int) $val; break; case 'float': $dbuser[$fld] = $val === NULL ? NULL : (double) $val; break; case 'string': $dbuser[$fld] = $val === NULL ? NULL : trim($val); break; case 'bool': $dbuser[$fld] = $val ? 1 : 0; break; case 'array': // TODO: maybe check for real arrays here? $dbuser[$fld] = $val; break; case 'custom_profile_field': // Arrays and NULL values are left untouched. // Other values are truncated to their configured field length. if ($val !== NULL && !is_array($val)) { $val = substr($val, 0, $custom['length']); } $user_data[$custom['id']] = $val; unset($dbuser[$fld]); break; case 'ignore_field': unset($dbuser[$fld]); break; default: trigger_error('phorum_api_user_save(): Illegal field type used: ' . htmlspecialchars($fldtype), E_USER_ERROR); return NULL; break; } } // Add the custom profile field data to the user data. $dbuser['user_data'] = $user_data; // At this point, we should have a couple of mandatory fields available // in our data. Without these fields, the user record is not sane // enough to continue with. // We really need a username, so we can always generate a display name. if (!isset($dbuser['username']) || $dbuser['username'] == '') { trigger_error('phorum_api_user_save(): the username field for a user record ' . 'cannot be empty', E_USER_ERROR); return NULL; } // Phorum sends out mail messages on several occasions. So we need a // mail address for the user. if (!isset($dbuser['email']) || $dbuser['email'] == '') { trigger_error('phorum_api_user_save(): the email field for a user record ' . 'cannot be empty', E_USER_ERROR); return NULL; } // For new accounts only. if (!$existing) { if (empty($dbuser['date_added'])) { $dbuser['date_added'] = time(); } if (empty($dbuser['date_last_active'])) { $dbuser['date_last_active'] = time(); } } // Handle password encryption. foreach (array('password', 'password_temp') as $fld) { // Sometimes, this function is (accidentally) called with existing // passwords in the data. Prevent duplicate encryption. if ($existing && strlen($existing[$fld]) == 32 && $existing[$fld] == $dbuser[$fld]) { continue; } // If the password field is empty, we should never store the MD5 sum // of an empty string as a safety precaution. Instead we store a // string which will never work as a password. This could happen in // case of bugs in the code or in case external user auth is used // (in which case Phorum can have empty passwords, since the Phorum // passwords are not used at all). if (!isset($dbuser[$fld]) || $dbuser[$fld] === NULL || $dbuser[$fld] == '' || $dbuser[$fld] == '*NO PASSWORD SET*') { $dbuser[$fld] = '*NO PASSWORD SET*'; continue; } // Only crypt the password using MD5, if the PHORUM_FLAG_RAW_PASSWORD // flag is not set. if (!($flags & PHORUM_FLAG_RAW_PASSWORD)) { $dbuser[$fld] = md5($dbuser[$fld]); } } // Determine the display name to use for the user. If the setting // $PHORUM["custom_display_name"] is enabled (a "secret" setting which // cannot be changed through the admin settings, but only through // modules that consciously set it), then Phorum expects that the display // name is a HTML formatted display_name field, which is provided by // 3rd party software. Otherwise, the username or real_name is used // (depending on the $PHORUM["display_name_source"] Phorum setting). if (empty($PHORUM['custom_display_name'])) { $display_name = $dbuser['username']; if ($PHORUM['display_name_source'] == 'real_name' && isset($dbuser['real_name']) && trim($dbuser['real_name']) != '') { $display_name = $dbuser['real_name']; } $dbuser['display_name'] = $display_name; } elseif (!isset($dbuser['display_name']) || trim($dbuser['display_name']) == '') { $dbuser['display_name'] = htmlspecialchars($dbuser['username'], ENT_COMPAT, $PHORUM['DATA']['HCHARSET']); } /** * [hook] * user_save * * [description] * This hook can be used to handle the data that is going to be * stored in the database for a user. Modules can do some last * minute change on the data or keep some external system in sync * with the Phorum user data.<sbr/> * <sbr/> * In combination with the <hook>user_get</hook> hook, this hook * could also be used to store and retrieve some of the Phorum * user fields using some external system. * * [category] * User data handling * * [when] * Just before user data is stored in the database. * * [input] * An array containing user data that will be sent to the database. * * [output] * The same array as the one that was used for the hook call * argument, possibly with some updated fields in it. * * [example] * <hookcode> * function phorum_mod_foo_user_save($user) * { * // Add "[A]" in front of admin user real_name fields. * $A = $user["admin"] ? "[A]" : ""; * $real_name = preg_replace('/^\[A\]/', $A, $user["real_name"]); * $user['real_name'] = $real_name; * * // Some fictional external system to keep in sync. * include("../coolsys.php"); * coolsys_save($user); * * return $user; * } * </hookcode> */ if (isset($PHORUM['hooks']['user_save'])) { $dbuser = phorum_hook('user_save', $dbuser); } /** * [hook] * user_register * * [description] * This hook is called when a user registration is completed by * setting the status for the user to PHORUM_USER_ACTIVE. * This hook will not be called right after filling in the * registration form (unless of course, the registration has been * setup to require no verification at all in which case the user * becomes active right away). * * [category] * User data handling * * [when] * Right after a new user registration becomes active. * * [input] * An array containing user data for the registered user. * * [output] * The same array as the one that was used for the hook call * argument, possibly with some updated fields in it. * * [example] * <hookcode> * function phorum_mod_foo_user_register($user) * { * // Log user registrations through syslog. * openlog("Phorum", LOG_PID | LOG_PERROR, LOG_LOCAL0); * syslog(LOG_NOTICE, "New user registration: $user[username]"); * * return $user; * } * </hookcode> */ if (isset($PHORUM['hooks']['user_register'])) { // Only fire this hook if a user goes from some pending state // to the active state. if ($dbuser['active'] == PHORUM_USER_ACTIVE) { // For new users we have no existing status. For those we asume // a pending status, so below a switch to active status will mean // that the user registration is activated. $orig_status = $existing ? $existing['active'] : PHORUM_USER_PENDING_MOD; if ($orig_status == PHORUM_USER_PENDING_BOTH || $orig_status == PHORUM_USER_PENDING_EMAIL || $orig_status == PHORUM_USER_PENDING_MOD) { $dbuser = phorum_hook('user_register', $dbuser); } } } // Add or update the user in the database. if ($existing) { phorum_db_user_save($dbuser); } else { $dbuser['user_id'] = phorum_db_user_add($dbuser); } // If the display name changed for the user, then we do need to run // updates throughout the Phorum database to make references to this // user to show up correctly. if ($existing && $existing['display_name'] != $dbuser['display_name']) { phorum_db_user_display_name_updates($dbuser); } // If user caching is enabled, we invalidate the cache for this user. if (!empty($PHORUM['cache_users'])) { phorum_cache_remove('user', $dbuser['user_id']); } // Are we handling the active Phorum user? Then refresh the user data. if (isset($PHORUM['user']) && $PHORUM['user']['user_id'] == $dbuser['user_id']) { $PHORUM['user'] = phorum_api_user_get($user['user_id'], TRUE, TRUE); } return $dbuser['user_id']; }
// Find the update batch that we have to run. $batch = empty($_REQUEST["batch"]) ? 0 : $_REQUEST["batch"]; // Retrieve users for this batch. $res = phorum_db_user_get_all($batch * $batchsize, $batchsize); // Handle batch. $updated = 0; while ($user = phorum_db_fetch_row($res, DB_RETURN_ASSOC)) { $updated++; // We save an empty user, to make sure that the display name in the // database is up-to-date. This will already run needed updates in // case the display name changed ... phorum_api_user_save(array("user_id" => $user["user_id"])); // ... but still we run the name updates here, so inconsitencies // are flattened out. $user = phorum_api_user_get($user["user_id"]); phorum_db_user_display_name_updates(array("user_id" => $user["user_id"], "display_name" => $user["display_name"])); } if ($updated == 0) { $frm = new PhorumInputForm("", "post", "Finish"); $frm->addbreak("Display names updated"); $frm->addmessage("The display names are all updated successfully."); $frm->show(); return; } } // Retrieve user count. $user_count = isset($_REQUEST['user_count']) ? (int) $_REQUEST['user_count'] : phorum_db_user_count(); $perc = floor(($batch + 1) * $batchsize / $user_count * 100); if ($perc > 100) { $perc = 100; }