Parcourir la source

Merge pull request #5 from forkbb/delete_guest

Delete guest from users table
Visman il y a 3 ans
Parent
commit
bc2d56df2d

+ 1 - 1
app/Controllers/Install.php

@@ -37,7 +37,7 @@ class Install
         }
         $uri = \rawurldecode($uri);
 
-        $this->c->user = $this->c->users->create(['id' => 2, 'group_id' => FORK_GROUP_ADMIN]);
+        $this->c->user = $this->c->users->create(['id' => 1, 'group_id' => FORK_GROUP_ADMIN]);
         $this->c->Lang->load('common');
 
         $r = $this->c->Router;

+ 1 - 1
app/Controllers/Primary.php

@@ -42,7 +42,7 @@ class Primary
                 || ! isset($this->c->admins->list[$this->c->user->id])
             ) {
                 if (! $this->c->isInit('user')) {
-                    $this->c->user = $this->c->users->create(['id' => 1, 'group_id' => FORK_GROUP_GUEST]);
+                    $this->c->user = $this->c->users->create(['id' => 0, 'group_id' => FORK_GROUP_GUEST]);
                 }
 
                 return $this->c->Maintenance;

+ 14 - 14
app/Controllers/Routing.php

@@ -54,7 +54,7 @@ class Routing
             // смена кодовой фразы
             $r->add(
                 $r::DUO,
-                '/login/{id|i:[2-9]|[1-9]\d+}/{key}/{hash}',
+                '/login/{id|i:[1-9]\d*}/{key}/{hash}',
                 'Auth:changePass',
                 'ChangePassword'
             );
@@ -75,7 +75,7 @@ class Routing
                 );
                 $r->add(
                     $r::GET,
-                    '/registration/activate/{id|i:[2-9]|[1-9]\d+}/{key}/{hash}',
+                    '/registration/activate/{id|i:[1-9]\d*}/{key}/{hash}',
                     'Register:activate',
                     'RegActivate'
                 );
@@ -163,7 +163,7 @@ class Routing
 
                 $r->add(
                     $r::GET,
-                    '/search[/user/{uid|i:[2-9]|[1-9]\d+}]/{action:(?!search)[a-z_]+}[/in_forum/{forum|i:[1-9]\d*}][/{page|i:[1-9]\d*}]',
+                    '/search[/user/{uid|i:[1-9]\d*}]/{action:(?!search)[a-z_]+}[/in_forum/{forum|i:[1-9]\d*}][/{page|i:[1-9]\d*}]',
                     'Search:action',
                     'SearchAction'
                 );
@@ -185,37 +185,37 @@ class Routing
                 // юзеры
                 $r->add(
                     $r::GET,
-                    '/user/{id|i:[2-9]|[1-9]\d+}/{name}',
+                    '/user/{id|i:[1-9]\d*}/{name}',
                     'ProfileView:view',
                     'User'
                 );
                 $r->add(
                     $r::DUO,
-                    '/user/{id|i:[2-9]|[1-9]\d+}/edit/profile',
+                    '/user/{id|i:[1-9]\d*}/edit/profile',
                     'ProfileEdit:edit',
                     'EditUserProfile'
                 );
                 $r->add(
                     $r::DUO,
-                    '/user/{id|i:[2-9]|[1-9]\d+}/edit/config',
+                    '/user/{id|i:[1-9]\d*}/edit/config',
                     'ProfileConfig:config',
                     'EditUserBoardConfig'
                 );
                 $r->add(
                     $r::DUO,
-                    '/user/{id|i:[2-9]|[1-9]\d+}/edit/email',
+                    '/user/{id|i:[1-9]\d*}/edit/email',
                     'ProfileEmail:email',
                     'EditUserEmail'
                 );
                 $r->add(
                     $r::DUO,
-                    '/user/{id|i:[2-9]|[1-9]\d+}/edit/passphrase',
+                    '/user/{id|i:[1-9]\d*}/edit/passphrase',
                     'ProfilePass:pass',
                     'EditUserPass'
                 );
                 $r->add(
                     $r::DUO,
-                    '/user/{id|i:[2-9]|[1-9]\d+}/edit/moderation',
+                    '/user/{id|i:[1-9]\d*}/edit/moderation',
                     'ProfileMod:moderation',
                     'EditUserModeration'
                 );
@@ -358,7 +358,7 @@ class Routing
             ) {
                 $r->add(
                     $r::DUO,
-                    '/send_email/{id|i:[2-9]|[1-9]\d+}/{hash}',
+                    '/send_email/{id|i:[1-9]\d*}/{hash}',
                     'Email:email',
                     'SendEmail'
                 );
@@ -437,7 +437,7 @@ class Routing
                 );
                 $r->add(
                     $r::GET,
-                    '/admin/users/user/{id|i:[2-9]|[1-9]\d+}[/{page|i:[1-9]\d*}]',
+                    '/admin/users/user/{id|i:[1-9]\d*}[/{page|i:[1-9]\d*}]',
                     'AdminUsersStat:view',
                     'AdminUserStat'
                 );
@@ -464,7 +464,7 @@ class Routing
 
             $r->add(
                 $r::GET,
-                '/admin/users/promote/{uid|i:[2-9]|[1-9]\d+}/{pid|i:[1-9]\d*}/{token}',
+                '/admin/users/promote/{uid|i:[1-9]\d*}/{pid|i:[1-9]\d*}/{token}',
                 'AdminUsersPromote:promote',
                 'AdminUserPromote'
             );
@@ -493,7 +493,7 @@ class Routing
                 );
                 $r->add(
                     $r::DUO,
-                    '/admin/bans/new[/{ids:\d+(?:-\d+)*}[/{uid|i:[2-9]|[1-9]\d+}]]',
+                    '/admin/bans/new[/{ids:\d+(?:-\d+)*}[/{uid|i:[1-9]\d*}]]',
                     'AdminBans:add',
                     'AdminBansNew'
                 );
@@ -511,7 +511,7 @@ class Routing
                 );
                 $r->add(
                     $r::GET,
-                    '/admin/bans/delete/{id|i:[1-9]\d*}/{token}[/{uid|i:[2-9]|[1-9]\d+}]',
+                    '/admin/bans/delete/{id|i:[1-9]\d*}/{token}[/{uid|i:[1-9]\d*}]',
                     'AdminBans:delete',
                     'AdminBansDelete'
                 );

+ 1 - 1
app/Controllers/Update.php

@@ -37,7 +37,7 @@ class Update
         }
         $uri = \rawurldecode($uri);
 
-        $this->c->user = $this->c->users->create(['id' => 2, 'group_id' => FORK_GROUP_ADMIN]); //???? id?
+        $this->c->user = $this->c->users->create(['id' => 1, 'group_id' => FORK_GROUP_ADMIN]); //???? id?
         $this->c->Lang->load('common');
 
         $r = $this->c->Router;

+ 2 - 3
app/Models/Cookie/Cookie.php

@@ -117,14 +117,13 @@ class Cookie extends Model
 
         if (
             ! \is_string($ckUser)
-            || ! \preg_match('%^(\-)?(\d{1,10})_(\d{10})_([a-f\d]{32,128})_([a-f\d]{32,128})$%Di', $ckUser, $ms)
+            || ! \preg_match('%^(\-)?([1-9]\d{0,9})_(\d{10})_([a-f\d]{32,128})_([a-f\d]{32,128})$%Di', $ckUser, $ms)
         ) {
             return;
         }
 
         if (
-            2 > $ms[2]
-            || \time() > $ms[3]
+            \time() > $ms[3]
             || ! \hash_equals(
                     $this->c->Secury->hmac($ms[1] . $ms[2] . $ms[3] . $ms[4], $this->key1),
                     $ms[5]

+ 1 - 1
app/Models/Forum/Delete.php

@@ -80,7 +80,7 @@ class Delete extends Action
             $this->c->DB->exec($query, $vars);
 
             $query = 'UPDATE ::forums
-                SET last_poster_id=1
+                SET last_poster_id=0
                 WHERE last_poster_id IN (?ai:users)';
 
             $this->c->DB->exec($query, $vars);

+ 5 - 0
app/Models/Group/Save.php

@@ -51,6 +51,11 @@ class Save extends Action
         $this->c->DB->exec($query, $vars);
         $group->resModified();
 
+        // сбросить кеш для гостя
+        if ($group->groupGuest) {
+            $this->c->users->resetGuest();
+        }
+
         return $group;
     }
 

+ 7 - 7
app/Models/Online/Online.php

@@ -95,7 +95,7 @@ class Online extends Model
                 if ($cur['logged'] < $tVisit) {
                     $needClean = true;
 
-                    if ($cur['user_id'] > 1) {
+                    if ($cur['user_id'] > 0) {
                         $vars = [
                             ':last' => $cur['logged'],
                             ':id' => $cur['user_id'],
@@ -124,7 +124,7 @@ class Online extends Model
             }
 
             // пользователь
-            if ($cur['user_id'] > 1) {
+            if ($cur['user_id'] > 0) {
                 $users[$cur['user_id']] = $cur['ident'];
             // гость
             } elseif ('' == $cur['o_name']) {
@@ -158,7 +158,7 @@ class Online extends Model
         $this->all    = $all;
         $this->detail = $detail;
 
-        unset($this->online[1]);
+        unset($this->online[0]);
 
         if ($detail) {
             $this->users  = $users;
@@ -186,15 +186,15 @@ class Online extends Model
             if ($this->c->user->logged > 0) {
                 $query = 'UPDATE ::online
                     SET logged=?i:logged, o_position=?s:pos, o_name=?s:name
-                    WHERE user_id=1 AND ident=?s:ip';
+                    WHERE user_id=0 AND ident=?s:ip';
             } else {
                 $query = 'INSERT INTO ::online (user_id, ident, logged, o_position, o_name)
-                    SELECT 1, ?s:ip, ?i:logged, ?s:pos, ?s:name
+                    SELECT 0, ?s:ip, ?i:logged, ?s:pos, ?s:name
                     FROM ::groups
                     WHERE NOT EXISTS (
                         SELECT 1
                         FROM ::online
-                        WHERE user_id=1 AND ident=?s:ip
+                        WHERE user_id=0 AND ident=?s:ip
                     )
                     LIMIT 1';
             }
@@ -240,7 +240,7 @@ class Online extends Model
             ];
             $query = 'DELETE
                 FROM ::online
-                WHERE user_id=1 AND ident=?s:ip';
+                WHERE user_id=0 AND ident=?s:ip';
         } else {
             $vars = [
                 ':id' => $user->id,

+ 3 - 3
app/Models/PM/Delete.php

@@ -146,19 +146,19 @@ class Delete extends Method
             $this->c->DB->exec($query, $vars);
 
             $query = 'UPDATE ::pm_topics
-                SET poster_id=1, poster_status=?i:status
+                SET poster_id=0, poster_status=?i:status
                 WHERE poster_id IN (?ai:ids)';
 
             $this->c->DB->exec($query, $vars);
 
             $query = 'UPDATE ::pm_topics
-                SET target_id=1, target_status=?i:status
+                SET target_id=0, target_status=?i:status
                 WHERE target_id IN (?ai:ids)';
 
             $this->c->DB->exec($query, $vars);
 
             $query = 'UPDATE ::pm_posts
-                SET poster_id=1
+                SET poster_id=0
                 WHERE poster_id IN (?ai:ids)';
 
             $this->c->DB->exec($query, $vars);

+ 3 - 9
app/Models/PM/PPost.php

@@ -64,21 +64,15 @@ class PPost extends DataModel
 
     protected function getuser(): User
     {
-        $user = $this->c->users->load($this->poster_id);
-
         if (
-            ! $user instanceof User
-            && 1 !== $this->poster_id // ???? может сменить id гостя?
+            $this->poster_id < 1
+            || ! ($user = $this->c->users->load($this->poster_id)) instanceof User
         ) {
-            $user = $this->c->users->load(1);
+            $user = $this->c->users->guest(['username' => $this->poster]);
         }
 
         if (! $user instanceof User) {
             throw new RuntimeException("No user data in ppost number {$this->id}");
-        } elseif ($user->isGuest) {
-            $user = clone $user;
-
-            $user->__username = $this->poster;
         }
 
         return $user;

+ 3 - 4
app/Models/Pages/Admin/Install.php

@@ -1127,7 +1127,6 @@ class Install extends Admin
         $this->c->DB->exec('UPDATE ::groups SET g_pm=0, g_sig_length=0, g_sig_lines=0 WHERE g_id=?i', [FORK_GROUP_GUEST]);
 
         $ip = \filter_var($_SERVER['REMOTE_ADDR'], \FILTER_VALIDATE_IP) ?: '0.0.0.0';
-        $this->c->DB->exec('INSERT INTO ::users (group_id, username, username_normal, password, signature, u_pm) VALUES (?i, ?s, ?s, ?s, \'\', ?i)', [FORK_GROUP_GUEST, __('Guest '), $this->c->users->normUsername(__('Guest ')), __('Guest '), 0]);
         $this->c->DB->exec('INSERT INTO ::users (group_id, username, username_normal, password, email, email_normal, language, style, num_posts, last_post, registered, registration_ip, last_visit, signature, num_topics) VALUES (?i, ?s, ?s, ?s, ?s, ?s, ?s, ?s, 1, ?i, ?i, ?s, ?i, \'\', 1)', [FORK_GROUP_ADMIN, $v->username, $this->c->users->normUsername($v->username), password_hash($v->password, \PASSWORD_DEFAULT), $v->email, $this->c->NormEmail->normalize($v->email), $v->defaultlang, $v->defaultstyle, $now, $now, $ip, $now]);
 
         $pun_config = [
@@ -1212,9 +1211,9 @@ class Install extends Admin
         }
 
         $this->c->DB->exec('INSERT INTO ::categories (cat_name, disp_position) VALUES (?s, ?i)', [__('Test category'), 1]);
-        $this->c->DB->exec('INSERT INTO ::forums (forum_name, forum_desc, num_topics, num_posts, last_post, last_post_id, last_poster, last_poster_id, last_topic, disp_position, cat_id, moderators) VALUES (?s, ?s, ?i, ?i, ?i, ?i, ?s, ?i, ?s, ?i, ?i, \'\')', [__('Test forum'), __('This is just a test forum'), 1, 1, $now, 1, $v->username, 2, __('Test post'), 1, 1]);
-        $this->c->DB->exec('INSERT INTO ::topics (poster, poster_id, subject, posted, first_post_id, last_post, last_post_id, last_poster, last_poster_id, forum_id) VALUES(?s, ?i, ?s, ?i, ?i, ?i, ?i, ?s, ?i, ?i)', [$v->username, 2, __('Test post'), $now, 1, $now, 1, $v->username, 2, 1]);
-        $this->c->DB->exec('INSERT INTO ::posts (poster, poster_id, poster_ip, message, posted, topic_id) VALUES(?s, ?i, ?s, ?s, ?i, ?i)', [$v->username, 2, $ip, __('Test message'), $now, 1]);
+        $this->c->DB->exec('INSERT INTO ::forums (forum_name, forum_desc, num_topics, num_posts, last_post, last_post_id, last_poster, last_poster_id, last_topic, disp_position, cat_id, moderators) VALUES (?s, ?s, ?i, ?i, ?i, ?i, ?s, ?i, ?s, ?i, ?i, \'\')', [__('Test forum'), __('This is just a test forum'), 1, 1, $now, 1, $v->username, 1, __('Test post'), 1, 1]);
+        $this->c->DB->exec('INSERT INTO ::topics (poster, poster_id, subject, posted, first_post_id, last_post, last_post_id, last_poster, last_poster_id, forum_id) VALUES(?s, ?i, ?s, ?i, ?i, ?i, ?i, ?s, ?i, ?i)', [$v->username, 1, __('Test post'), $now, 1, $now, 1, $v->username, 1, 1]);
+        $this->c->DB->exec('INSERT INTO ::posts (poster, poster_id, poster_ip, message, posted, topic_id) VALUES(?s, ?i, ?s, ?s, ?i, ?i)', [$v->username, 1, $ip, __('Test message'), $now, 1]);
 
         $smilies = [
             ':)'         => 'smile.png',

+ 41 - 1229
app/Models/Pages/Admin/Update.php

@@ -24,7 +24,8 @@ use function \ForkBB\__;
 class Update extends Admin
 {
     const PHP_MIN                    = '7.3.0';
-    const LATEST_REV_WITH_DB_CHANGES = 37;
+    const REV_MIN_FOR_UPDATE         = 42;
+    const LATEST_REV_WITH_DB_CHANGES = 43;
     const LOCK_NAME                  = 'lock_update';
     const LOCk_TTL                   = 1800;
     const JSON_OPTIONS               = \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE | \JSON_THROW_ON_ERROR;
@@ -160,7 +161,7 @@ class Update extends Admin
                     // база не от ForkBB или старая ревизия
                     if (
                         null === $e
-                        && $this->c->config->i_fork_revision < 41
+                        && $this->c->config->i_fork_revision < self::REV_MIN_FOR_UPDATE
                     ) {
                         $e = 'Version mismatch error';
                     }
@@ -397,1277 +398,88 @@ class Update extends Admin
 #    }
 
     /**
-     * rev.1 to rev.2
+     * rev.42 to rev.43
      */
-    protected function stageNumber1(array $args): ?int
+    protected function stageNumber42(array $args): ?int
     {
-        $this->c->DB->alterField('users', 'gender', 'TINYINT UNSIGNED', false, 0);
-        $this->c->DB->alterField('users', 'disp_topics', 'TINYINT UNSIGNED', false, 0);
-        $this->c->DB->alterField('users', 'disp_posts', 'TINYINT UNSIGNED', false, 0);
-
-        $this->c->DB->addField('users', 'ip_check_type', 'TINYINT UNSIGNED', false, 0);
-        $this->c->DB->addField('users', 'login_ip_cache', 'VARCHAR(255)', false, '');
-
-        return null;
-    }
-
-    /**
-     * rev.2 to rev.3
-     */
-    protected function stageNumber2(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'multiple=>AdminUsersRecalculate',
-            '\\ForkBB\\Models\\Pages\\Admin\\Users\\Recalculate::class',
-            'AdminUsersNew'
-        );
-        $coreConfig->add(
-            'EOL',
-            '\\PHP_EOL'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.3 to rev.4
-     */
-    protected function stageNumber3(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $result = $coreConfig->delete('multiple=>AdminUsersRecalculate');
-
-        $coreConfig->add(
-            'multiple=>UserManagerUpdateLoginIpCache',
-            '\\ForkBB\\Models\\User\\UpdateLoginIpCache::class',
-            'UserManagerUpdateCountTopics'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.4 to rev.5
-     */
-    protected function stageNumber4(array $args): ?int
-    {
-        unset($this->c->config->o_date_format);
-        unset($this->c->config->o_time_format);
-
-        $this->c->config->save();
-
-        $query = 'UPDATE ::users
-            SET time_format=time_format+1
-            WHERE time_format>0';
+        $query = 'DELETE FROM ::users WHERE id=1';
 
         $this->c->DB->exec($query);
 
-        $query = 'UPDATE ::users
-            SET date_format=date_format+1
-            WHERE date_format>0';
+        $query = 'UPDATE ::forums SET last_poster_id=0 WHERE last_poster_id=1';
 
         $this->c->DB->exec($query);
 
-        return null;
-    }
-
-    /**
-     * rev.5 to rev.6
-     */
-    protected function stageNumber5(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'multiple=>Email',
-            '\\ForkBB\\Models\\Pages\\Email::class',
-            'Report'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.6 to rev.7
-     */
-    protected function stageNumber6(array $args): ?int
-    {
-        $this->c->DB->addField('groups', 'g_sig_use', 'TINYINT(1)', false, 1);
-        $this->c->DB->addField('groups', 'g_sig_length', 'SMALLINT UNSIGNED', false, 400);
-        $this->c->DB->addField('groups', 'g_sig_lines', 'TINYINT UNSIGNED', false, 4);
-
-        $vars = [
-            ':sig_use'    => '1' == $this->c->config->o_signatures ? 1 : 0,
-            ':sig_length' => $this->c->config->p_sig_length > 10000 ? 10000 : (int) $this->c->config->p_sig_length,
-            ':sig_lines'  => $this->c->config->p_sig_lines> 255 ? 255 : (int) $this->c->config->p_sig_lines,
-        ];
-        $query = 'UPDATE ::groups
-            SET g_sig_use=?i:sig_use, g_sig_length=?i:sig_length, g_sig_lines=?i:sig_lines';
-
-        $this->c->DB->query($query, $vars);
-
-        $vars = [
-            ':grp' => FORK_GROUP_ADMIN,
-        ];
-        $query = 'UPDATE ::groups
-            SET g_sig_use=1, g_sig_length=10000, g_sig_lines=255
-            WHERE g_id=?i:grp';
-
-        $this->c->DB->query($query, $vars);
-
-        $vars = [
-            ':grp' => FORK_GROUP_GUEST,
-        ];
-        $query = 'UPDATE ::groups
-            SET g_sig_use=0, g_sig_length=0, g_sig_lines=0
-            WHERE g_id=?i:grp';
-
-        $this->c->DB->query($query, $vars);
-
-        unset($this->c->config->o_signatures);
-        unset($this->c->config->p_sig_length);
-        unset($this->c->config->p_sig_lines);
-
-        $this->c->config->save();
-
-        return null;
-    }
-
-    /**
-     * rev.7 to rev.8
-     */
-    protected function stageNumber7(array $args): ?int
-    {
-        $this->c->DB->dropField('groups', 'g_sig_use');
-
-        return null;
-    }
-
-    /**
-     * rev.8 to rev.9
-     */
-    protected function stageNumber8(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'multiple=>Feed',
-            '\\ForkBB\\Models\\Pages\\Feed::class',
-            'Email'
-        );
-        $coreConfig->add(
-            'multiple=>PostManagerFeed',
-            '\\ForkBB\\Models\\Post\\Feed::class',
-            'PostManagerMove'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.9 to rev.10
-     */
-    protected function stageNumber9(array $args): ?int
-    {
-        $this->c->config->i_email_max_recipients = 1;
-
-        $this->c->config->save();
-
-        return null;
-    }
-
-    /**
-     * rev.10 to rev.11
-     */
-    protected function stageNumber10(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'shared=>subscriptions',
-            '\\ForkBB\\Models\\Subscription\\Model::class',
-            'search'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.11 to rev.12
-     */
-    protected function stageNumber11(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'multiple=>SearchModelActionF',
-            '\\ForkBB\\Models\\Search\\ActionF::class',
-            'SearchModelActionT'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.12 to rev.13
-     */
-    protected function stageNumber12(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'shared=>SubscriptionModelSend',
-            '\\ForkBB\\Models\\Subscription\\Send::class'
-        );
-
-        $result = $coreConfig->delete('multiple=>BanListModelIsBanned');
-
-        $coreConfig->add(
-            'shared=>BanListModelIsBanned',
-            '\\ForkBB\\Models\\BanList\\IsBanned::class'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
+        $query = 'UPDATE ::online SET user_id=0 WHERE user_id=1';
 
-    /**
-     * rev.13 to rev.14
-     */
-    protected function stageNumber13(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
+        $this->c->DB->exec($query);
 
-        $result = $coreConfig->delete('multiple=>AdminPermissions');
+        $query = 'UPDATE ::pm_posts SET poster_id=0 WHERE poster_id=1';
 
-        $coreConfig->add(
-            'multiple=>AdminParser',
-            '\\ForkBB\\Models\\Pages\\Admin\\Parser\\Edit::class',
-            'AdminReports'
-        );
+        $this->c->DB->exec($query);
 
-        $coreConfig->add(
-            'multiple=>AdminParserSmilies',
-            '\\ForkBB\\Models\\Pages\\Admin\\Parser\\Smilies::class',
-            'AdminParser'
-        );
+        $query = 'UPDATE ::pm_topics SET poster_id=0 WHERE poster_id=1';
 
-        $coreConfig->add(
-            'multiple=>AdminParserBBCode',
-            '\\ForkBB\\Models\\Pages\\Admin\\Parser\\BBCode::class',
-            'AdminParserSmilies'
-        );
+        $this->c->DB->exec($query);
 
-        $coreConfig->save();
+        $query = 'UPDATE ::pm_topics SET target_id=0 WHERE target_id=1';
 
-        return null;
-    }
+        $this->c->DB->exec($query);
 
-    /**
-     * rev.14 to rev.15
-     */
-    protected function stageNumber14(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
+        $query = 'UPDATE ::posts SET poster_id=0 WHERE poster_id=1';
 
-        $result = $coreConfig->delete('multiple=>SmileyListModelLoad');
+        $this->c->DB->exec($query);
 
-        $coreConfig->add(
-            'shared=>SmileyListModelLoad',
-            '\\ForkBB\\Models\\SmileyList\\Load::class'
-        );
+        $query = 'UPDATE ::posts SET editor_id=0 WHERE editor_id=1';
 
-        $coreConfig->add(
-            'shared=>SmileyListModelUpdate',
-            '\\ForkBB\\Models\\SmileyList\\Update::class'
-        );
+        $this->c->DB->exec($query);
 
-        $coreConfig->add(
-            'shared=>SmileyListModelInsert',
-            '\\ForkBB\\Models\\SmileyList\\Insert::class'
-        );
+        $query = 'UPDATE ::reports SET reported_by=0 WHERE reported_by=1';
 
-        $coreConfig->add(
-            'shared=>SmileyListModelDelete',
-            '\\ForkBB\\Models\\SmileyList\\Delete::class'
-        );
+        $this->c->DB->exec($query);
 
-        $coreConfig->save();
+        $query = 'UPDATE ::reports SET zapped_by=0 WHERE zapped_by=1';
 
-        $this->c->DB->renameField('smilies', 'image', 'sm_image');
-        $this->c->DB->renameField('smilies', 'text', 'sm_code');
-        $this->c->DB->renameField('smilies', 'disp_position', 'sm_position');
+        $this->c->DB->exec($query);
 
-        $this->c->DB->alterField('smilies', 'sm_position', 'INT(10) UNSIGNED', false, 0);
+        $query = 'UPDATE ::topics SET poster_id=0 WHERE poster_id=1';
 
-        return null;
-    }
+        $this->c->DB->exec($query);
 
-    /**
-     * rev.15 to rev.16
-     */
-    protected function stageNumber15(array $args): ?int
-    {
-        // bbcode
-        $schema = [
-            'FIELDS' => [
-                'id'           => ['SERIAL', false],
-                'bb_tag'       => ['VARCHAR(11)', false, ''],
-                'bb_edit'      => ['TINYINT(1)', false, 1],
-                'bb_delete'    => ['TINYINT(1)', false, 1],
-                'bb_structure' => ['MEDIUMTEXT', false],
-            ],
-            'PRIMARY KEY' => ['id'],
-            'UNIQUE KEYS' => [
-                'bb_tag_idx' => ['bb_tag'],
-            ],
-        ];
-        $this->c->DB->createTable('bbcode', $schema);
-
-        $query = 'INSERT INTO ::bbcode (bb_tag, bb_edit, bb_delete, bb_structure)
-            VALUES(?s:tag, 1, 0, ?s:structure)';
-
-        $bbcodes = include $this->c->DIR_APP . '/config/defaultBBCode.php';
-        foreach ($bbcodes as $bbcode) {
-            $vars = [
-                ':tag'       => $bbcode['tag'],
-                ':structure' => \json_encode($bbcode, self::JSON_OPTIONS),
-            ];
-            $this->c->DB->exec($query, $vars);
-        }
+        $query = 'UPDATE ::topics SET last_poster_id=0 WHERE last_poster_id=1';
 
-        $this->c->config->a_bb_white_mes = [];
-        $this->c->config->a_bb_white_sig = ['b', 'i', 'u', 'color', 'colour', 'email', 'url'];
-        $this->c->config->a_bb_black_mes = [];
-        $this->c->config->a_bb_black_sig = [];
+        $this->c->DB->exec($query);
 
-        unset($this->c->config->o_quote_depth);
-        unset($this->c->config->p_sig_img_tag);
-        unset($this->c->config->p_message_img_tag);
+        $query = 'UPDATE ::warnings SET poster_id=0 WHERE poster_id=1';
 
-        $this->c->config->save();
+        $this->c->DB->exec($query);
 
         $coreConfig = new CoreConfig($this->configFile);
 
-        $result = $coreConfig->delete('BBCODE_INFO=>forSign');
-
-        $coreConfig->add(
-            'shared=>bbcode',
-            '\'@BBCodeListModel:init\'',
-            'subscriptions'
-        );
-
-        $coreConfig->add(
-            'shared=>BBCodeListModel',
-            [
-                'class' => '\\ForkBB\\Models\\BBCodeList\\Model::class',
-                'file'  => '\'defaultBBCode.php\'',
-            ]
-        );
-
-        $coreConfig->add(
-            'shared=>BBCodeListModelGenerate',
-            '\\ForkBB\\Models\\BBCodeList\\Generate::class'
-        );
-
         $coreConfig->add(
-            'shared=>BBCodeListModelLoad',
-            '\\ForkBB\\Models\\BBCodeList\\Load::class'
+            'shared=>Groups/save',
+            '\\ForkBB\\Models\\Group\\Save::class',
+            'Group/save'
         );
 
         $coreConfig->add(
-            'shared=>BBCodeListModelUpdate',
-            '\\ForkBB\\Models\\BBCodeList\\Update::class'
+            'shared=>Groups/perm',
+            '\\ForkBB\\Models\\Group\\Perm::class',
+            'Group/save'
         );
 
         $coreConfig->add(
-            'shared=>BBCodeListModelInsert',
-            '\\ForkBB\\Models\\BBCodeList\\Insert::class'
-        );
-
-        $coreConfig->add(
-            'shared=>BBCodeListModelDelete',
-            '\\ForkBB\\Models\\BBCodeList\\Delete::class'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.16 to rev.17
-     */
-    protected function stageNumber16(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'shared=>Router=>csrf',
-            '\'@Csrf\''
+            'shared=>Groups/delete',
+            '\\ForkBB\\Models\\Group\\Delete::class',
+            'Group/save'
         );
 
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.17 to rev.18
-     */
-    protected function stageNumber17(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'multiple=>BBStructure',
-            '\\ForkBB\\Models\\BBCodeList\\Structure::class'
-        );
+        $result = $coreConfig->delete('shared=>Group/delete');
+        $result = $coreConfig->delete('shared=>Group/perm');
+        $result = $coreConfig->delete('shared=>Group/save');
 
         $coreConfig->save();
 
         return null;
     }
-
-    /**
-     * rev.18 to rev.19
-     */
-    protected function stageNumber18(array $args): ?int
-    {
-        $this->c->DB->addField('users', 'avatar', 'VARCHAR(30)', false, '', 'title');
-
-        $dir     = $this->c->DIR_PUBLIC . $this->c->config->o_avatars_dir . '/';
-        $avatars = [];
-
-        if (
-            \is_dir($dir)
-            && false !== ($dh = \opendir($dir))
-        ) {
-            while (false !== ($entry = \readdir($dh))) {
-                if (
-                    \preg_match('%^([1-9]\d*)\.(jpg|gif|png)$%D', $entry, $matches)
-                    && \is_file($dir . $entry)
-                ) {
-                    $avatars[$matches[2]][] = (int) $matches[1];
-                }
-            }
-            \closedir($dh);
-        }
-
-        $query = 'UPDATE ::users
-            SET avatar=CONCAT(id, \'.\', ?s:ext)
-            WHERE id IN (?ai:ids)';
-
-        foreach ($avatars as $ext => $ids) {
-            $vars = [
-                ':ext' => $ext,
-                ':ids' => $ids,
-            ];
-
-            $this->c->DB->exec($query, $vars);
-        }
-
-        return null;
-    }
-
-    /**
-     * rev.19 to rev.20
-     */
-    protected function stageNumber19(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $result = $coreConfig->delete('shared=>FileCache');
-
-        $coreConfig->add(
-            'shared=>Cache',
-            $result
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.20 to rev.21
-     */
-    protected function stageNumber20(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'shared=>Test',
-            '\\ForkBB\\Core\\Test::class',
-            'Func'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.21 to rev.22
-     */
-    protected function stageNumber21(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'USERNAME_PATTERN',
-            '\'%^(?=.{2,25}$)\\p{L}[\\p{L}\\p{N}\\x20\\._-]+$%uD\'',
-            'FLOOD_INTERVAL'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.22 to rev.23
-     */
-    protected function stageNumber22(array $args): ?int
-    {
-        $this->c->config->i_topic_review          = $this->c->config->o_topic_review          ?? 15;
-        $this->c->config->i_disp_topics_default   = $this->c->config->o_disp_topics_default   ?? 30;
-        $this->c->config->i_disp_posts_default    = $this->c->config->o_disp_posts_default    ?? 25;
-        $this->c->config->i_disp_users            = $this->c->config->o_disp_users            ?? 50;
-        $this->c->config->i_default_email_setting = $this->c->config->o_default_email_setting ?? 2;
-        $this->c->config->i_avatars_width         = $this->c->config->o_avatars_width         ?? 60;
-        $this->c->config->i_avatars_height        = $this->c->config->o_avatars_height        ?? 60;
-        $this->c->config->i_avatars_size          = $this->c->config->o_avatars_size          ?? 10240;
-        $this->c->config->i_feed_type             = $this->c->config->o_feed_type             ?? 2;
-        $this->c->config->i_feed_ttl              = $this->c->config->o_feed_ttl              ?? 0;
-        $this->c->config->i_report_method         = $this->c->config->o_report_method         ?? 0;
-        $this->c->config->i_default_user_group    = $this->c->config->o_default_user_group    ?? FORK_GROUP_MEMBER;
-        $this->c->config->a_max_users = [
-            'number' => (int) ($this->c->config->st_max_users ?? 1),
-            'time'   => (int) ($this->c->config->st_max_users_time ?? \time()),
-        ];
-
-        unset($this->c->config->o_enable_acaptcha);
-        unset($this->c->config->o_crypto_salt);
-        unset($this->c->config->o_crypto_pas);
-        unset($this->c->config->o_crypto_enable);
-        unset($this->c->config->o_check_ip);
-        unset($this->c->config->o_coding_forms);
-        unset($this->c->config->o_fbox_files);
-        unset($this->c->config->o_fbox_guest);
-        unset($this->c->config->o_show_version);
-        unset($this->c->config->o_topic_review);
-        unset($this->c->config->o_disp_topics_default);
-        unset($this->c->config->o_disp_posts_default);
-        unset($this->c->config->o_disp_users);
-        unset($this->c->config->o_default_email_setting);
-        unset($this->c->config->o_avatars_width);
-        unset($this->c->config->o_avatars_height);
-        unset($this->c->config->o_avatars_size);
-        unset($this->c->config->o_feed_type);
-        unset($this->c->config->o_feed_ttl);
-        unset($this->c->config->o_report_method);
-        unset($this->c->config->o_board_redirect);
-        unset($this->c->config->o_board_redirectg);
-        unset($this->c->config->o_default_user_group);
-        unset($this->c->config->st_max_users);
-        unset($this->c->config->st_max_users_time);
-
-        $this->c->config->save();
-
-        return null;
-    }
-
-    /**
-     * rev.23 to rev.24
-     */
-    protected function stageNumber23(array $args): ?int
-    {
-        $this->c->DB->addField('forums', 'last_poster_id', 'INT(10) UNSIGNED', false, 0, 'last_poster');
-
-        $query = 'UPDATE ::forums AS f
-            SET f.last_poster_id=COALESCE((
-                SELECT u.id
-                FROM ::users AS u
-                WHERE u.username=f.last_poster AND u.id>1
-            ), 0)';
-        $this->c->DB->exec($query);
-
-        $this->c->DB->renameField('posts', 'edited_by', 'editor');
-        $this->c->DB->addField('posts', 'editor_id', 'INT(10) UNSIGNED', false, 0, 'editor');
-
-        $query = 'UPDATE ::posts AS p
-            SET p.editor_id=COALESCE((
-                SELECT u.id
-                FROM ::users AS u
-                WHERE u.username=p.editor AND u.id>1
-            ), 0)';
-        $this->c->DB->exec($query);
-
-        unset($this->c->config->o_merge_timeout);
-
-        $this->c->config->save();
-
-        return null;
-    }
-
-    /**
-     * rev.24 to rev.25
-     */
-    protected function stageNumber24(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'multiple=>ForumManagerUpdateUsername',
-            '\\ForkBB\\Models\\Forum\\UpdateUsername::class',
-            'ForumManagerMarkread'
-        );
-
-        $coreConfig->add(
-            'multiple=>PostManagerUpdateUsername',
-            '\\ForkBB\\Models\\Post\\UpdateUsername::class',
-            'PostManagerFeed'
-        );
-
-        $coreConfig->add(
-            'multiple=>TopicManagerUpdateUsername',
-            '\\ForkBB\\Models\\Topic\\UpdateUsername::class',
-            'TopicManagerMove'
-        );
-
-        $coreConfig->add(
-            'multiple=>OnlineModelUpdateUsername',
-            '\\ForkBB\\Models\\Online\\UpdateUsername::class',
-            'OnlineModelInfo'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.25 to rev.26
-     */
-    protected function stageNumber25(array $args): ?int
-    {
-        $this->c->DB->renameField('topics', 'poll_kol', 'poll_votes');
-        $this->c->DB->renameField('poll', 'question', 'question_id');
-        $this->c->DB->renameField('poll', 'field', 'field_id');
-        $this->c->DB->renameField('poll', 'choice', 'qna_text');
-
-        $this->c->config->b_poll_enabled       = $this->c->config->o_poll_enabled ?? 0;
-        $this->c->config->i_poll_max_questions = $this->c->config->o_poll_max_ques ?? 3;
-        $this->c->config->i_poll_max_fields    = $this->c->config->o_poll_max_field ?? 20;
-        $this->c->config->i_poll_time          = $this->c->config->o_poll_time ?? 60;
-        $this->c->config->i_poll_term          = $this->c->config->o_poll_term ?? 3;
-        $this->c->config->b_poll_guest         = $this->c->config->o_poll_guest ?? 0;
-
-        unset($this->c->config->o_poll_enabled);
-        unset($this->c->config->o_poll_max_ques);
-        unset($this->c->config->o_poll_max_field);
-        unset($this->c->config->o_poll_time);
-        unset($this->c->config->o_poll_term);
-        unset($this->c->config->o_poll_guest);
-
-        $this->c->config->save();
-
-        return null;
-    }
-
-    /**
-     * rev.26 to rev.27
-     */
-    protected function stageNumber26(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'shared=>polls',
-            '\\ForkBB\\Models\\Poll\\Manager::class',
-            'posts'
-        );
-
-        $coreConfig->add(
-            'shared=>PollManagerLoad',
-            '\\ForkBB\\Models\\Poll\\Load::class',
-            'UsersRules'
-        );
-
-        $coreConfig->add(
-            'shared=>PollManagerSave',
-            '\\ForkBB\\Models\\Poll\\Save::class',
-            'PollManagerLoad'
-        );
-
-        $coreConfig->add(
-            'shared=>PollManagerDelete',
-            '\\ForkBB\\Models\\Poll\\Delete::class',
-            'PollManagerSave'
-        );
-
-        $coreConfig->add(
-            'shared=>PollManagerRevision',
-            '\\ForkBB\\Models\\Poll\\Revision::class',
-            'PollManagerDelete'
-        );
-
-        $coreConfig->add(
-            'multiple=>PollModel',
-            '\\ForkBB\\Models\\Poll\\Model::class',
-            'PostManagerUpdateUsername'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.27 to rev.28
-     */
-    protected function stageNumber27(array $args): ?int
-    {
-        $this->c->DB->alterField('topics', 'poll_type', 'SMALLINT UNSIGNED', false, 0);
-
-        return null;
-    }
-
-    /**
-     * rev.28 to rev.29
-     */
-    protected function stageNumber28(array $args): ?int
-    {
-        $query = 'UPDATE ::poll AS pl
-            SET pl.qna_text=CONCAT(pl.votes, \'|\', pl.qna_text)
-            WHERE pl.field_id=0';
-
-        $this->c->DB->query($query);
-
-        $query = 'UPDATE ::poll AS pl, ::topics AS t
-            SET pl.votes=t.poll_votes
-            WHERE pl.field_id=0 AND pl.tid=t.id';
-
-        $this->c->DB->query($query);
-
-        $this->c->DB->dropField('topics', 'poll_votes');
-
-        return null;
-    }
-
-    /**
-     * rev.29 to rev.30
-     */
-    protected function stageNumber29(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'multiple=>Poll',
-            '\\ForkBB\\Models\\Pages\\Poll::class',
-            'Feed'
-        );
-
-        $coreConfig->save();
-
-        return null;
-   }
-
-    /**
-     * rev.30 to rev.31
-     */
-    protected function stageNumber30(array $args): ?int
-    {
-        $queries = [
-            'UPDATE ::bbcode SET bb_structure = REPLACE(bb_structure, \'"text only"\', \'"text_only"\')',
-            'UPDATE ::bbcode SET bb_structure = REPLACE(bb_structure, \'"no attr"\', \'"No_attr"\')',
-            'UPDATE ::bbcode SET bb_structure = REPLACE(bb_structure, \'"self nesting"\', \'"self_nesting"\')',
-            'UPDATE ::bbcode SET bb_structure = REPLACE(bb_structure, \'"body format"\', \'"body_format"\')',
-            'UPDATE ::bbcode SET bb_structure = REPLACE(bb_structure, \'"tags only"\', \'"tags_only"\')',
-            'UPDATE ::bbcode SET bb_structure = REPLACE(bb_structure, \'"text handler"\', \'"text_handler"\')',
-        ];
-
-        foreach ($queries as $query) {
-            $this->c->DB->exec($query);
-        }
-
-        return null;
-   }
-
-    /**
-     * rev.31 to rev.32
-     */
-    protected function stageNumber31(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'HTTP_HEADERS',
-            [
-                'common' => [
-                    'X-Content-Type-Options'  => '\'nosniff\'',
-                    'X-Frame-Options'         => '\'DENY\'',
-                    'X-XSS-Protection'        => '\'1; mode=block\'',
-                    'Referrer-Policy'         => '\'origin-when-cross-origin\'',
-                    'Content-Security-Policy' => '\'default-src \\\'self\\\';img-src *;object-src \\\'none\\\';frame-ancestors \\\'none\\\'\';base-uri \\\'self\\\';form-action \\\'self\\\'',
-                    'Feature-Policy'          => '\'accelerometer \\\'none\\\';ambient-light-sensor \\\'none\\\';autoplay \\\'none\\\';battery \\\'none\\\';camera \\\'none\\\';document-domain \\\'self\\\';fullscreen \\\'self\\\';geolocation \\\'none\\\';gyroscope \\\'none\\\';magnetometer \\\'none\\\';microphone \\\'none\\\';midi \\\'none\\\';payment \\\'none\\\';picture-in-picture \\\'none\\\';sync-xhr \\\'self\\\';usb \\\'none\\\'\'',
-                ],
-                'secure' => [
-                    'X-Content-Type-Options'  => '\'nosniff\'',
-                    'X-Frame-Options'         => '\'DENY\'',
-                    'X-XSS-Protection'        => '\'1; mode=block\'',
-                    'Referrer-Policy'         => '\'origin-when-cross-origin\'',
-                    'Content-Security-Policy' => '\'default-src \\\'self\\\';object-src \\\'none\\\';frame-ancestors \\\'none\\\'\';base-uri \\\'self\\\';form-action \\\'self\\\'',
-                    'Feature-Policy'          => '\'accelerometer \\\'none\\\';ambient-light-sensor \\\'none\\\';autoplay \\\'none\\\';battery \\\'none\\\';camera \\\'none\\\';document-domain \\\'self\\\';fullscreen \\\'self\\\';geolocation \\\'none\\\';gyroscope \\\'none\\\';magnetometer \\\'none\\\';microphone \\\'none\\\';midi \\\'none\\\';payment \\\'none\\\';picture-in-picture \\\'none\\\';sync-xhr \\\'self\\\';usb \\\'none\\\'\'',
-                ],
-            ],
-            'USERNAME_PATTERN'
-        );
-
-        $coreConfig->save();
-
-        return null;
-   }
-
-    /**
-     * rev.32 to rev.33
-     */
-    protected function stageNumber32(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'shared=>Log',
-            [
-                'class'  => '\\ForkBB\\Core\\Log::class',
-                'config' => [
-                    'path'       => '\'%DIR_LOG%/{Y-m-d}.log\'',
-                    'lineFormat' => '"\\\\%datetime\\\\% [\\\\%level_name\\\\%] \\\\%message\\\\%\\t\\\\%context\\\\%\\n"',
-                    'timeFormat' => '\'Y-m-d H:i:s\'',
-                ],
-            ],
-            'NormEmail'
-        );
-
-        $coreConfig->save();
-
-        return null;
-   }
-
-    /**
-     * rev.33 to rev.34
-     */
-    protected function stageNumber33(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'shared=>LogViewer',
-            [
-                'class'  => '\\ForkBB\\Core\\LogViewer::class',
-                'config' => [
-                    'dir'        => '\'%DIR_LOG%\'',
-                    'pattern'    => '\'*.log\'',
-                    'lineFormat' => '"\\\\%datetime\\\\% [\\\\%level_name\\\\%] \\\\%message\\\\%\\t\\\\%context\\\\%\\n"',
-                ],
-                'cache' => '\'%Cache%\'',
-            ],
-            'Log'
-        );
-
-        $coreConfig->add(
-            'multiple=>AdminLogs',
-            '\\ForkBB\\Models\\Pages\\Admin\\Logs::class',
-            'AdminParserBBCode'
-        );
-
-        $coreConfig->save();
-
-        return null;
-   }
-
-    /**
-     * rev.34 to rev.35
-     */
-    protected function stageNumber34(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'shared=>HTMLCleaner',
-            [
-                'class'  => '\\ForkBB\\Core\\HTMLCleaner::class',
-                'config' => '\'%DIR_APP%/config/jevix.default.php\'',
-            ],
-            'LogViewer'
-        );
-
-        $coreConfig->add(
-            'shared=>VLhtml',
-            '\\ForkBB\\Models\\Validators\\Html::class',
-            'VLemail'
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.35 to rev.36
-     */
-    protected function stageNumber35(array $args): ?int
-    {
-        unset($this->c->config->o_pms_enabled);
-        unset($this->c->config->o_pms_min_kolvo);
-
-        $this->c->config->b_pm = 0;
-
-        $this->c->config->save();
-
-        $this->c->DB->dropTable('pms_new_block');
-        $this->c->DB->dropTable('pms_new_posts');
-        $this->c->DB->dropTable('pms_new_topics');
-
-        // pm_block
-        $schema = [
-            'FIELDS' => [
-                'bl_first_id'  => ['INT(10) UNSIGNED', false, 0],
-                'bl_second_id' => ['INT(10) UNSIGNED', false, 0],
-            ],
-            'INDEXES' => [
-                'bl_first_id_idx'  => ['bl_first_id'],
-                'bl_second_id_idx' => ['bl_second_id'],
-            ],
-        ];
-        $this->c->DB->createTable('pm_block', $schema);
-
-        // pm_posts
-        $schema = [
-            'FIELDS' => [
-                'id'            => ['SERIAL', false],
-                'poster_number' => ['TINYINT UNSIGNED', false, 0],
-                'poster_ip'     => ['VARCHAR(45)', false, ''],
-                'message'       => ['TEXT', false],
-                'hide_smilies'  => ['TINYINT(1)', false, 0],
-                'posted'        => ['INT(10) UNSIGNED', false, 0],
-                'edited'        => ['INT(10) UNSIGNED', false, 0],
-                'topic_id'      => ['INT(10) UNSIGNED', false, 0],
-            ],
-            'PRIMARY KEY' => ['id'],
-            'INDEXES' => [
-                'topic_id_idx' => ['topic_id'],
-            ],
-        ];
-        $this->c->DB->createTable('pm_posts', $schema);
-
-        // pm_rnd
-        $schema = [
-            'FIELDS' => [
-                'user_id'     => ['INT(10) UNSIGNED', false, 0],
-                'topic_id'    => ['INT(10) UNSIGNED', false, 0],
-                'user_number' => ['TINYINT UNSIGNED', false, 0],
-                'username'    => ['VARCHAR(190)', false, ''],
-                'pt_status'   => ['TINYINT UNSIGNED', false, 0],
-                'last_visit'  => ['INT(10) UNSIGNED', false, 0],
-            ],
-            'PRIMARY KEY' => ['user_id', 'topic_id', 'user_number'],
-            'INDEXES' => [
-                'topic_id_idx'   => ['topic_id'],
-                'pt_status_idx'  => ['pt_status'],
-            ],
-        ];
-        $this->c->DB->createTable('pm_rnd', $schema);
-
-        // pm_topics
-        $schema = [
-            'FIELDS' => [
-                'id'          => ['SERIAL', false],
-                'subject'     => ['VARCHAR(255)', false, ''],
-                'num_replies' => ['INT(10) UNSIGNED', false, 0],
-                'last_post'   => ['INT(10) UNSIGNED', false, 0],
-                'last_number' => ['TINYINT UNSIGNED', false, 0],
-            ],
-            'PRIMARY KEY' => ['id'],
-            'INDEXES' => [
-                'last_post_idx' => ['last_post'],
-            ],
-        ];
-        $this->c->DB->createTable('pm_topics', $schema);
-
-        return null;
-    }
-
-    /**
-     * rev.36 to rev.37
-     */
-    protected function stageNumber36(array $args): ?int
-    {
-        $this->c->DB->exec('UPDATE ::groups SET g_pm=0, g_sig_length=0, g_sig_lines=0 WHERE g_id=?i', [FORK_GROUP_GUEST]);
-
-        $this->c->DB->renameField('users', 'messages_enable', 'u_pm');
-        $this->c->DB->renameField('users', 'messages_email',  'u_pm_notify');
-        $this->c->DB->renameField('users', 'messages_flag',   'u_pm_flash');
-        $this->c->DB->renameField('users', 'messages_new',    'u_pm_num_new');
-        $this->c->DB->renameField('users', 'messages_all',    'u_pm_num_all');
-        $this->c->DB->renameField('users', 'pmsn_last_post',  'u_pm_last_post');
-
-        return null;
-    }
-
-    /**
-     * rev.37 to rev.38
-     */
-    protected function stageNumber37(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'shared=>pms',
-            '\\ForkBB\\Models\\PM\\Model::class',
-            'bbcode'
-        );
-
-        $coreConfig->add(
-            'multiple=>PM',
-            '\\ForkBB\\Models\\Pages\\PM::class',
-            'Poll'
-        );
-
-        $coreConfig->add(
-            'multiple=>PMConfig',
-            '\\ForkBB\\Models\\Pages\\PM\\PMConfig::class',
-            'PM'
-        );
-
-        $coreConfig->add(
-            'multiple=>PMBlock',
-            '\\ForkBB\\Models\\Pages\\PM\\PMBlock::class',
-            'PM'
-        );
-
-        $coreConfig->add(
-            'multiple=>PMEdit',
-            '\\ForkBB\\Models\\Pages\\PM\\PMEdit::class',
-            'PM'
-        );
-
-        $coreConfig->add(
-            'multiple=>PMDelete',
-            '\\ForkBB\\Models\\Pages\\PM\\PMDelete::class',
-            'PM'
-        );
-
-        $coreConfig->add(
-            'multiple=>PMTopic',
-            '\\ForkBB\\Models\\Pages\\PM\\PMTopic::class',
-            'PM'
-        );
-
-        $coreConfig->add(
-            'multiple=>PMPost',
-            '\\ForkBB\\Models\\Pages\\PM\\PMPost::class',
-            'PM'
-        );
-
-        $coreConfig->add(
-            'multiple=>PMView',
-            '\\ForkBB\\Models\\Pages\\PM\\PMView::class',
-            'PM'
-        );
-
-        $coreConfig->add(
-            'multiple=>PTopicModel',
-            '\\ForkBB\\Models\\PM\\PTopic::class'
-        );
-
-        $coreConfig->add(
-            'multiple=>PPostModel',
-            '\\ForkBB\\Models\\PM\\PPost::class'
-        );
-
-        $coreConfig->add(
-            'shared=>PBlockModel',
-            '\\ForkBB\\Models\\PM\\PBlock::class'
-        );
-
-        $coreConfig->add(
-            'shared=>PMPTopicCalcStat',
-            '\\ForkBB\\Models\\PM\\CalcStat::class'
-        );
-
-        $coreConfig->add(
-            'shared=>PMModelLoad',
-            '\\ForkBB\\Models\\PM\\Load::class'
-        );
-
-        $coreConfig->add(
-            'shared=>PMModelSave',
-            '\\ForkBB\\Models\\PM\\Save::class'
-        );
-
-        $coreConfig->add(
-            'shared=>PMModelDelete',
-            '\\ForkBB\\Models\\PM\\Delete::class'
-        );
-
-        $coreConfig->add(
-            'shared=>PMModelUpdateUsername',
-            '\\ForkBB\\Models\\PM\\UpdateUsername::class'
-        );
-
-        $coreConfig->save();
-
-        $this->c->DB->dropTable('pm_rnd');
-        $this->c->DB->dropTable('pm_posts');
-        $this->c->DB->dropTable('pm_topics');
-
-        // pm_posts
-        $schema = [
-            'FIELDS' => [
-                'id'           => ['SERIAL', false],
-                'poster'       => ['VARCHAR(190)', false, ''],
-                'poster_id'    => ['INT(10) UNSIGNED', false, 0],
-                'poster_ip'    => ['VARCHAR(45)', false, ''],
-                'message'      => ['TEXT', false],
-                'hide_smilies' => ['TINYINT(1)', false, 0],
-                'posted'       => ['INT(10) UNSIGNED', false, 0],
-                'edited'       => ['INT(10) UNSIGNED', false, 0],
-                'topic_id'     => ['INT(10) UNSIGNED', false, 0],
-            ],
-            'PRIMARY KEY' => ['id'],
-            'INDEXES' => [
-                'topic_id_idx' => ['topic_id'],
-            ],
-        ];
-        $this->c->DB->createTable('pm_posts', $schema);
-
-        // pm_topics
-        $schema = [
-            'FIELDS' => [
-                'id'            => ['SERIAL', false],
-                'subject'       => ['VARCHAR(255)', false, ''],
-                'poster'        => ['VARCHAR(190)', false, ''],
-                'poster_id'     => ['INT(10) UNSIGNED', false, 0],
-                'poster_status' => ['TINYINT UNSIGNED', false, 0],
-                'poster_visit'  => ['INT(10) UNSIGNED', false, 0],
-                'target'        => ['VARCHAR(190)', false, ''],
-                'target_id'     => ['INT(10) UNSIGNED', false, 0],
-                'target_status' => ['TINYINT UNSIGNED', false, 0],
-                'target_visit'  => ['INT(10) UNSIGNED', false, 0],
-                'num_replies'   => ['INT(10) UNSIGNED', false, 0],
-                'first_post_id' => ['INT(10) UNSIGNED', false, 0],
-                'last_post'     => ['INT(10) UNSIGNED', false, 0],
-                'last_post_id'  => ['INT(10) UNSIGNED', false, 0],
-                'last_number'   => ['TINYINT UNSIGNED', false, 0],
-            ],
-            'PRIMARY KEY' => ['id'],
-            'INDEXES' => [
-                'last_post_idx'        => ['last_post'],
-                'poster_id_status_idx' => ['poster_id', 'poster_status'],
-                'target_id_status_idx' => ['target_id', 'target_status'],
-            ],
-        ];
-        $this->c->DB->createTable('pm_topics', $schema);
-
-        return null;
-    }
-
-    /**
-     * rev.38 to rev.39
-     */
-    protected function stageNumber38(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'shared=>Files=>drivers',
-            [
-                '\\ForkBB\\Core\\Image\\ImagickDriver::class',
-                '\\ForkBB\\Core\\Image\\GDDriver::class',
-                '\\ForkBB\\Core\\Image\\DefaultDriver::class',
-            ]
-        );
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.39 to rev.40
-     */
-    protected function stageNumber39(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $result = $coreConfig->delete('GROUP_ADMIN');
-        $result = $coreConfig->delete('GROUP_MOD');
-        $result = $coreConfig->delete('GROUP_GUEST');
-        $result = $coreConfig->delete('GROUP_MEMBER');
-
-        $coreConfig->save();
-
-        return null;
-    }
-
-    /**
-     * rev.40 to rev.41
-     */
-    protected function stageNumber40(array $args): ?int
-    {
-        $coreConfig = new CoreConfig($this->configFile);
-
-        $coreConfig->add(
-            'shared=>UserManagerNormUsername',
-            '\\ForkBB\\Models\\User\\NormUsername::class'
-        );
-
-        $coreConfig->save();
-
-        $this->c->DB->addField('users', 'username_normal', 'VARCHAR(190)', false, '', 'username');
-
-        $confChange = [
-            'shared' => [
-                'UserManagerNormUsername' => \ForkBB\Models\User\NormUsername::class,
-            ],
-        ];
-
-        $this->c->config($confChange);
-
-        $query = 'SELECT id, username FROM ::users';
-        $users = $this->c->DB->query($query)->fetchAll(PDO::FETCH_KEY_PAIR);
-
-        $query = 'UPDATE ::users AS u
-            SET u.username_normal = ?s:norm
-            WHERE u.id=?i:id';
-
-        foreach ($users as $id => $username) {
-            $vars = [
-                ':id'   => $id,
-                ':norm' => $this->c->users->normUsername($username),
-            ];
-
-            $this->c->DB->exec($query, $vars);
-        }
-
-        return null;
-    }
 }

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

@@ -83,7 +83,7 @@ abstract class Users extends Admin
             return (int) $value;
         }, $selected);
         $bad = \array_filter($selected, function ($value) {
-            return $value < 2;
+            return $value < 1;
         });
 
         if (! empty($bad)) {

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

@@ -61,7 +61,7 @@ class Result extends Users
             ])->addRules([
                 'token'          => 'token:AdminUsersResult',
                 'users'          => 'required|array',
-                'users.*'        => 'required|integer|min:2|max:9999999999',
+                'users.*'        => 'required|integer|min:1|max:9999999999',
                 'ban'            => $this->c->userRules->banUsers ? 'checkbox' : 'absent',
                 'delete'         => $this->c->userRules->deleteUsers ? 'checkbox' : 'absent',
                 'change_group'   => $this->c->userRules->changeGroup ? 'checkbox' : 'absent',

+ 1 - 2
app/Models/Pages/PM.php

@@ -12,7 +12,6 @@ namespace ForkBB\Models\Pages;
 
 use ForkBB\Models\Page;
 use ForkBB\Models\PM\Cnst;
-use function \ForkBB\__;
 
 class PM extends Page
 {
@@ -29,7 +28,7 @@ class PM extends Page
             if ('' === \trim($second, '1234567890')) {
                 $second = (int) $second;
 
-                if ($second < 2) { // ???? вынести все в роутер?
+                if ($second < 1) { // ???? вынести все в роутер?
                     return $this->c->Message->message('Bad request');
                 }
             } elseif (

+ 3 - 3
app/Models/Post/Delete.php

@@ -100,13 +100,13 @@ class Delete extends Action
                 ':users' => $uidsToGuest,
             ];
             $query = 'UPDATE ::posts
-                SET poster_id=1
+                SET poster_id=0
                 WHERE poster_id IN (?ai:users)';
 
             $this->c->DB->exec($query, $vars);
 
             $query = 'UPDATE ::posts
-                SET editor_id=1
+                SET editor_id=0
                 WHERE editor_id IN (?ai:users)';
 
             $this->c->DB->exec($query, $vars);
@@ -134,7 +134,7 @@ class Delete extends Action
             $parents = $this->c->topics->loadByIds($tids, false);
 
             $query = 'UPDATE ::posts
-                SET editor_id=1
+                SET editor_id=0
                 WHERE editor_id IN (?ai:users)';
 
             $this->c->DB->exec($query, $vars);

+ 6 - 10
app/Models/Post/Post.php

@@ -64,22 +64,18 @@ class Post extends DataModel
      */
     protected function getuser(): User
     {
-        $user = $this->c->users->load($this->poster_id);
-
         if (
-            ! $user instanceof User
-            && 1 !== $this->poster_id // ???? может сменить id гостя?
+            $this->poster_id < 1
+            || ! ($user = $this->c->users->load($this->poster_id)) instanceof User
         ) {
-            $user = $this->c->users->load(1);
+            $user = $this->c->users->guest([
+                'username' => $this->poster,
+                'email'    => $this->poster_email,
+            ]);
         }
 
         if (! $user instanceof User) {
             throw new RuntimeException("No user data in post number {$this->id}");
-        } elseif ($user->isGuest) {
-            $user = clone $user;
-
-            $user->__email    = $this->poster_email;
-            $user->__username = $this->poster;
         }
 
         return $user;

+ 1 - 1
app/Models/Post/UserInfoFromIP.php

@@ -34,7 +34,7 @@ class UserInfoFromIP extends Action
         $ids    = [];
 
         while ($row = $stmt->fetch()) {
-            if (1 === $row['poster_id']) {
+            if ($row['poster_id'] < 1) {
                 $result[] = $row['poster'];
             } elseif (empty($ids[$row['poster_id']])) {
                 $result[]               = $row['poster_id'];

+ 1 - 0
app/Models/Post/View.php

@@ -62,6 +62,7 @@ class View extends Action
             }
         }
 
+        unset($userIds[0]); // гостя загружать не надо
         $this->c->users->loadByIds($userIds);
 
         $offset    = ($arg->page - 1) * $this->c->user->disp_posts;

+ 16 - 22
app/Models/Report/Report.php

@@ -40,20 +40,17 @@ class Report extends DataModel
      */
     protected function getauthor(): User
     {
-        if ($this->reported_by < 1) {
-            throw new RuntimeException('No author data');
-        }
-
-        $user = $this->c->users->load($this->reported_by);
-
         if (
-            ! $user instanceof User
-            || $user->isGuest
+            $this->reported_by < 1
+            || ! ($user = $this->c->users->load($this->reported_by)) instanceof User
         ) {
-            $user = $this->c->users->create();
+            $user = $this->c->users->guest([
+                'username' => '{User #' . $this->reported_by .'}',
+            ]);
+        }
 
-            $user->__id       = $this->reported_by;
-            $user->__username = '{User #' . $this->reported_by .'}';
+        if (! $user instanceof User) {
+            throw new RuntimeException('No author data');
         }
 
         return $user;
@@ -79,20 +76,17 @@ class Report extends DataModel
      */
     protected function getmarker(): User
     {
-        if ($this->zapped_by < 1) {
-            throw new RuntimeException('No marker data');
-        }
-
-        $user = $this->c->users->load($this->zapped_by);
-
         if (
-            ! $user instanceof User
-            || $user->isGuest
+            $this->zapped_by < 1
+            || ! ($user = $this->c->users->load($this->zapped_by)) instanceof User
         ) {
-            $user = $this->c->users->create();
+            $user = $this->c->users->guest([
+                'username' => '{User #' . $this->zapped_by .'}',
+            ]);
+        }
 
-            $user->__id       = $this->zapped_by;
-            $user->__username = '{User #' . $this->zapped_by .'}';
+        if (! $user instanceof User) {
+            throw new RuntimeException('No marker data');
         }
 
         return $user;

+ 2 - 2
app/Models/Topic/Delete.php

@@ -130,13 +130,13 @@ class Delete extends Action
                 ':users' => $uidsToGuest,
             ];
             $query = 'UPDATE ::topics
-                SET poster_id=1
+                SET poster_id=0
                 WHERE poster_id IN (?ai:users)';
 
             $this->c->DB->exec($query, $vars);
 
             $query = 'UPDATE ::topics
-                SET last_poster_id=1
+                SET last_poster_id=0
                 WHERE last_poster_id IN (?ai:users)';
 
             $this->c->DB->exec($query, $vars);

+ 9 - 13
app/Models/User/Current.php

@@ -28,12 +28,12 @@ class Current extends Action
 
         if (! $user->isGuest) {
             if (! $cookie->verifyUser($user)) {
-                $user = $this->load(1, $ip);
+                $user = $this->load(0, $ip);
             } elseif ($user->ip_check_type > 0) {
                 $hexIp = \bin2hex(\inet_pton($ip));
 
                 if (false === \strpos("|{$user->login_ip_cache}|", "|{$hexIp}|")) {
-                    $user = $this->load(1, $ip);
+                    $user = $this->load(0, $ip);
                 }
             }
         }
@@ -71,7 +71,7 @@ class Current extends Action
     {
         $data = null;
 
-        if ($id > 1) {
+        if ($id > 0) {
             $vars = [
                 ':id' => $id,
             ];
@@ -88,20 +88,16 @@ class Current extends Action
             $vars = [
                 ':ip' => $ip,
             ];
-            $query = 'SELECT u.*, g.*, o.logged, o.last_post, o.last_search
-                FROM ::users AS u
-                INNER JOIN ::groups AS g ON u.group_id=g.g_id
-                LEFT JOIN ::online AS o ON (o.user_id=1 AND o.ident=?s:ip)
-                WHERE u.id=1';
+            $query = 'SELECT o.logged, o.last_post, o.last_search
+                FROM ::online AS o
+                WHERE o.user_id=0 AND o.ident=?s:ip';
 
             $data = $this->c->DB->query($query, $vars)->fetch();
 
-            if (empty($data['id'])) {
-                throw new RuntimeException('Unable to fetch guest information. Your database must contain both a guest user and a guest user group');
-            }
+            return $this->manager->guest($data ?: []);
+        } else {
+            return $this->manager->create($data);
         }
-
-        return $this->manager->create($data);
     }
 
     /**

+ 12 - 6
app/Models/User/Filter.php

@@ -23,7 +23,7 @@ class Filter extends Action
     {
         $fields  = $this->c->dbMap->users;
         $orderBy = [];
-        $where   = ['u.id>1'];
+        $where   = [];
 
         foreach ($order as $field => $dir) {
             if (! isset($fields[$field])) {
@@ -98,11 +98,17 @@ class Filter extends Action
             }
         }
 
-        $where = \implode(' AND ', $where);
-        $query = "SELECT u.id
-            FROM ::users AS u
-            WHERE {$where}
-            ORDER BY {$orderBy}";
+        if (empty($where)) {
+            $query = "SELECT u.id
+                FROM ::users AS u
+                ORDER BY {$orderBy}";
+        } else {
+            $where = \implode(' AND ', $where);
+            $query = "SELECT u.id
+                FROM ::users AS u
+                WHERE {$where}
+                ORDER BY {$orderBy}";
+        }
 
         return $this->c->DB->query($query, $vars)->fetchAll(PDO::FETCH_COLUMN);
     }

+ 10 - 7
app/Models/User/Save.php

@@ -21,24 +21,25 @@ class Save extends Action
      */
     public function update(User $user): User
     {
-        if ($user->id < 1) {
-            throw new RuntimeException('The model does not have ID');
+        if (
+            (! $user->isGuest && $user->id < 1)
+            || ($user->isGuest && 0 !== $user->id)
+        ) {
+            throw new RuntimeException('Bad ID');
         }
 
         $modified = $user->getModified();
+
         if (empty($modified)) {
             return $user;
         }
 
         $values = $user->getAttrs();
 
-        if (
-            $user->isGuest
-            && ! $user->isUnverified
-        ) {
+        if ($user->isGuest) {
             $fileds = $this->c->dbMap->online;
             $table  = 'online';
-            $where  = 'user_id=1 AND ident=?s';
+            $where  = 'user_id=0 AND ident=?s';
         } else {
             $fileds = $this->c->dbMap->users;
             $table  = 'users';
@@ -113,6 +114,8 @@ class Save extends Action
     {
         if (null !== $user->id) {
             throw new RuntimeException('The model has ID');
+        } elseif ($user->isGuest) {
+            throw new RuntimeException('Unexpected guest');
         }
 
         // вычисление username_normal для нового пользователя

+ 2 - 2
app/Models/User/UpdateCountPosts.php

@@ -35,10 +35,10 @@ class UpdateCountPosts extends Action
             }
         }
         // сообщения гостя не считаем
-        unset($ids[1]);
+        unset($ids[0]); // ????
 
         if (empty($ids)) {
-            $where = 'u.id != 1';
+            $where = 'u.id > 0';
             $vars  = [];
         } else {
             $where = 'u.id IN (?ai:ids)';

+ 2 - 2
app/Models/User/UpdateCountTopics.php

@@ -35,10 +35,10 @@ class UpdateCountTopics extends Action
             }
         }
         // темы гостя не считаем
-        unset($ids[1]);
+        unset($ids[0]); // ????
 
         if (empty($ids)) {
-            $where = 'u.id != 1';
+            $where = 'u.id > 0';
             $vars  = [];
         } else {
             $where = 'u.id IN (?ai:ids)';

+ 7 - 8
app/Models/User/User.php

@@ -47,7 +47,7 @@ class User extends DataModel
      */
     protected function getisUnverified(): bool
     {
-        return $this->group_id === FORK_GROUP_UNVERIFIED;
+        return FORK_GROUP_UNVERIFIED === $this->group_id;
     }
 
     /**
@@ -55,9 +55,9 @@ class User extends DataModel
      */
     protected function getisGuest(): bool
     {
-        return $this->group_id === FORK_GROUP_GUEST
-            || $this->id < 2
-            || null === $this->group_id;
+        return FORK_GROUP_GUEST === $this->group_id
+            || null === $this->group_id
+            || $this->id < 1;
     }
 
     /**
@@ -65,7 +65,7 @@ class User extends DataModel
      */
     protected function getisAdmin(): bool
     {
-        return $this->group_id === FORK_GROUP_ADMIN;
+        return FORK_GROUP_ADMIN === $this->group_id;
     }
 
     /**
@@ -73,8 +73,7 @@ class User extends DataModel
      */
     protected function getisAdmMod(): bool
     {
-        return $this->group_id === FORK_GROUP_ADMIN
-            || 1 == $this->g_moderator;
+        return $this->isAdmin || 1 == $this->g_moderator;
     }
 
     /**
@@ -91,7 +90,7 @@ class User extends DataModel
      */
     public function isModerator(Model $model): bool
     {
-        if (1 != $this->g_moderator) {
+        if (1 !== $this->g_moderator) {
             return false;
         }
 

+ 42 - 0
app/Models/User/Users.php

@@ -12,9 +12,12 @@ namespace ForkBB\Models\User;
 
 use ForkBB\Models\Manager;
 use ForkBB\Models\User\User;
+use RuntimeException;
 
 class Users extends Manager
 {
+    const CACHE_NAME = 'guest';
+
     /**
      * Ключ модели для контейнера
      * @var string
@@ -129,4 +132,43 @@ class Users extends Manager
 
         return $id;
     }
+
+    /**
+     * Создает гостя
+     */
+    public function guest(array $attrs = []): User
+    {
+        $cache = $this->c->Cache->get(self::CACHE_NAME);
+
+        if (! \is_array($cache)) {
+            $cache = $this->c->groups->get(FORK_GROUP_GUEST)->getAttrs();
+
+            if (true !== $this->c->Cache->set(self::CACHE_NAME, $cache)) {
+                throw new RuntimeException('Unable to write value to cache - ' . self::CACHE_NAME);
+            }
+        }
+
+        return $this->create(
+            [
+                'id'          => 0,
+                'group_id'    => FORK_GROUP_GUEST,
+                'time_format' => 0,
+                'date_format' => 0,
+            ]
+            + $attrs
+            + $cache
+        );
+    }
+
+    /**
+     * Сбрасывает кеш гостя
+     */
+    public function resetGuest(): Users
+    {
+        if (true !== $this->c->Cache->delete(self::CACHE_NAME)) {
+            throw new RuntimeException('Unable to remove key from cache - ' . self::CACHE_NAME);
+        }
+
+        return $this;
+    }
 }

+ 1 - 1
app/bootstrap.php

@@ -58,7 +58,7 @@ if (
 }
 $c->PUBLIC_URL = $c->BASE_URL . $forkPublicPrefix;
 
-$c->FORK_REVISION = 42;
+$c->FORK_REVISION = 43;
 $c->START         = $forkStart;
 $c->DIR_APP       = __DIR__;
 $c->DIR_PUBLIC    = \realpath(__DIR__ . '/../public');

+ 1 - 5
app/config/install.php

@@ -64,14 +64,10 @@ return [
             'class' => \ForkBB\Core\Secury::class,
             'hmac'  => '%HMAC%',
         ],
-        'FileCache' => [
+        'Cache' => [
             'class'     => \ForkBB\Core\Cache\FileCache::class,
             'cache_dir' => '%DIR_CACHE%',
         ],
-        'Cache' => [
-            'class'    => \ForkBB\Core\Cache::class,
-            'provider' => '@FileCache',
-        ],
         'Validator' => \ForkBB\Core\Validator::class,
         'View' => [
             'class'     => \ForkBB\Core\View::class,

+ 3 - 3
app/config/main.dist.php

@@ -223,9 +223,9 @@ return [
         'Forums/save'           => \ForkBB\Models\Forum\Save::class,
         'Forums/updateUsername' => \ForkBB\Models\Forum\UpdateUsername::class,
 
-        'Group/delete' => \ForkBB\Models\Group\Delete::class,
-        'Group/perm'   => \ForkBB\Models\Group\Perm::class,
-        'Group/save'   => \ForkBB\Models\Group\Save::class,
+        'Groups/delete' => \ForkBB\Models\Group\Delete::class,
+        'Groups/perm'   => \ForkBB\Models\Group\Perm::class,
+        'Groups/save'   => \ForkBB\Models\Group\Save::class,
 
         'Online/info'           => \ForkBB\Models\Online\Info::class,
         'Online/updateUsername' => \ForkBB\Models\Online\UpdateUsername::class,