Visman пре 5 година
родитељ
комит
4205a69c3e

+ 14 - 0
app/Models/DataModel.php

@@ -14,6 +14,12 @@ class DataModel extends Model
      */
     protected $modified = [];
 
+    /**
+     * Массив состояний отслеживания изменений в свойствах модели
+     * @var array
+     */
+    protected $track = [];
+
     /**
      * Устанавливает значения для свойств
      * Флаги модификации свойст сброшены
@@ -27,6 +33,7 @@ class DataModel extends Model
         $this->a        = $attrs; //????
         $this->aCalc    = [];
         $this->modified = [];
+        $this->track    = [];
         return $this;
     }
 
@@ -78,6 +85,7 @@ class DataModel extends Model
     public function resModified()
     {
         $this->modified = [];
+        $this->track    = [];
     }
 
     /**
@@ -105,8 +113,12 @@ class DataModel extends Model
             }
         }
 
+        $this->track[$name] = $track;
+
         parent::__set($name, $val);
 
+        unset($this->track[$name]);
+
         if (null === $track) {
             return;
         }
@@ -127,9 +139,11 @@ class DataModel extends Model
      */
     public function __get($name)
     {
+        // без вычисления
         if (\strpos($name, '__') === 0) {
             $name = \substr($name, 2);
             return isset($this->a[$name]) ? $this->a[$name] : null;
+        // с вычислениями
         } else {
             return parent::__get($name);
         }

+ 1 - 1
app/Models/Pages/Admin/Users.php

@@ -102,7 +102,7 @@ abstract class Users extends Admin
             $this->rules = $this->c->UsersRules->init();
         }
 
-        $userList = $this->c->users->load($selected);
+        $userList = $this->c->users->load(...$selected);
         $result   = [];
         foreach ($userList as $user) {
             if (! $user instanceof User) {

+ 1 - 1
app/Models/Pages/Admin/Users/Action.php

@@ -82,7 +82,7 @@ class Action extends Users
             return $message;
         }
 
-        $this->userList = $this->c->users->load($ids);
+        $this->userList = $this->c->users->load(...$ids);
         switch ($args['action']) {
             case self::ACTION_BAN:
                 return $this->ban($args, $method);

+ 27 - 5
app/Models/Pages/Admin/Users/Result.php

@@ -4,6 +4,7 @@ namespace ForkBB\Models\Pages\Admin\Users;
 
 use ForkBB\Core\Validator;
 use ForkBB\Models\Pages\Admin\Users;
+use ForkBB\Models\User\Model as User;
 
 class Result extends Users
 {
@@ -29,14 +30,14 @@ class Result extends Users
                 return $this->c->Message->message('Bad request');
             }
 
-            $ids    = $this->forIP($data['ip']);
+            $idsN   = $this->forIP($data['ip']);
             $crName = $data['ip'];
         } else {
-            $ids    = $this->forFilter($data);
+            $idsN   = $this->forFilter($data);
             $crName = \ForkBB\__('Results head');
         }
 
-        $number = \count($ids);
+        $number = \count($idsN);
         if (0 == $number) {
             $view = $this->c->AdminUsers;
             $view->fIswev = ['i', \ForkBB\__('No users found')];
@@ -96,8 +97,29 @@ class Result extends Users
         }
 
         $startNum = ($page - 1) * $this->c->config->o_disp_users;
-        $ids      = \array_slice($ids, $startNum, $this->c->config->o_disp_users);
-        $userList = $this->c->users->load($ids);
+        $idsN     = \array_slice($idsN, $startNum, $this->c->config->o_disp_users);
+        $ids      = [];
+        $userList = [];
+
+        foreach ($idsN as $cur) {
+            if (\is_int($cur)) {
+                $ids[] = $cur;
+            }
+            $userList[$cur] = $cur;
+        }
+
+        if (! empty($ids)) {
+            $idsN = $this->c->users->load(...$ids);
+            if (! \is_array($idsN)) {
+                $idsN = [$idsN];
+            }
+
+            foreach ($idsN as $cur)  {
+                if ($cur instanceof User) {
+                    $userList[$cur->id] = $cur;
+                }
+            }
+        }
 
         $this->nameTpl    = 'admin/users_result';
         $this->mainSuffix = '-one-column';

+ 2 - 2
app/Models/Pages/Auth.php

@@ -147,7 +147,7 @@ class Auth extends Page
     public function vLoginProcess(Validator $v, $password)
     {
         if (! empty($v->getErrors())) {
-        } elseif (! ($user = $this->c->users->load($v->username, 'username')) instanceof User
+        } elseif (! ($user = $this->c->users->load($this->c->users->create(['username' => $v->username]))) instanceof User
             || $user->isGuest
         ) {
             $v->addError('Wrong user/pass');
@@ -309,7 +309,7 @@ class Auth extends Page
     public function changePass(array $args, $method)
     {
         if (! \hash_equals($args['hash'], $this->c->Secury->hash($args['email'] . $args['key']))
-            || ! ($user = $this->c->users->load($args['email'], 'email')) instanceof User
+            || ! ($user = $this->c->users->load($this->c->users->create(['email' => $args['email']]))) instanceof User
             || $user->isGuest
             || empty($user->activate_string)
             || ! \hash_equals($user->activate_string, $args['key'])

+ 9 - 8
app/Models/Pages/Install.php

@@ -141,7 +141,7 @@ class Install extends Page
                     'dbprefix'     => 'string:trim|max:40|check_prefix',
                     'username'     => 'required|string:trim|min:2|max:25',
                     'password'     => 'required|string|min:16|password',
-                    'email'        => 'required|string:trim,lower|max:80|email',
+                    'email'        => 'required|string:trim|max:80|email',
                     'title'        => 'required|string:trim|max:255',
                     'descr'        => 'string:trim|max:65000 bytes',
                     'baseurl'      => 'required|string:trim|rtrim_url',
@@ -498,7 +498,7 @@ class Install extends Page
                 'id'          => ['SERIAL', false],
                 'username'    => ['VARCHAR(190)', false, ''],
                 'ip'          => ['VARCHAR(255)', false, ''],
-                'email'       => ['VARCHAR(80)', false, ''],
+                'email'       => ['VARCHAR(190)', false, ''],
                 'message'     => ['VARCHAR(255)', false, ''],
                 'expire'      => ['INT(10) UNSIGNED', false, 0],
                 'ban_creator' => ['INT(10) UNSIGNED', false, 0],
@@ -653,7 +653,7 @@ class Install extends Page
                 'poster'       => ['VARCHAR(190)', false, ''],
                 'poster_id'    => ['INT(10) UNSIGNED', false, 1],
                 'poster_ip'    => ['VARCHAR(45)', false, ''],
-                'poster_email' => ['VARCHAR(80)', false, ''],
+                'poster_email' => ['VARCHAR(190)', false, ''],
                 'message'      => ['MEDIUMTEXT', false, ''],
                 'hide_smilies' => ['TINYINT(1)', false, 0],
                 'edit_post'    => ['TINYINT(1)', false, 0],
@@ -868,7 +868,8 @@ class Install extends Page
                 'group_id'         => ['INT(10) UNSIGNED', false, 0],
                 'username'         => ['VARCHAR(190)', false, ''],
                 'password'         => ['VARCHAR(255)', false, ''],
-                'email'            => ['VARCHAR(80)', false, ''],
+                'email'            => ['VARCHAR(190)', false, ''],
+                'email_normal'     => ['VARCHAR(190)', false, ''],
                 'email_confirmed'  => ['TINYINT(1)', false, 0],
                 'title'            => ['VARCHAR(50)', false, ''],
                 'realname'         => ['VARCHAR(40)', false, ''],
@@ -920,8 +921,8 @@ class Install extends Page
             ],
             'PRIMARY KEY' => ['id'],
             'UNIQUE KEYS' => [
-                'username_idx' => ['username(25)'],
-                'email_idx'    => ['email'],
+                'username_idx'     => ['username(25)'],
+                'email_normal_idx' => ['email_normal'],
             ],
             'INDEXES' => [
                 'registered_idx' => ['registered'],
@@ -1035,8 +1036,8 @@ class Install extends Page
         $this->c->DB->exec('UPDATE ::groups SET g_pm_limit=0 WHERE g_id=?i', [$this->c->GROUP_ADMIN]);
 
         $ip = \filter_var($_SERVER['REMOTE_ADDR'], \FILTER_VALIDATE_IP) ?: 'unknow';
-        $this->c->DB->exec('INSERT INTO ::users (group_id, username, password, email) VALUES (?i, ?s, ?s, ?s)', [$this->c->GROUP_GUEST, \ForkBB\__('Guest '), \ForkBB\__('Guest '), \ForkBB\__('Guest ')]);
-        $this->c->DB->exec('INSERT INTO ::users (group_id, username, password, email, language, style, num_posts, last_post, registered, registration_ip, last_visit) VALUES (?i, ?s, ?s, ?s, ?s, ?s, ?i, ?i, ?i, ?s, ?i)', [$this->c->GROUP_ADMIN, $v->username, password_hash($v->password, \PASSWORD_DEFAULT), $v->email, $v->defaultlang, $v->defaultstyle, 1, $now, $now, $ip, $now]);
+        $this->c->DB->exec('INSERT INTO ::users (group_id, username, password) VALUES (?i, ?s, ?s)', [$this->c->GROUP_GUEST, \ForkBB\__('Guest '), \ForkBB\__('Guest ')]);
+        $this->c->DB->exec('INSERT INTO ::users (group_id, username, password, email, email_normal, language, style, num_posts, last_post, registered, registration_ip, last_visit) VALUES (?i, ?s, ?s, ?s, ?s, ?s, ?s, ?i, ?i, ?i, ?s, ?i)', [$this->c->GROUP_ADMIN, $v->username, password_hash($v->password, \PASSWORD_DEFAULT), $v->email, $this->c->NormEmail->normalize($v->email), $v->defaultlang, $v->defaultstyle, 1, $now, $now, $ip, $now]);
 
         $pun_config = [
             'i_fork_revision'         => $this->c->FORK_REVISION,

+ 1 - 1
app/Models/Pages/Message.php

@@ -18,7 +18,7 @@ class Message extends Page
     public function message($message, $back = true, $status = 404, array $headers = [])
     {
         $this->nameTpl     = 'message';
-        $this->httpStatus  = $status;
+        $this->httpStatus  = \max(200, $status);
         $this->httpHeaders = $headers;
         $this->titles      = \ForkBB\__('Info');
         $this->back        = $back;

+ 1 - 1
app/Models/Pages/Register.php

@@ -24,7 +24,7 @@ class Register extends Page
                 'token'    => 'token:RegisterForm',
                 'agree'    => 'required|token:Register',
                 'on'       => 'integer',
-                'email'    => 'required_with:on|string:trim,lower|email:noban,unique',
+                'email'    => 'required_with:on|string:trim|email:noban,unique',
                 'username' => 'required_with:on|string:trim,spaces|username',
                 'password' => 'required_with:on|string|min:16|password',
             ])->addAliases([

+ 1 - 1
app/Models/Pages/Userlist.php

@@ -92,7 +92,7 @@ class Userlist extends Page
         if ($number) {
             $this->startNum = ($page - 1) * $this->c->config->o_disp_users;
             $ids = \array_slice($ids, $this->startNum, $this->c->config->o_disp_users);
-            $this->userList = $this->c->users->load($ids);
+            $this->userList = $this->c->users->load(...$ids);
 
             $links = [];
             $vars = ['page' => $page];

+ 3 - 2
app/Models/Post/View.php

@@ -177,7 +177,8 @@ class View extends Action
             }
         }
 
-        $this->c->users->load(\array_keys($userIds));
+        $ids = \array_keys($userIds);
+        $this->c->users->load(...$ids);
 
         $offset    = ($arg->page - 1) * $this->c->user->disp_posts;
         $timeMax   = 0;
@@ -191,7 +192,7 @@ class View extends Action
 
         if ($arg instanceof Topic) {
             foreach ($result as $post) {
-                if ($post->id === $arg->last_post_id) { // время последнего сообщения в теме может равняться 
+                if ($post->id === $arg->last_post_id) { // время последнего сообщения в теме может равняться
                     $timeMax = $arg->last_post;         // времени его редактирования, а не создания
                 } elseif ($post->posted > $timeMax) {
                     $timeMax = $post->posted;

+ 22 - 36
app/Models/User/Load.php

@@ -3,46 +3,41 @@
 namespace ForkBB\Models\User;
 
 use ForkBB\Models\Action;
+use ForkBB\Models\User\Model as User;
 use InvalidArgumentException;
 
 class Load extends Action
 {
     /**
-     * Получение пользователя по условию
+     * Получение пользователя
      *
      * @param mixed $value
-     * @param string $field
      *
      * @throws InvalidArgumentException
      *
      * @return mixed
      */
-    public function load($value, $field = 'id')
+    public function load($value)
     {
-        $flag = \is_array($value);
-
-        switch (($flag ? 'a_' : '') . $field) {
-            case 'id':
-                $where = 'u.id=?i:field';
-                break;
-            case 'username':
+        if (\is_array($value)) {
+            $where = 'u.id IN (?ai:field)';
+        } elseif ($value instanceof User) {
+            if ('' != $value->username) {
                 $where = 'u.username=?s:field';
-                break;
-            case 'email':
+                $value = $value->username;
+            } elseif ('' != $value->email) {
                 $where = 'u.email=?s:field';
-                break;
-            case 'a_id':
-                $where = 'u.id IN (?ai:field)';
-                break;
-            case 'a_username':
-                $where = 'u.username IN (?as:field)';
-                break;
-            case 'a_email':
-                $where = 'u.email IN (?as:field)';
-                break;
-            default:
+                $value = $value->email;
+            } elseif ('' != $value->email_normal) {
+                $where = 'u.email_normal=?s:field';
+                $value = $value->email_normal;
+            } else {
                 throw new InvalidArgumentException('Field not supported');
+            }
+        } else {
+            $where = 'u.id=?i:field';
         }
+
         $vars = [':field' => $value];
         $sql = 'SELECT u.*, g.*
                 FROM ::users AS u
@@ -51,19 +46,10 @@ class Load extends Action
 
         $data = $this->c->DB->query($sql, $vars)->fetchAll();
 
-        if ($flag) {
-            $result = [];
-            foreach ($data as $row) {
-                $result[] = $this->manager->create($row);
-            }
-            return $result;
-        } else {
-            $count = \count($data);
-            // число найденных пользователей отлично от одного
-            if (1 !== $count) {
-                return $count;
-            }
-            return $this->manager->create($data[0]);
+        $result = [];
+        foreach ($data as $row) {
+            $result[] = $this->manager->create($row);
         }
+        return $result;
     }
 }

+ 44 - 41
app/Models/User/Manager.php

@@ -4,7 +4,7 @@ namespace ForkBB\Models\User;
 
 use ForkBB\Models\ManagerModel;
 use ForkBB\Models\User\Model as User;
-use RuntimeException;
+use InvalidArgumentException;
 
 class Manager extends ManagerModel
 {
@@ -21,34 +21,55 @@ class Manager extends ManagerModel
     }
 
     /**
-     * Получение пользователя по условию
+     * Получение пользователя(ей) по id, массиву id или по модели User
      *
-     * @param mixed $value
-     * @param string $field
+     * @param mixed ...$args
+     *
+     * @throws InvalidArgumentException
      *
      * @return mixed
      */
-    public function load($value, $field = 'id')
+    public function load(...$args)
     {
-        if (\is_array($value)) {
-            $result = \array_flip($value); // ???? а если пользователь не найдется?
-            if ('id' === $field) {
-                $temp = [];
-                foreach ($value as $id) {
-                    if (\is_string($id)) { // ???? для пользователей из админки
-                        $result[$id] = $id;
-                    } elseif ($this->get($id) instanceof User) {
-                        $result[$id] = $this->get($id);
-                    } else {
-                        $temp[] = $id;
-                    }
+        $result = [];
+        $count = \count($args);
+        $countID = 0;
+        $countUser = 0;
+        $reqIDs = [];
+        $error = false;
+        $user = null;
+
+        foreach ($args as $arg) {
+            if ($arg instanceof User) {
+                ++$countUser;
+                $user = $arg;
+            } elseif (\is_int($arg) && $arg > 0) {
+                ++$countID;
+                if ($this->get($arg) instanceof User) {
+                    $result[$arg] = $this->get($arg);
+                } else {
+                    $result[$arg] = false;
+                    $reqIDs[] = $arg;
                 }
-                $value = $temp;
+            } else {
+                $error = true;
             }
-            if (empty($value)) {
-                return $result;
+        }
+
+        if ($error || $countUser * $countID > 0 || $countUser > 1 || ($countID > 0 && $count > $countID)) {
+            throw new InvalidArgumentException('Expected only integer, integer array or User');
+        }
+
+        if (! empty($reqIDs) || null !== $user) {
+            if (null !== $user) {
+                $data = $user;
+            } elseif (1 === \count($reqIDs)) {
+                $data = \array_pop($reqIDs);
+            } else {
+                $data = $reqIDs;
             }
-            foreach ($this->Load->load($value, $field) as $user) {
+
+            foreach ($this->Load->load($data) as $user) {
                 if ($user instanceof User) {
                     if ($this->get($user->id) instanceof User) {
                         $result[$user->id] = $this->get($user->id);
@@ -58,27 +79,9 @@ class Manager extends ManagerModel
                     }
                 }
             }
-
-            return $result;
-        } else {
-            $user = 'id' === $field ? $this->get($value) : null;
-
-            if (! $user instanceof User) {
-                $user = $this->Load->load($value, $field);
-
-                if ($user instanceof User) {
-                    $test = $this->get($user->id);
-
-                    if ($test instanceof User) {
-                        return $test;
-                    }
-
-                    $this->set($user->id, $user);
-                }
-            }
-
-            return $user;
         }
+
+        return 1 === \count($result) ? \array_pop($result) : $result;
     }
 
     /**

+ 15 - 0
app/Models/User/Model.php

@@ -365,4 +365,19 @@ class Model extends DataModel
             return null;
         }
     }
+
+    /**
+     * Установка email и вычисление нормализованного email
+     *
+     * @param string $email
+     */
+    protected function setemail($email)
+    {
+        $this->a['email'] = $email;
+
+        if (isset($email[0])) {
+            $property = (! isset($this->track['email']) ? '__' : '') . 'email_normal';
+            $this->$property = $this->c->NormEmail->normalize($email);
+        }
+    }
 }

+ 6 - 4
app/Models/Validators/Email.php

@@ -47,8 +47,8 @@ class Email extends Validators
             $ok = false;
         }
         // отсутствует пользователь с таким email (или их больше одного O_o)
-        if (isset($attrs['exists'])) {
-            $user = $this->c->users->load($email, 'email');
+        if ($ok && isset($attrs['exists'])) {
+            $user = $this->c->users->load($this->c->users->create(['email_normal' => $this->c->NormEmail->normalize($email)]));
 
             if (! $user instanceof User) {
                 $v->addError('Invalid email');
@@ -58,12 +58,14 @@ class Email extends Validators
         // email не уникален
         if ($ok && isset($attrs['unique']) && (! $originalUser instanceof User || ! $originalUser->isGuest)) {
             if (true === $user) {
-                $user = $this->c->users->load($email, 'email');
+                $user = $this->c->users->load($this->c->users->create(['email_normal' => $this->c->NormEmail->normalize($email)]));
             }
 
             $id = $originalUser instanceof User ? $originalUser->id : true;
 
-            if (($user instanceof User && $id !== $user->id) || (! $user instanceof User && 0 !== $user)) {
+            if (($user instanceof User && $id !== $user->id)
+                || (\is_array($user) && \count($user) > 1) // ???? эта ветка не реальна? поле email_normal уникально
+            ) {
                 $v->addError('Dupe email');
                 $ok = false;
             }

+ 1 - 0
app/config/install.php

@@ -82,6 +82,7 @@ return [
             'eol'   => '%EOL%',
         ],
         'Func' => \ForkBB\Core\Func::class,
+        'NormEmail' => \MioVisman\NormEmail\NormEmail::class,
         'Csrf' => [
             'class'  => \ForkBB\Core\Csrf::class,
             'Secury' => '@Secury',

+ 1 - 0
app/config/main.dist.php

@@ -84,6 +84,7 @@ return [
             'eol'   => '%EOL%',
         ],
         'Func' => \ForkBB\Core\Func::class,
+        'NormEmail' => \MioVisman\NormEmail\NormEmail::class,
 
         'config'     => '@ConfigModel:init',
         'bans'       => '@BanListModel:init',