Вход Регистрация
Файл: 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($groups, SORT_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'), -1, PREG_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 = false, XenForo_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 > 0 && $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 > 0 && $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 === 0 || $this->getModelFromCache('XenForo_Model_Language')->getLanguageById($languageId))
        {
            return 
true;
        }

        
$languageId = 0;
        return 
true;
    }

    protected function 
_verifyStyleId(&$styleId)
    {
        if (
$styleId === 0 || $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(0, 0, 0, $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($field, self::$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($userId, false);

        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'), null, 0);
    }

    
/**
     * @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');
    }
}
Онлайн: 2
Реклама