Вход Регистрация
Файл: library/XenForo/DataWriter/User.php
Строк: 1103
<?php

/**
* Data writer for users.
*
* @package XenForo_User
*/
class XenForo_DataWriter_User extends XenForo_DataWriter
{
    const 
OPTION_USERNAME_LENGTH_MIN 'usernameLengthMin';
    const 
OPTION_USERNAME_LENGTH_MAX 'usernameLengthMax';
    const 
OPTION_USERNAME_DISALLOWED_NAMES 'usernameIllegalNames';
    const 
OPTION_USERNAME_REGEX 'usernameRegex';
    const 
OPTION_CUSTOM_TITLE_DISALLOWED 'customTitleDisallowed';
    const 
OPTION_ADMIN_EDIT 'adminEdit';
    const 
OPTION_ALLOW_DELETE_SELF 'allowDeleteSelf';
    const 
OPTION_MESSAGE_MAX_LENGTH 'messageMaxLength';
    const 
OPTION_LOG_CHANGES 'logChanges';

    const 
DATA_USER_FIELD_DEFINITIONS 'userFields';

    
/**
     * Title of the phrase that will be created when a call to set the
     * existing data fails (when the data doesn't exist).
     *
     * @var string
     */
    
protected $_existingDataErrorPhrase 'requested_user_not_found';

    
/**
     * If this is set, it represents a set of seconardy group relations to *replace*.
     * When it is null, no relations will be updated.
     * @var null|array
     */
    
protected $_secondaryGroups null;

    
/**
     * The custom fields to be updated. Use setCustomFields to manage this.
     *
     * @var array
     */
    
protected $_updateCustomFields = array();

    
/**
    * Gets the fields that are defined for the table. See parent for explanation.
    *
    * @return array
    */
    
protected function _getFields()
    {
        return array(
            
'xf_user' => array(
                
'user_id'
                    
=> array('type' => self::TYPE_UINT'autoIncrement' => true'verification' => array('XenForo_DataWriter_Helper_User''verifyUserid')),
                
'username'
                    
=> array('type' => self::TYPE_STRING'required' => true'maxLength' => 50'verification' => array('$this''_verifyUsername'), 'requiredError' => 'please_enter_valid_name'),
                
'email'
                    
=> array('type' => self::TYPE_STRING'maxLength' => 120'verification' => array('$this''_verifyEmail'), 'requiredError' => 'please_enter_valid_email'),
                
'gender'
                    
=> array('type' => self::TYPE_STRING'default' => '''allowedValues' => array('male''female''')),
                
'style_id'
                    
=> array('type' => self::TYPE_UINT'default' => 0'verification' => array('$this''_verifyStyleId')),
                
'language_id'
                    
=> array('type' => self::TYPE_UINT'default' => 0'verification' => array('$this''_verifyLanguageId')),
                
'timezone'
                    
=> array('type' => self::TYPE_STRING'default' => 'Europe/London''maxLength' => 50'verification' => array('$this''_verifyTimezone')),
                
'visible'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 1),
                
'user_group_id'
                    
=> array('type' => self::TYPE_UINT'required' => true),
                
'secondary_group_ids'
                    
=> array('type' => self::TYPE_BINARY'default' => ''),
                
'display_style_group_id'
                    
=> array('type' => self::TYPE_UINT'default' => 0),
                
'permission_combination_id'
                    
=> array('type' => self::TYPE_UINT'default' => 0),
                
'message_count'
                    
=> array('type' => self::TYPE_UINT_FORCED'default' => 0),
                
'alerts_unread'
                    
=> array('type' => self::TYPE_UINT_FORCED'default' => 0),
                
'conversations_unread'
                    
=> array('type' => self::TYPE_UINT_FORCED'default' => 0),
                
'register_date'
                    
=> array('type' => self::TYPE_UINT'default' => XenForo_Application::$time),
                
'last_activity'
                    
=> array('type' => self::TYPE_UINT'default' => XenForo_Application::$time),
                
'trophy_points'
                    
=> array('type' => self::TYPE_UINT_FORCED'default' => 0),
                
'avatar_date'
                    
=> array('type' => self::TYPE_UINT'default' => 0),
                
'avatar_width'
                    
=> array('type' => self::TYPE_UINT'max' => 65535'default' => 0),
                
'avatar_height'
                    
=> array('type' => self::TYPE_UINT'max' => 65535'default' => 0),
                
'gravatar'
                    
=> array('type' => self::TYPE_STRING'maxLength' => 120'default' => '''verification' => array('$this''_verifyGravatar')),
                
'user_state'
                    
=> array('type' => self::TYPE_STRING'allowedValues' => array('valid''email_confirm''email_confirm_edit''moderated''email_bounce'), 'default' => 'valid'),
                
'is_moderator'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 0),
                
'is_admin'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 0),
                
'is_staff'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 0),
                
'is_banned'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 0),
                
'like_count'
                    
=> array('type' => self::TYPE_UINT_FORCED'default' => 0),
                
'custom_title'
                    
=> array('type' => self::TYPE_STRING'maxLength' => 50'default' => '''verification' => array('$this''_verifyCustomTitle')),
                
'warning_points'
                    
=> array('type' => self::TYPE_UINT_FORCED'default' => 0)
            ),
            
'xf_user_profile' => array(
                
'user_id'    => array('type' => self::TYPE_UINT,   'default' => array('xf_user''user_id'), 'required' => true),
                
'dob_day'    => array('type' => self::TYPE_UINT,   'default' => 0'max' => 31),
                
'dob_month'  => array('type' => self::TYPE_UINT,   'default' => 0'max' => 12),
                
'dob_year'   => array('type' => self::TYPE_UINT,   'default' => 0'max' => 2100),
                
'status'     => array('type' => self::TYPE_STRING'default' => '''maxLength' => 140),
                
'status_date' => array('type' => self::TYPE_UINT'default' => 0),
                
'status_profile_post_id' => array('type' => self::TYPE_UINT'default' => 0),
                
'signature'  => array('type' => self::TYPE_STRING'default' => '''verification' => array('$this''_verifyMessageLikeContent'), 'maxLength' => 20000),
                
'homepage'   => array('type' => self::TYPE_STRING'default' => '''verification' => array('XenForo_DataWriter_Helper_Uri''verifyUriOrEmpty')),
                
'location'   => array('type' => self::TYPE_STRING'default' => '''maxLength' => 50),
                
'occupation' => array('type' => self::TYPE_STRING'default' => '''maxLength' => 50),
                
'following'  => array('type' => self::TYPE_STRING'default' => '''verification' => array('XenForo_DataWriter_Helper_Denormalization''verifyIntCommaList')),
                
'ignored'    => array('type' => self::TYPE_SERIALIZED'default' => ''),
                
'csrf_token' => array('type' => self::TYPE_STRING'default' => ''),
                
'avatar_crop_x' => array('type' => self::TYPE_UINT'default' => 0),
                
'avatar_crop_y' => array('type' => self::TYPE_UINT'default' => 0),
                
'about'      => array('type' => self::TYPE_STRING'default' => '''verification' => array('$this''_verifyMessageLikeContent'), 'maxLength' => 20000),
                
'custom_fields' => array('type' => self::TYPE_SERIALIZED'default' => ''),
                
'external_auth' => array('type' => self::TYPE_SERIALIZED'default' => ''),
            ),
            
'xf_user_option' => array(
                
'user_id'
                    
=> array('type' => self::TYPE_UINT'default' => array('xf_user''user_id'), 'required' => true),
                
'show_dob_year'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 1),
                
'show_dob_date'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 1),
                
'content_show_signature'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 1),
                
'receive_admin_email'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 1),
                
'email_on_conversation'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 1),
                
'is_discouraged'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 0),
                
'default_watch_state'
                    
=> array('type' => self::TYPE_STRING'default' => '''allowedValues' => array('''watch_no_email''watch_email')),
                
'alert_optout'
                    
=> array('type' => self::TYPE_STRING'default' => ''),
                
'enable_rte'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 1),
                
'enable_flash_uploader'
                    
=> array('type' => self::TYPE_BOOLEAN'default' => 1),
            ),
            
'xf_user_privacy' => array(
                
'user_id'
                    
=> array('type' => self::TYPE_UINT'default' => array('xf_user''user_id'), 'required' => true),
                
'allow_view_profile'
                    
=> array('type' => self::TYPE_STRING'default' => 'everyone''verification' => array('$this''_verifyPrivacyChoice')),
                
'allow_post_profile'
                    
=> array('type' => self::TYPE_STRING'default' => 'everyone''verification' => array('$this''_verifyPrivacyChoice')),
                
'allow_send_personal_conversation'
                    
=> array('type' => self::TYPE_STRING'default' => 'everyone''verification' => array('$this''_verifyPrivacyChoice')),
                
'allow_view_identities'
                    
=> array('type' => self::TYPE_STRING'default' => 'everyone''verification' => array('$this''_verifyPrivacyChoice')),
                
'allow_receive_news_feed'
                    
=> array('type' => self::TYPE_STRING'default' => 'everyone''verification' => array('$this''_verifyPrivacyChoice')),
            ),
            
'xf_user_authenticate' => array(
                
'user_id'      => array('type' => self::TYPE_UINT,   'default' => array('xf_user''user_id'), 'required' => true),
                
'scheme_class' => array('type' => self::TYPE_STRING'required' => true'maxLength' => 75),
                
'data'         => array('type' => self::TYPE_BINARY'required' => true),
                
'remember_key' => array('type' => self::TYPE_STRING'default' => '')
            )
        );
    }

    
/**
     * Changes to the following fields will not be logged by the user change log system
     *
     * @var array
     */
    
static $changeLogIgnoreFields = array
    (
        
'user_id',
        
'password',
        
'style_id',
        
'language_id',
        
'display_style_group_id',
        
'permission_combination_id',
        
'message_count',
        
'alerts_unread',
        
'conversations_unread',
        
'last_activity',
        
'trophy_points',
        
'avatar_width',
        
'avatar_height',
        
'like_count',
        
'warning_points',

        
'status',
        
'status_date',
        
'status_profile_post_id',
        
'following',
        
'ignored',
        
'csrf_token',
        
'avatar_crop_x',
        
'avatar_crop_y',
        
'external_auth',

        
'alert_optout',

        
'scheme_class',
        
'remember_key',
    );

    
/**
    * Gets the actual existing data out of data that was passed in. See parent for explanation.
    *
    * @param mixed
    *
    * @return array|false
    */
    
protected function _getExistingData($data)
    {
        if (!
$userId $this->_getExistingPrimaryKey($data'user_id'))
        {
            return 
false;
        }

        if (!
$user $this->_getUserModel()->getFullUserById($userId))
        {
            return 
false;
        }

        
$returnData $this->getTablesDataFromArray($user);
        
$returnData['xf_user_authenticate'] = $this->_getUserModel()->getUserAuthenticationRecordByUserId($userId);

        return 
$returnData;
    }

    
/**
    * Gets SQL condition to update the existing record.
    *
    * @return string
    */
    
protected function _getUpdateCondition($tableName)
    {
        return 
'user_id = ' $this->_db->quote($this->getExisting('user_id'));
    }

    
/**
     * Gets the default options for this data writer.
     */
    
protected function _getDefaultOptions()
    {
        
$options XenForo_Application::get('options');

        return array(
            
self::OPTION_USERNAME_LENGTH_MIN => intval($options->get('usernameLength''min')),
            
self::OPTION_USERNAME_LENGTH_MAX => intval($options->get('usernameLength''max')),
            
self::OPTION_USERNAME_DISALLOWED_NAMES => preg_split('/r?n/'$options->get('usernameValidation''disallowedNames')),
            
self::OPTION_USERNAME_REGEX => $options->get('usernameValidation''matchRegex'),
            
self::OPTION_CUSTOM_TITLE_DISALLOWED => preg_split('/r?n/'$options->get('disallowedCustomTitles')),
            
self::OPTION_ADMIN_EDIT => false,
            
self::OPTION_ALLOW_DELETE_SELF => false,
            
self::OPTION_MESSAGE_MAX_LENGTH => $options->messageMaxLength,
            
self::OPTION_LOG_CHANGES => true,
        );
    }

    
/**
     * Set the full list of secondary user groups (IDs) the user belongs to.
     *
     * @param array $groups
     */
    
public function setSecondaryGroups(array $groups)
    {
        
$groups array_map('intval'$groups);
        
$groups array_unique($groups);
        
sort($groupsSORT_NUMERIC);

        
$zeroKey array_search(0$groups);
        if (
$zeroKey !== false)
        {
            unset(
$groups[$zeroKey]);
        }

        
$this->_secondaryGroups $groups;
    }

    
/**
     * Set the full list of alerts from which the user opts out.
     *
     * @param array $optOuts [alert1 => true, alert2 => true]
     */
    
public function setAlertOptOuts(array $optOuts)
    {
        
ksort($optOuts);
        
$optOuts array_keys($optOuts);

        
$userId $this->get('user_id');

        if (
$optOuts !== array_keys(preg_split('/s*,s*/'$this->getExisting('alert_optout'), -1PREG_SPLIT_NO_EMPTY)))
        {
            
$this->_db->delete('xf_user_alert_optout''user_id = ' $userId);

            foreach (
$optOuts AS $alert)
            {
                
$this->_db->insert('xf_user_alert_optout', array(
                    
'user_id' => $userId,
                    
'alert' => $alert
                
));
            }

            
$this->set('alert_optout'implode(','$optOuts));
        }
    }

    public function 
setCustomFields(array $fieldValues, array $fieldsShown null)
    {
        if (
$fieldsShown === null)
        {
            
// not passed - assume keys are all there
            
$fieldsShown array_keys($fieldValues);
        }

        
$fieldModel $this->_getFieldModel();
        
$fields $this->_getUserFieldDefinitions();

        if (
$this->get('user_id') && !$this->_importMode)
        {
            
$existingValues $fieldModel->getUserFieldValues($this->get('user_id'));
        }
        else
        {
            
$existingValues = array();
        }

        
$finalValues = array();

        foreach (
$fieldsShown AS $fieldId)
        {
            if (!isset(
$fields[$fieldId]))
            {
                continue;
            }

            
$field $fields[$fieldId];
            
$multiChoice = ($field['field_type'] == 'checkbox' || $field['field_type'] == 'multiselect');

            if (
$multiChoice)
            {
                
// multi selection - array
                
$value = array();
                if (isset(
$fieldValues[$fieldId]))
                {
                    if (
is_string($fieldValues[$fieldId]))
                    {
                        
$value = array($fieldValues[$fieldId]);
                    }
                    else if (
is_array($fieldValues[$fieldId]))
                    {
                        
$value $fieldValues[$fieldId];
                    }
                }
            }
            else
            {
                
// single selection - string
                
if (isset($fieldValues[$fieldId]))
                {
                    if (
is_array($fieldValues[$fieldId]))
                    {
                        
$value count($fieldValues[$fieldId]) ? strval(reset($fieldValues[$fieldId])) : '';
                    }
                    else
                    {
                        
$value strval($fieldValues[$fieldId]);
                    }
                }
                else
                {
                    
$value '';
                }
            }

            
$existingValue = (isset($existingValues[$fieldId]) ? $existingValues[$fieldId] : null);

            if (!
$this->_importMode)
            {
                if (!
$this->getOption(self::OPTION_ADMIN_EDIT))
                {
                    if (
$field['user_editable'] == 'never')
                    {
                        continue;
                    }
                    else if (
$field['user_editable'] == 'once')
                    {
                        if (
$existingValue !== null && $existingValue !== '' && $existingValue !== array())
                        {
                            continue;
                        }
                    }
                }

                
$valid $fieldModel->verifyUserFieldValue($field$value$error);
                if (!
$valid)
                {
                    
$this->error($error"custom_field_$fieldId");
                    continue;
                }

                if (!
$this->getOption(self::OPTION_ADMIN_EDIT) && $field['required'] && ($value === '' || $value === array()))
                {
                    
$this->error(new XenForo_Phrase('please_enter_value_for_all_required_fields'), "required");
                    continue;
                }
            }

            if (
$value !== $existingValue)
            {
                
$finalValues[$fieldId] = $value;
            }
        }

        
$this->_updateCustomFields $finalValues $this->_updateCustomFields;
        
$this->set('custom_fields'$finalValues $existingValues);
    }

    
/**
     * Sets the user's password.
     *
     * @param string $password
     * @param string|false $passwordConfirm If a string, ensures that the password and the confirm are the same
     * @param XenForo_Authentication_Abstract|null $auth Auth object to generate the password (or null to use default)
     * @param boolean If true, do not accept an empty password
     *
     * @return boolean
     */
    
public function setPassword($password$passwordConfirm falseXenForo_Authentication_Abstract $auth null$requirePassword false)
    {
        if (
$requirePassword && $password === '')
        {
            
$this->error(new XenForo_Phrase('please_enter_valid_password'), 'password');
            return 
false;
        }

        if (
$passwordConfirm !== false && $password !== $passwordConfirm)
        {
            
$this->error(new XenForo_Phrase('passwords_did_not_match'), 'password');
            return 
false;
        }

        if (!
$auth)
        {
            
$auth XenForo_Authentication_Abstract::createDefault();
        }

        
$authData $auth->generate($password);
        if (!
$authData)
        {
            
$this->error(new XenForo_Phrase('please_enter_valid_password'), 'password');
            return 
false;
        }

        
$this->set('scheme_class'$auth->getClassName());
        
$this->set('data'$authData'xf_user_authenticate');
        return 
true;
    }

    
/**
     * Advances the user state forward one step, following the registration rules.
     *
     * @param boolean $allowEmailConfirm
     */
    
public function advanceRegistrationUserState($allowEmailConfirm true)
    {
        
$options XenForo_Application::get('options');

        if (
$this->isInsert())
        {
            if (!
$this->get('user_state'))
            {
                if (
$options->get('registrationSetup''emailConfirmation') && $allowEmailConfirm)
                {
                    
$this->set('user_state''email_confirm');
                }
                else if (
$options->get('registrationSetup''moderation'))
                {
                    
$this->set('user_state''moderated');
                }
                else
                {
                    
$this->set('user_state''valid');
                }
            }
        }
        else
        {
            switch (
$this->get('user_state'))
            {
                case 
'email_confirm':
                    if (
$options->get('registrationSetup''moderation'))
                    {
                        
$this->set('user_state''moderated');
                        break;
                    }
                    
// otherwise, fall through

                
case 'email_confirm_edit'// this is a user editing email, never send back to moderation
                
case 'moderated':
                    
$this->set('user_state''valid');
                    break;
            }
        }
    }

    
/**
    * Verification callback to check that a username is valid
    *
    * @param string Username
    *
    * @return bool
    */
    
protected function _verifyUsername(&$username)
    {
        if (
$this->isUpdate() && $username === $this->getExisting('username'))
        {
            return 
true// unchanged, always pass
        
}

        
// standardize white space in names
        
$username preg_replace('/s+/u'' '$username);
        try
        {
            
// if this matches, then v isn't known (appears to be PCRE < 7.2) so don't strip
            
if (!preg_match('/v/''v'))
            {
                
$newName preg_replace('/v+/u'' '$username);
                if (
is_string($newName))
                {
                    
$username $newName;
                }
            }
        }
        catch (
Exception $e) {}

        
$username trim($username);

        
$usernameLength utf8_strlen($username);
        
$minLength $this->getOption(self::OPTION_USERNAME_LENGTH_MIN);
        
$maxLength $this->getOption(self::OPTION_USERNAME_LENGTH_MAX);

        if (!
$this->getOption(self::OPTION_ADMIN_EDIT))
        {
            if (
$minLength && $usernameLength $minLength)
            {
                
$this->error(new XenForo_Phrase('please_enter_name_that_is_at_least_x_characters_long', array('count' => $minLength)), 'username');
                return 
false;
            }
            if (
$maxLength && $usernameLength $maxLength)
            {
                
$this->error(new XenForo_Phrase('please_enter_name_that_is_at_most_x_characters_long', array('count' => $maxLength)), 'username');
                return 
false;
            }

            
$disallowedNames $this->getOption(self::OPTION_USERNAME_DISALLOWED_NAMES);
            if (
$disallowedNames)
            {
                foreach (
$disallowedNames AS $name)
                {
                    
$name trim($name);
                    if (
$name === '')
                    {
                        continue;
                    }
                    if (
stripos($username$name) !== false)
                    {
                        
$this->error(new XenForo_Phrase('please_enter_another_name_disallowed_words'), 'username');
                        return 
false;
                    }
                }
            }

            
$matchRegex $this->getOption(self::OPTION_USERNAME_REGEX);
            if (
$matchRegex)
            {
                
$matchRegex str_replace('#''\#'$matchRegex); // escape delim only
                
if (!preg_match('#' $matchRegex '#i'$username))
                {
                    
$this->error(new XenForo_Phrase('please_enter_another_name_required_format'), 'username');
                    return 
false;
                }
            }

            
$censoredUserName XenForo_Helper_String::censorString($username);
            if (
$censoredUserName !== $username)
            {
                
$this->error(new XenForo_Phrase('please_enter_name_that_does_not_contain_any_censored_words'), 'username');
                return 
false;
            }
        }

        
// ignore check if unicode properties aren't compiled
        
try
        {
            if (@
preg_match("/p{C}/u"$username))
            {
                
$this->error(new XenForo_Phrase('please_enter_name_without_using_control_characters'), 'username');
                return 
false;
            }
        }
        catch (
Exception $e) {}

        if (
strpos($username',') !== false)
        {
            
$this->error(new XenForo_Phrase('please_enter_name_that_does_not_contain_comma'), 'username');
            return 
false;
        }

        if (
XenForo_Helper_Email::isEmailValid($username))
        {
            
$this->error(new XenForo_Phrase('please_enter_name_that_does_not_resemble_an_email_address'), 'username');
            return 
false;
        }

        
$existingUser $this->_getUserModel()->getUserByName($username);
        if (
$existingUser && $existingUser['user_id'] != $this->get('user_id'))
        {
            
$this->error(new XenForo_Phrase('usernames_must_be_unique'), 'username');
            return 
false;
        }

        
// compare against romanized name to help reduce confusable issues
        
$romanized utf8_deaccent(utf8_romanize($username));
        if (
$romanized != $username)
        {
            
$existingUser $this->_getUserModel()->getUserByName($romanized);
            if (
$existingUser && $existingUser['user_id'] != $this->get('user_id'))
            {
                
$this->error(new XenForo_Phrase('usernames_must_be_unique'), 'username');
                return 
false;
            }
        }

        return 
true;
    }

    
/**
    * Verification callback to check the email address is in a valid form
    *
    * @param string Email Address
    *
    * @return bool
    */
    
protected function _verifyEmail(&$email)
    {
        if (
$this->isUpdate() && $email === $this->getExisting('email'))
        {
            return 
true;
        }

        if (
$this->getOption(self::OPTION_ADMIN_EDIT) && $email === '')
        {
            return 
true;
        }

        if (!
XenForo_Helper_Email::isEmailValid($email))
        {
            
$this->error(new XenForo_Phrase('please_enter_valid_email'), 'email');
            return 
false;
        }

        
$existingUser $this->_getUserModel()->getUserByEmail($email);
        if (
$existingUser && $existingUser['user_id'] != $this->get('user_id'))
        {
            
$this->error(new XenForo_Phrase('email_addresses_must_be_unique'), 'email');
            return 
false;
        }

        if (
XenForo_Helper_Email::isEmailBanned($email))
        {
            
$this->error(new XenForo_Phrase('email_address_you_entered_has_been_banned_by_administrator'), 'email');
            return 
false;
        }

        return 
true;
    }

    protected function 
_verifyMessageLikeContent(&$message$null$fieldName)
    {
        
$maxLength $this->getOption(self::OPTION_MESSAGE_MAX_LENGTH);
        if (
$maxLength && utf8_strlen($message) > $maxLength)
        {
            
$this->error(new XenForo_Phrase('please_enter_message_with_no_more_than_x_characters', array('count' => $maxLength)), $fieldName);
            return 
false;
        }

        return 
true;
    }

    protected function 
_verifyTimezone(&$timezone)
    {
        try
        {
            new 
DateTimeZone($timezone);
        }
        catch (
Exception $e)
        {
            
$this->error(new XenForo_Phrase('please_select_valid_time_zone'), 'timezone');
            return 
false;
        }

        return 
true;
    }

    protected function 
_verifyLanguageId(&$languageId)
    {
        if (
$languageId === || $this->getModelFromCache('XenForo_Model_Language')->getLanguageById($languageId))
        {
            return 
true;
        }

        
$languageId 0;
        return 
true;
    }

    protected function 
_verifyStyleId(&$styleId)
    {
        if (
$styleId === || $this->getModelFromCache('XenForo_Model_Style')->getStyleById($styleId))
        {
            return 
true;
        }

        
$styleId 0;
        return 
true;
    }

    
/**
     * Verifies that a gravatar email address is valid, or empty
     *
     * @param string $gravatarEmail
     *
     * @return boolean
     */
    
protected function _verifyGravatar(&$gravatarEmail)
    {
        if (
$gravatarEmail !== '' && !XenForo_Helper_Email::isEmailValid($gravatarEmail))
        {
            
$this->error(new XenForo_Phrase('please_enter_valid_email'), 'gravatar');
            return 
false;
        }

        return 
true;
    }

    
/**
     * Validates a custom user title, checking for blocked terms, banned markup etc.
     *
     * @param string $title
     *
     * @return boolean
     */
    
protected function _verifyCustomTitle(&$title)
    {
        if (!
$this->getOption(self::OPTION_ADMIN_EDIT))
        {
            if (
$title === $this->getExisting('custom_title'))
            {
                return 
true// can always keep the existing value
            
}

            if (
$title !== XenForo_Helper_String::censorString($title))
            {
                
$this->error(new XenForo_Phrase('please_enter_custom_title_that_does_not_contain_any_censored_words'), 'custom_title');
                return 
false;
            }

            
$disallowed $this->getOption(self::OPTION_CUSTOM_TITLE_DISALLOWED);
            if (
$disallowed && !$this->get('is_moderator') && !$this->get('is_admin'))
            {
                foreach (
$disallowed AS $value)
                {
                    
$value trim($value);
                    if (
$value === '')
                    {
                        continue;
                    }
                    if (
stripos($title$value) !== false)
                    {
                        
$this->error(new XenForo_Phrase('please_enter_another_custom_title_disallowed_words'), 'custom_title');
                        return 
false;
                    }
                }
            }
        }

        return 
true;
    }

    
/**
     * Verification callback for privacy choice field
     * Valid:
     * * everyone
     * * members
     * * followed
     * * none
     *
     * @param string Choice
     *
     * @return boolean
     */
    
protected function _verifyPrivacyChoice(&$choice$dw$fieldName)
    {
        if (!
in_array(strtolower($choice), array('everyone''members''followed''none')))
        {
            
$choice 'none';
        }

        return 
true;
    }

    
/**
     * Pre-save default setting.
     */
    
protected function _preSaveDefaults()
    {
        if (
is_array($this->_secondaryGroups))
        {
            
$primaryGroupKey array_search($this->get('user_group_id'), $this->_secondaryGroups);
            if (
$primaryGroupKey !== false)
            {
                unset(
$this->_secondaryGroups[$primaryGroupKey]);
            }

            
$this->set('secondary_group_ids'implode(','$this->_secondaryGroups));
        }

        if (
$this->isChanged('scheme_class''xf_user_authenticate') || $this->isChanged('data''xf_user_authenticate'))
        {
            
$this->set('remember_key'XenForo_Application::generateRandomString(40));
        }

        if (!
$this->get('csrf_token'))
        {
            
$this->set('csrf_token'XenForo_Application::generateRandomString(40));
        }
    }

    
/**
     * Pre-save handling.
     */
    
protected function _preSave()
    {
        
$this->checkDob();

        if (!
$this->get('scheme_class''xf_user_authenticate') || !$this->get('data''xf_user_authenticate'))
        {
            
$this->error(new XenForo_Phrase('please_enter_valid_password'), 'password'false);

            
// prevent required errors
            
$this->set('scheme_class''invalid');
            
$this->set('data''invalid''xf_user_authenticate');
        }

        if (!
$this->get('language_id'))
        {
            
$this->set('language_id'XenForo_Application::get('options')->defaultLanguageId);
        }

        if (
$this->isUpdate() && $this->isChanged('email')
            && 
$this->get('user_state') == 'email_bounce' && !$this->isChanged('user_state')
        )
        {
            if (
XenForo_Application::get('options')->get('registrationSetup''emailConfirmation'))
            {
                
$this->set('user_state''email_confirm_edit');
            }
            else
            {
                
$this->set('user_state''valid');
            }
        }
    }

    
/**
     * Checks that the date of birth entered is valid. If not entered or not changed,
     * it's valid.
     *
     * @return boolean
     */
    
public function checkDob()
    {
        if (
$this->isChanged('dob_day') || $this->isChanged('dob_month') || $this->isChanged('dob_year'))
        {
            if (!
$this->get('dob_day') || !$this->get('dob_month'))
            {
                
$this->set('dob_day'0);
                
$this->set('dob_month'0);
                
$this->set('dob_year'0);
            }
            else
            {
                
$year $this->get('dob_year');
                if (!
$year)
                {
                    
$year 2008// pick a leap year to be sure
                
}
                else if (
$year 100)
                {
                    
$year += ($year 30 2000 1900);
                    
$this->set('dob_year'$year);
                }

                if (
$year intval(date('Y'))
                    || 
$year 1900
                    
|| !checkdate($this->get('dob_month'), $this->get('dob_day'), $year)
                    || 
gmmktime(000$this->get('dob_month'), $this->get('dob_day'), $year) > XenForo_Application::$time 86400 // +1 day to be careful with TZs ahead of GMT
                
)
                {
                    if (
$this->_importMode)
                    {
                        
// don't error, wipe it out
                        
$this->set('dob_day'0);
                        
$this->set('dob_month'0);
                        
$this->set('dob_year'0);
                    }
                    else
                    {
                        
$this->error(new XenForo_Phrase('please_enter_valid_date_of_birth'), 'dob');
                    }

                    return 
false;
                }
            }
        }

        return 
true;
    }

    
/**
     * List of denormalised username fields in tables that should
     * be updated in the event that a username is changed.
     *
     * @var array [query-key => [tablename, username-field, user-id-field]]
     */
    
public static $usernameChangeUpdates = array();

    
/**
     * Post-save handling.
     */
    
protected function _postSave()
    {
        
$db $this->_db;
        
$userId $this->get('user_id');

        if (
$this->isChanged('secondary_group_ids') || $this->isChanged('user_group_id'))
        {
            
// TODO: update user group change list to remove any group IDs that have been removed
            
$this->rebuildUserGroupRelations();
        }

        if (
$this->isChanged('user_group_id') || $this->isChanged('secondary_group_ids'))
        {
            
$this->rebuildPermissionCombinationId();

            if (!
$this->isChanged('display_style_group_id') || !$this->get('display_style_group_id'))
            {
                
$this->rebuildDisplayStyleGroupId();
            }
        }

        
$this->updateCustomFields();

        if (
$this->isUpdate())
        {
            if (
$this->isChanged('warning_points'))
            {
                
$this->getModelFromCache('XenForo_Model_Warning')->userWarningPointsChanged(
                    
$this->get('user_id'), $this->get('warning_points'), $this->getExisting('warning_points')
                );
            }

            
// publish events to news feed
            
$this->_publishIfChanged('location');
            
$this->_publishIfChanged('occupation');
            
$this->_publishIfChanged('homepage');

            
$this->_publishIfAvatarChanged();

            
//TODO: handle publishing custom fields

            // handle username change updates
            
if ($this->isChanged('username') && XenForo_Application::get('options')->updateContentOnUsernameChange)
            {
                
$this->_getUserModel()->changeContentUser($userId$this->get('username'), $this->getExisting('username'));
            }

            if (
$this->isChanged('signature'))
            {
                
$this->getModelFromCache('XenForo_Model_BbCode')->deleteBbCodeParseCacheForContent(
                    
'signature'$this->get('user_id')
                );
            }
        }

        if (
$this->isChanged('user_state') && ($this->get('user_state') == 'moderated' || $this->getExisting('user_state') == 'moderated')) {
            
$this->getModelFromCache('XenForo_Model_User')->rebuildUserModerationQueueCache();
        }

        
// user change logging, only on regular updates
        
if ($this->isUpdate() && !$this->_importMode && $this->getOption(self::OPTION_LOG_CHANGES))
        {
            if (
$changes $this->getNewData())
            {
                
$changedFields = array();

                foreach (
$changes AS $table => $fields)
                {
                    foreach (
$fields AS $field => $newValue)
                    {
                        if (!
in_array($fieldself::$changeLogIgnoreFields))
                        {
                            
$oldValue $this->getExisting($field$table);

                            if (
$oldValue != $newValue)
                            {
                                if (
$table == 'xf_user_authenticate' && $field == 'data')
                                {
                                    
$oldValue '*****';
                                    
$newValue '*****';
                                }

                                
$changedFields[$field] = array($oldValue$newValue);
                            }
                        }
                    }
                }

                if (!empty(
$changedFields))
                {
                    
$this->_getUserModel()->logChanges($userId$changedFields);
                }
            }
        }
    }

    public function 
rebuildUserGroupRelations()
    {
        
$db $this->_db;
        
$userId intval($this->get('user_id'));
        
$userIdQuoted $db->quote($userId);

        
$inserts = array();
        
$inserts[] = '(' $userIdQuoted ', ' $db->quote($this->get('user_group_id')) . ', 1)';

        
$secondaryGroups = ($this->get('secondary_group_ids') ? explode(','$this->get('secondary_group_ids')) : array());
        foreach (
$secondaryGroups AS $groupId)
        {
            
$inserts[] = '(' $userIdQuoted ', ' $db->quote($groupId) . ', 0)';
        }

        
$db->delete('xf_user_group_relation''user_id = ' $userIdQuoted);
        
$db->query('
            INSERT INTO xf_user_group_relation
                (user_id, user_group_id, is_primary)
            VALUES
                ' 
implode(','$inserts) . '
            ON DUPLICATE KEY UPDATE
                is_primary = VALUES(is_primary)
        '
);
    }

    public function 
rebuildPermissionCombinationId($checkForUserPerms true)
    {
        
$combinationId $this->_getPermissionModel()->updateUserPermissionCombination(
            
$this->getMergedData(), true$checkForUserPerms
        
);
        
$this->_setPostSave('permission_combination_id'$combinationId);
    }

    public function 
rebuildDisplayStyleGroupId()
    {
        
$db $this->_db;

        
$groups = ($this->get('secondary_group_ids') ? explode(','$this->get('secondary_group_ids')) : array());
        
$groups[] = $this->get('user_group_id');

        
$displayStyleGroupId $this->getModelFromCache('XenForo_Model_UserGroup')->getDisplayStyleGroupIdForCombination($groups);

        if (
$displayStyleGroupId !== $this->get('display_style_group_id'))
        {
            
$this->_setPostSave('display_style_group_id'$displayStyleGroupId);
            
$db->update('xf_user',
                array(
'display_style_group_id' => $displayStyleGroupId),
                
'user_id = ' $db->quote($this->get('user_id'))
            );
        }
    }

    public function 
rebuildCustomFields()
    {
        
$this->_getUserModel()->rebuildCustomFieldCache($this->get('user_id'));
    }

    public function 
rebuildIgnoreCache()
    {
        
$users $this->_getUserIgnoreModel()->getUserIgnoreCache($this->get('user_id'));
        
$this->_setPostSave('ignored'$users);

        
$db $this->_db;
        
$db->update('xf_user_profile',
            array(
'ignored' => serialize($users)),
            
'user_id = ' $db->quote($this->get('user_id'))
        );
    }

    public function 
updateCustomFields()
    {
        if (
$this->_updateCustomFields)
        {
            
$userId $this->get('user_id');

            foreach (
$this->_updateCustomFields AS $fieldId => $value)
            {
                if (
is_array($value))
                {
                    
$value serialize($value);
                }
                
$this->_db->query('
                    INSERT INTO xf_user_field_value
                        (user_id, field_id, field_value)
                    VALUES
                        (?, ?, ?)
                    ON DUPLICATE KEY UPDATE
                        field_value = VALUES(field_value)
                '
, array($userId$fieldId$value));
            }
        }
    }

    protected function 
_publishIfAvatarChanged()
    {
        
// avatar
        
$publishAvatarChange false;

        
/*
         * Logic: publish if:
         * gravatar has changed and gravatar is not empty
         * or
         * gravatar has changed and become empty but avatar_date is non-zero
         * or
         * avatar_date has changed and last change was > 24 hours ago
         */

        // if gravatar has changed value
        
if ($this->isChanged('gravatar'))
        {
            
// if gravatar is not empty or avatar_date is non-zero
            
if ($this->getNew('gravatar') != '' || $this->get('avatar_date'))
            {
                
$publishAvatarChange true;
            }
        }
        else if (
$this->isChanged('avatar_date'))
        {
            if (
$this->get('avatar_date') - $this->getExisting('avatar_date') > 86400)
            {
                
$publishAvatarChange true;
            }
        }

        if (
$publishAvatarChange)
        {
            
$this->_publish('avatar_change');
        }
    }

    
/**
     * Wrapper around _publish. Will publish 'old' and 'new' versions of the field
     * specified if they are different, provided that 'new' is not empty.
     *
     * @param string $fieldName
     */
    
protected function _publishIfChanged($fieldName)
    {
        if (
$this->isChanged($fieldName) && $newValue $this->get($fieldName))
        {
            
$this->_publish($fieldName, array(
                
'old' => $this->getExisting($fieldName),
                
'new' => $newValue
            
));
        }
    }

    
/**
     * Publish an item to the news feed
     *
     * @param string action
     * @param mixed extra data
     */
    
protected function _publish($action$extraData null)
    {
        if (
$this->get('user_id') != XenForo_Visitor::getUserId())
        {
            return;
        }

        
$this->_getNewsFeedModel()->publish(
            
$this->get('user_id'),
            
$this->get('username'),
            
'user',
            
0,
            
$action,
            
$extraData
        
);
    }

    protected function 
_preDelete()
    {
        if (!
$this->getOption(self::OPTION_ALLOW_DELETE_SELF))
        {
            if (
$this->get('user_id') == XenForo_Visitor::getUserId())
            {
                
$this->error(new XenForo_Phrase('you_cannot_delete_your_own_account'));
            }
        }
    }

    
/**
     * Post-delete handling.
     */
    
protected function _postDelete()
    {
        
$db $this->_db;
        
$userId $this->get('user_id');
        
$userIdQuoted $db->quote($userId);

        
$db->delete('xf_user_alert'"alerted_user_id = $userIdQuoted");
        
$db->delete('xf_user_alert_optout'"user_id = $userIdQuoted");
        
$db->delete('xf_user_change_log'"user_id = $userIdQuoted");
        
$db->delete('xf_user_change_temp'"user_id = $userIdQuoted");
        
$db->delete('xf_user_confirmation'"user_id = $userIdQuoted");
        
$db->delete('xf_user_external_auth'"user_id = $userIdQuoted");
        
$db->delete('xf_user_field_value'"user_id = $userIdQuoted");
        
$db->delete('xf_user_follow'"follow_user_id = $userIdQuoted");
        
$db->delete('xf_user_follow'"user_id = $userIdQuoted");
        
$db->delete('xf_user_group_change'"user_id = $userIdQuoted");
        
$db->delete('xf_user_group_promotion_log'"user_id = $userIdQuoted");
        
$db->delete('xf_user_group_relation'"user_id = $userIdQuoted");
        
$db->delete('xf_user_ignored'"ignored_user_id = $userIdQuoted");
        
$db->delete('xf_user_ignored'"user_id = $userIdQuoted");
        
$db->delete('xf_user_news_feed_cache'"user_id = $userIdQuoted");
        
$db->delete('xf_user_trophy'"user_id = $userIdQuoted");
        
$db->delete('xf_user_upgrade_active'"user_id = $userIdQuoted");
        
$db->delete('xf_user_upgrade_expired'"user_id = $userIdQuoted");
        
$db->delete('xf_warning'"user_id = $userIdQuoted");

        
$db->delete('xf_permission_combination'"user_id = $userIdQuoted");
        
$db->delete('xf_permission_entry'"user_id = $userIdQuoted");
        
$db->delete('xf_permission_entry_content'"user_id = $userIdQuoted");

        if (
$this->get('is_moderator'))
        {
            
$db->delete('xf_moderator',  "user_id = $userIdQuoted");
            
$db->delete('xf_moderator_content',  "user_id = $userIdQuoted");
        }
        if (
$this->get('is_admin'))
        {
            
$db->delete('xf_admin',  "user_id = $userIdQuoted");
            
$db->delete('xf_admin_permission_entry',  "user_id = $userIdQuoted");
        }
        if (
$this->get('is_banned'))
        {
            
$db->delete('xf_user_ban',  "user_id = $userIdQuoted");
        }

        
$db->delete('xf_profile_post'"profile_user_id = $userIdQuoted");

        
$db->delete('xf_news_feed'"user_id = $userIdQuoted");

        
$db->delete('xf_conversation_user'"owner_user_id = $userIdQuoted");
        
// note: leaving records in conversation recipient to keep data somewhat intact for others

        
$db->delete('xf_ip'"content_type = 'user' AND user_id = $userIdQuoted");
        
// note: leaving content-associated IPs

        
$db->delete('xf_forum_read'"user_id = $userIdQuoted");
        
$db->delete('xf_thread_read'"user_id = $userIdQuoted");
        
$db->delete('xf_forum_watch'"user_id = $userIdQuoted");
        
$db->delete('xf_thread_watch'"user_id = $userIdQuoted");

        
$this->getModelFromCache('XenForo_Model_Avatar')->deleteAvatar($userIdfalse);

        if (
$this->get('user_state') == 'moderated')
        {
            
$this->getModelFromCache('XenForo_Model_User')->rebuildUserModerationQueueCache();
        }

        
$this->getModelFromCache('XenForo_Model_BbCode')->deleteBbCodeParseCacheForContent(
            
'signature'$this->get('user_id')
        );

        
$this->_getUserModel()->changeContentUser($userId$this->get('username'), null0);
    }

    
/**
     * @return XenForo_Model_Permission
     */
    
protected function _getPermissionModel()
    {
        return 
$this->getModelFromCache('XenForo_Model_Permission');
    }

    
/**
     * Fetch (and cache) user field definitions
     *
     * @return array
     */
    
protected function _getUserFieldDefinitions()
    {
        
$fields $this->getExtraData(self::DATA_USER_FIELD_DEFINITIONS);

        if (
is_null($fields))
        {
            
$fields $this->_getFieldModel()->getUserFields();

            
$this->setExtraData(self::DATA_USER_FIELD_DEFINITIONS$fields);
        }

        return 
$fields;
    }

    
/**
     * @return XenForo_Model_UserField
     */
    
protected function _getFieldModel()
    {
        return 
$this->getModelFromCache('XenForo_Model_UserField');
    }
}
Онлайн: 1
Реклама