/** * 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']; }
/** * Create or update the configuration for a custom profile field. * * @param array $field * This parameter holds the field configuration to save. This array * must contain the following fields: * * - id: If a new field has to be created, then use NULL for this field. * If a profile field has to be updated, then use the existing * profile field's id. * * - name: The name that has to be assigned to the custom profile field. * This name can only contain letters, numbers and underscores * (_) and it has to start with a letter. * * The following fields are optional. If they are missing, then a default * value will be used for them. * * - length: The maximum length for the field data. This will make sure * that the data that is stored in the custom profile field will * be truncated in case its length surpasses the configured * custom profile field length. If this field is missing or set * to NULL, then the default length 255 will be used. * * - html_disabled: If this field is set to a true value, then * special HTML characters are not usable in this field. When * displaying the custom field's data, Phorum will automatically * escape these characters. Only use a false value for this * field if the data that will be saved in the field is really safe * for direct use in a web page (to learn about the security risks * involved, search for "XSS" and "cross site scripting" on * the internet). If this field is missing or set to NULL, then * the default setting TRUE will be used. * * - show_in_admin: If this field is set to a true value, then the field * will be displayed on the details page for a user in the admin * "Edit Users" section. If this field is missing or set to NULL, * then the default setting FALSE will be used. * * @return array * This function returns the profile field data in an array, containing * the same fields as the {@link $field} function parameter. If a new * field was created, then the "file_id" field will be set to the new * custom profile field id. The fields "length" and "html_disabled" will * also be updated to their defaults if they were set to NULL in * the $field argument. */ function phorum_api_custom_profile_field_configure($field) { global $PHORUM; // The available fields and their defaults. NULL indicates a mandatory // field. The field "id" can be NULL though, when creating a new // custom profile field. $fields = array('id' => NULL, 'name' => NULL, 'length' => 255, 'html_disabled' => TRUE, 'show_in_admin' => FALSE); // Check if all required fields are in the $field argument. // Assign default values for missing or NULL fields or trigger // or an error if the field is mandatory. foreach ($fields as $f => $default) { if (!array_key_exists($f, $field)) { if ($default === NULL) { trigger_error('phorum_api_custom_profile_field_configure(): Missing field ' . "in \$field parameter: {$f}", E_USER_ERROR); } $field[$f] = $default; } elseif ($f != 'id' && $field[$f] === NULL) { trigger_error('phorum_api_custom_profile_field_configure(): Field $f in ' . "\$field parameter cannot be NULL", E_USER_ERROR); } } $field['id'] = $field['id'] === NULL ? NULL : (int) $field['id']; $field['name'] = trim($field['name']); settype($field['length'], 'int'); settype($field['html_disabled'], 'bool'); settype($field['show_in_admin'], 'bool'); // Check the profile field name. if (!preg_match('/^[a-z][\\w_]*$/i', $field['name'])) { return phorum_api_error_set(PHORUM_ERRNO_INVALIDINPUT, 'Field names can only contain letters, numbers and ' . 'underscores (_) and they must start with a letter.'); } // Check if the profile field name isn't an internally used name. // This is either one of the reserved names or a field that is // already used as a user data field. if (in_array($field['name'], $PHORUM['API']['cpf_reserved']) || isset($GLOBALS['PHORUM']['API']['user_fields'][$field['name']])) { return phorum_api_error_set(PHORUM_ERRNO_INVALIDINPUT, "The name \"{$field['name']}\" is reserved for internal use " . 'by Phorum. Please choose a different name for your custom ' . 'profile field.'); } // Check the bounds for the field length. if ($field['length'] > PHORUM_MAX_CPLENGTH) { return phorum_api_error_set(PHORUM_ERRNO_INVALIDINPUT, "The length \"{$field['length']}\" for the custom profile " . 'field is too large. The maximum length that can be used ' . 'is ' . PHORUM_MAX_CPLENGTH . '.'); } if ($field['length'] <= 0) { return phorum_api_error_set(PHORUM_ERRNO_INVALIDINPUT, "The length for the custom profile field must be above zero."); } // For new fields, check if the name isn't already in use. if ($field['id'] === NULL && phorum_api_custom_profile_field_byname($field['name'])) { return phorum_api_error_set(PHORUM_ERRNO_INVALIDINPUT, "A custom profile field with the name \"{$field['name']}\" " . 'already exists. Please choose a different name for your ' . 'custom profile field.'); } // For existing fields, check if the field id really exists. if ($field['id'] !== NULL && !isset($PHORUM['PROFILE_FIELDS'][$field['id']])) { return phorum_api_error_set(PHORUM_ERRNO_INVALIDINPUT, "A custom profile field with id \"{$field['id']}\" does not " . 'exist. Maybe the field was deleted before you updated its ' . 'settings.'); } // If we have to create a new field, then find a new id for it. // For indexing, we use the "num_fields" profile field configuration // setting. This field is more an auto increment index counter than // the number of fields. For historical reasons, we keep this name // in here (some module contain code which makes use of num_fields // directly). if ($field['id'] === NULL) { // Since there are modules meddling with the data, we do not // fully trust the num_fields. If we see a field with an id // higher than what's in num_fields, then we move the counter up. $high = isset($PHORUM['PROFILE_FIELDS']['num_fields']) ? (int) $PHORUM['PROFILE_FIELDS']['num_fields'] : 0; foreach ($PHORUM['PROFILE_FIELDS'] as $checkid => $profile_field) { if ($checkid > $high) { $high = $checkid; } } // Use the next available value as our id. $field['id'] = $high + 1; // Update the index. $PHORUM['PROFILE_FIELDS']['num_fields'] = $field['id']; } // Update the profile fields information in the settings. $PHORUM['PROFILE_FIELDS'][$field['id']] = $field; phorum_db_update_settings(array('PROFILE_FIELDS' => $PHORUM['PROFILE_FIELDS'])); return $field; }
if (!defined("PHORUM_ADMIN")) { return; } include_once "./include/api/base.php"; include_once "./include/api/custom_profile_fields.php"; // Create or update a custom profile field. if (count($_POST) && $_POST['name'] != '') { $_POST['curr'] = $_POST['curr'] == 'NEW' ? 'NEW' : (int) $_POST['curr']; $_POST['name'] = trim($_POST['name']); $_POST['length'] = (int) $_POST['length']; $_POST['html_disabled'] = !empty($_POST['html_disabled']) ? 1 : 0; $_POST['show_in_admin'] = !empty($_POST['show_in_admin']) ? 1 : 0; // Check if there is a deleted field with the same name. // If this is the case, then we want to give the admin a chance // to restore the deleted field. $check = phorum_api_custom_profile_field_byname($_POST['name']); if ($check !== FALSE && !empty($check["deleted"])) { // Handle restoring a deleted field. if (isset($_POST["restore"])) { if (phorum_api_custom_profile_field_restore($check["id"]) === FALSE) { phorum_admin_error(phorum_api_strerror()); } else { phorum_admin_okmsg("The custom profile field " . "\"{$check["name"]}\" has been restored."); } // Empty the POST array, so the code below won't try to // create or update a field. $_POST = array(); } elseif (isset($_POST["create"])) { phorum_api_custom_profile_field_delete($check["id"], TRUE); } else { ?>
<?php require_once './include/api/custom_profile_fields.php'; // Find out if we have an active real_name custom user profile field. $field = phorum_api_custom_profile_field_byname('real_name'); if (empty($field) || !empty($field['deleted'])) { return; } // If we do, then copy all available real_names to the new real_name // field in the user table. $ids = phorum_api_user_search_custom_profile_field($field['id'], '', '*', TRUE); if (!empty($ids)) { foreach ($ids as $id) { $user = phorum_api_user_get($id); phorum_api_user_save_raw(array("user_id" => $id, "real_name" => $user["real_name"])); } } // Now we can delete the existing real_name field. phorum_api_custom_profile_field_delete($real_name_field_id, TRUE);