示例#1
0
文件: user.php 项目: mgs2/kw-forum
/**
 * 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;
}
示例#3
0
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 {
            ?>
示例#4
0
<?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);