Example #1
0
/**
 * 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'];
}
Example #2
0
     // 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;
 }