Visman 7 gadi atpakaļ
vecāks
revīzija
d6e07a8f28
48 mainītis faili ar 800 papildinājumiem un 625 dzēšanām
  1. 5 4
      app/Controllers/Routing.php
  2. 0 163
      app/Core/FuncAll.php
  3. 0 1
      app/Core/Lang.php
  4. 1 1
      app/Core/Validator.php
  5. 15 2
      app/Core/View.php
  6. 6 1
      app/Models/Config/Save.php
  7. 0 20
      app/Models/Model.php
  8. 14 98
      app/Models/Page.php
  9. 13 13
      app/Models/Pages/Admin.php
  10. 58 58
      app/Models/Pages/Admin/Groups.php
  11. 1 1
      app/Models/Pages/Admin/Index.php
  12. 6 6
      app/Models/Pages/Admin/Statistics.php
  13. 22 22
      app/Models/Pages/Auth.php
  14. 1 5
      app/Models/Pages/Ban.php
  15. 3 3
      app/Models/Pages/CrumbTrait.php
  16. 9 12
      app/Models/Pages/Debug.php
  17. 209 0
      app/Models/Pages/Edit.php
  18. 1 1
      app/Models/Pages/Forum.php
  19. 1 1
      app/Models/Pages/Index.php
  20. 38 38
      app/Models/Pages/Install.php
  21. 1 1
      app/Models/Pages/Maintenance.php
  22. 2 2
      app/Models/Pages/Message.php
  23. 7 7
      app/Models/Pages/Post.php
  24. 26 17
      app/Models/Pages/PostFormTrait.php
  25. 22 11
      app/Models/Pages/PostValidatorTrait.php
  26. 2 2
      app/Models/Pages/Redirect.php
  27. 18 18
      app/Models/Pages/Register.php
  28. 5 5
      app/Models/Pages/Rules.php
  29. 5 4
      app/Models/Pages/Topic.php
  30. 65 34
      app/Models/Post.php
  31. 8 7
      app/Models/Post/Load.php
  32. 2 2
      app/Models/Topic.php
  33. 6 6
      app/Models/User.php
  34. 1 0
      app/bootstrap.php
  35. 8 8
      app/config/defaultBBCode.php
  36. 160 9
      app/functions.php
  37. 3 0
      app/lang/English/post.po
  38. 3 0
      app/lang/Russian/post.po
  39. 1 1
      app/lang/Russian/topic.po
  40. 2 2
      app/templates/admin/statistics.tpl
  41. 1 1
      app/templates/ban.tpl
  42. 5 5
      app/templates/forum.tpl
  43. 3 3
      app/templates/layouts/debug.tpl
  44. 5 5
      app/templates/layouts/stats.tpl
  45. 4 4
      app/templates/layouts/subforums.tpl
  46. 1 1
      app/templates/post.tpl
  47. 20 11
      app/templates/topic.tpl
  48. 11 9
      public/style/ForkBB/style.css

+ 5 - 4
app/Controllers/Routing.php

@@ -91,10 +91,11 @@ class Routing
             $r->add('GET',  '/topic/{id:[1-9]\d*}/new/reply[/{quote:[1-9]\d*}]', 'Post:newReply', 'NewReply');
             $r->add('POST', '/topic/{id:[1-9]\d*}/new/reply', 'Post:newReplyPost');
             // сообщения
-            $r->add('GET', '/post/{id:[1-9]\d*}#p{id}', 'Topic:viewPost', 'ViewPost');
-            $r->add('GET', '/post/{id:[1-9]\d*}/delete', 'Delete:delete', 'DeletePost'); //????
-            $r->add('GET', '/post/{id:[1-9]\d*}/edit', 'Edit:edit', 'EditPost'); //????
-            $r->add('GET', '/post/{id:[1-9]\d*}/report', 'Report:report', 'ReportPost'); //????
+            $r->add('GET',  '/post/{id:[1-9]\d*}#p{id}', 'Topic:viewPost', 'ViewPost');
+            $r->add('GET',  '/post/{id:[1-9]\d*}/edit', 'Edit:edit', 'EditPost');
+            $r->add('POST', '/post/{id:[1-9]\d*}/edit', 'Edit:editPost');
+            $r->add('GET',  '/post/{id:[1-9]\d*}/delete', 'Delete:delete', 'DeletePost');
+            $r->add('GET',  '/post/{id:[1-9]\d*}/report', 'Report:report', 'ReportPost');
 
         }
         // админ и модератор

+ 0 - 163
app/Core/FuncAll.php

@@ -1,163 +0,0 @@
-<?php
-
-namespace ForkBB\Core;
-
-use ForkBB\Models\Model;
-
-class FuncAll
-{
-    /**
-     * Контейнер
-     * @var Container
-     */
-    protected $c;
-
-    /**
-     * Модель вызова
-     * @var Model
-     */
-    protected $model;
-
-    /**
-     * Метод вызова
-     * @var string
-     */
-    protected $method;
-
-    /**
-     * Аргументы вызова
-     * @var array
-     */
-    protected $args;
-
-    /**
-     * Конструктор
-     *
-     * @param Container $container
-     */
-    public function __construct(Container $container)
-    {
-        $this->c = $container;
-    }
-
-    /**
-     * Настройка вызова
-     * 
-     * @param string $method
-     * @param Model $model
-     * @param array ...$args
-     * 
-     * @return FuncAll
-     */
-    public function setModel($method, Model $model, ...$args) 
-    {
-        $this->model  = $model;
-        $this->method = $method;
-        $this->args   = $args;
-        return $this;
-    }
-
-    /**
-     * Обработака вызова
-     * 
-     * @param string $name
-     * 
-     * @return mixed
-     */
-    public function __get($name)
-    {
-        $data = $this->model->{$name};
-
-        return $this->{$this->method}($data, ...$this->args);
-    }
-
-    /**
-     * Цензура
-     * 
-     * @param string $str
-     * 
-     * @return string
-     */
-    public function cens($str)
-    {
-        return $this->c->censorship->censor($str);
-    }
-
-    /**
-     * Возвращает число в формате языка текущего пользователя
-     *
-     * @param mixed $number
-     * @param int $decimals
-     *
-     * @return string
-     */
-    protected function num($number, $decimals = 0)
-    {
-        return is_numeric($number)
-            ? number_format($number, $decimals, __('lang_decimal_point'), __('lang_thousands_sep'))
-            : 'not a number';
-    }
-
-    /**
-     * Возвращает даты/время в формате текущего пользователя
-     *
-     * @param int $timestamp
-     * @param bool $dateOnly
-     * @param string $dateFormat
-     * @param string $timeFormat
-     * @param bool $timeOnly
-     * @param bool $noText
-     *
-     * @return string
-     */
-    protected function dt($timestamp, $dateOnly = false, $dateFormat = null, $timeFormat = null, $timeOnly = false, $noText = false)
-    {
-        if (empty($timestamp)) {
-            return __('Never');
-        }
-
-        $user = $this->c->user;
-
-        $diff = ($user->timezone + $user->dst) * 3600;
-        $timestamp += $diff;
-
-        if (null === $dateFormat) {
-            $dateFormat = $this->c->DATE_FORMATS[$user->date_format];
-        }
-        if(null === $timeFormat) {
-            $timeFormat = $this->c->TIME_FORMATS[$user->time_format];
-        }
-
-        $date = gmdate($dateFormat, $timestamp);
-
-        if(! $noText) {
-            $now = time() + $diff;
-
-            if ($date == gmdate($dateFormat, $now)) {
-                $date = __('Today');
-            } elseif ($date == gmdate($dateFormat, $now - 86400)) {
-                $date = __('Yesterday');
-            }
-        }
-
-        if ($dateOnly) {
-            return $date;
-        } elseif ($timeOnly) {
-            return gmdate($timeFormat, $timestamp);
-        } else {
-            return $date . ' ' . gmdate($timeFormat, $timestamp);
-        }
-    }
-
-    /**
-     * Преобразует timestamp в YYYY-MM-DDTHH:mm:ss.sssZ
-     * 
-     * @param int $timestamp
-     * 
-     * @return string
-     */
-    public function utc($timestamp)
-    {
-        return gmdate('Y-m-d\TH:i:s\Z', $timestamp);
-    }
-}

+ 0 - 1
app/Core/Lang.php

@@ -33,7 +33,6 @@ class Lang
     public function __construct(Container $container)
     {
         $this->c = $container;
-        __($this);
     }
 
     /**

+ 1 - 1
app/Core/Validator.php

@@ -331,7 +331,7 @@ class Validator
             $error = $error[0];
         }
 
-        $this->errors[$type][] = __($error, [':alias' => $alias, ':attr' => $attr]);
+        $this->errors[$type][] = \ForkBB\__($error, [':alias' => $alias, ':attr' => $attr]);
         $this->error           = true;
     }
 

+ 15 - 2
app/Core/View.php

@@ -53,10 +53,23 @@ class View extends Dirk
      */
     protected function compileTransformations($value)
     {
+        $perfix = <<<'EOD'
+<?php
+
+use function \ForkBB\__;
+use function \ForkBB\cens;
+use function \ForkBB\num;
+use function \ForkBB\dt;
+use function \ForkBB\utc;
+use function \ForkBB\size;
+
+?>
+EOD;
+
         if (strpos($value, '<!-- inline -->') === false) {
-            return $value;
+            return $perfix . $value;
         }
-        return preg_replace_callback(
+        return $perfix . preg_replace_callback(
             '%<!-- inline -->([^<]*(?:<(?!!-- endinline -->)[^<]*)*+)(?:<!-- endinline -->)?%',
             function ($matches) {
                 return preg_replace('%\h*\R\s*%', '', $matches[1]);

+ 6 - 1
app/Models/Config/Save.php

@@ -26,9 +26,14 @@ class Save extends MethodModel
                 ':name'  => $name
             ];
             //????
+            //????
             $count = $this->c->DB->exec('UPDATE ::config SET conf_value=?s:value WHERE conf_name=?s:name', $vars);
+            //????
+            //????
             if ($count === 0) {
-                $this->c->DB->exec('INSERT INTO ::config (conf_name, conf_value) VALUES (?s:name, ?s:value)', $vars);
+                //????
+                //????
+                $this->c->DB->exec('INSERT INTO ::config (conf_name, conf_value) SELECT ?s:name, ?s:value FROM ::groups WHERE NOT EXISTS (SELECT 1 FROM ::config WHERE conf_name=?s:name) LIMIT 1', $vars);
             }
         }
         $this->c->Cache->delete('config');

+ 0 - 20
app/Models/Model.php

@@ -122,24 +122,4 @@ class Model
             return $factory->$name(...$args);
         }
     }
-
-    public function cens()
-    {
-        return $this->c->FuncAll->setModel('cens', $this);
-    }
-
-    public function num($decimals = 0)
-    {
-        return $this->c->FuncAll->setModel('num', $this, $decimals);
-    }
-
-    public function dt($dateOnly = false, $dateFormat = null, $timeFormat = null, $timeOnly = false, $noText = false)
-    {
-        return $this->c->FuncAll->setModel('dt', $this, $dateOnly, $dateFormat, $timeFormat, $timeOnly, $noText);
-    }
-
-    public function utc()
-    {
-        return $this->c->FuncAll->setModel('utc', $this);
-    }
 }

+ 14 - 98
app/Models/Page.php

@@ -6,7 +6,7 @@ use ForkBB\Core\Container;
 use ForkBB\Models\Model;
 use RuntimeException;
 
-abstract class Page extends Model
+class Page extends Model
 {
     /**
      * Конструктор
@@ -57,42 +57,42 @@ abstract class Page extends Model
         $r = $this->c->Router;
 
         $nav = [
-            'index' => [$r->link('Index'), __('Index')]
+            'index' => [$r->link('Index'), \ForkBB\__('Index')]
         ];
 
         if ($user->g_read_board == '1' && $user->g_view_users == '1') {
-            $nav['userlist'] = [$r->link('Userlist'), __('User list')];
+            $nav['userlist'] = [$r->link('Userlist'), \ForkBB\__('User list')];
         }
 
         if ($this->c->config->o_rules == '1' && (! $user->isGuest || $user->g_read_board == '1' || $this->c->config->o_regs_allow == '1')) {
-            $nav['rules'] = [$r->link('Rules'), __('Rules')];
+            $nav['rules'] = [$r->link('Rules'), \ForkBB\__('Rules')];
         }
 
         if ($user->g_read_board == '1' && $user->g_search == '1') {
-            $nav['search'] = [$r->link('Search'), __('Search')];
+            $nav['search'] = [$r->link('Search'), \ForkBB\__('Search')];
         }
 
         if ($user->isGuest) {
-            $nav['register'] = [$r->link('Register'), __('Register')];
-            $nav['login'] = [$r->link('Login'), __('Login')];
+            $nav['register'] = [$r->link('Register'), \ForkBB\__('Register')];
+            $nav['login'] = [$r->link('Login'), \ForkBB\__('Login')];
         } else {
             $nav['profile'] = [$r->link('User', [
                 'id' => $user->id,
                 'name' => $user->username,
-            ]), __('Profile')];
+            ]), \ForkBB\__('Profile')];
             // New PMS
             if ($this->c->config->o_pms_enabled == '1' && ($user->isAdmin || $user->messages_new > 0)) { //????
-                $nav['pmsnew'] = ['pmsnew.php', __('PM')]; //'<li id="nav"'.((PUN_ACTIVE_PAGE == 'pms_new' || $user['messages_new'] > 0) ? ' class="isactive"' : '').'><a href="pmsnew.php">'.__('PM').(($user['messages_new'] > 0) ? ' (<span'.((empty($this->c->config->o_pms_flasher) || PUN_ACTIVE_PAGE == 'pms_new') ? '' : ' class="remflasher"' ).'>'.$user['messages_new'].'</span>)' : '').'</a></li>';
+                $nav['pmsnew'] = ['pmsnew.php', \ForkBB\__('PM')]; //'<li id="nav"'.((PUN_ACTIVE_PAGE == 'pms_new' || $user['messages_new'] > 0) ? ' class="isactive"' : '').'><a href="pmsnew.php">'.\ForkBB\__('PM').(($user['messages_new'] > 0) ? ' (<span'.((empty($this->c->config->o_pms_flasher) || PUN_ACTIVE_PAGE == 'pms_new') ? '' : ' class="remflasher"' ).'>'.$user['messages_new'].'</span>)' : '').'</a></li>';
             }
             // New PMS
 
             if ($user->isAdmMod) {
-                $nav['admin'] = [$r->link('Admin'), __('Admin')];
+                $nav['admin'] = [$r->link('Admin'), \ForkBB\__('Admin')];
             }
 
             $nav['logout'] = [$r->link('Logout', [
                 'token' => $this->c->Csrf->create('Logout'),
-            ]), __('Logout')];
+            ]), \ForkBB\__('Logout')];
         }
 
         if ($user->g_read_board == '1' && $this->c->config->o_additional_navlinks != '') {
@@ -124,7 +124,7 @@ abstract class Page extends Model
     protected function maintenance()
     {
         if ($this->c->config->o_maintenance == '1' && $this->c->user->isAdmin) {
-            $this->a['fIswev']['w']['maintenance'] = __('Maintenance mode enabled', $this->c->Router->link('AdminOptions', ['#' => 'maintenance']));
+            $this->a['fIswev']['w']['maintenance'] = \ForkBB\__('Maintenance mode enabled', $this->c->Router->link('AdminOptions', ['#' => 'maintenance']));
         }
     }
 
@@ -142,7 +142,7 @@ abstract class Page extends Model
             $titles = $this->titles;
         }
         $titles[] = $this->c->config->o_board_title;
-        return implode(__('Title separator'), $titles);
+        return implode(\ForkBB\__('Title separator'), $titles);
     }
 
     /**
@@ -229,7 +229,7 @@ abstract class Page extends Model
      * Дописывает в массив титула страницы новый элемент
      * $this->titles
      *
-     * @param string @val
+     * @param string $val
      */
     public function settitles($val)
     {
@@ -239,88 +239,4 @@ abstract class Page extends Model
             $this->a['titles'][] = $val;
         }
     }
-
-    /**
-     * Возвращает размер в байтах, Кбайтах, ...
-     *
-     * @param int $size
-     *
-     * @return string
-     */
-    protected function size($size)
-    {
-        $units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'];
-
-        for ($i = 0; $size > 1024; ++$i) {
-            $size /= 1024;
-        }
-
-        return __('Size unit '.$units[$i], round($size, 2));
-    }
-
-    /**
-     * Возвращает число в формате языка текущего пользователя
-     *
-     * @param mixed $number
-     * @param int $decimals
-     *
-     * @return string
-     */
-    protected function number($number, $decimals = 0)
-    {
-        return is_numeric($number)
-            ? number_format($number, $decimals, __('lang_decimal_point'), __('lang_thousands_sep'))
-            : 'not a number';
-    }
-
-    /**
-     * Возвращает время в формате текущего пользователя
-     *
-     * @param int|string $timestamp
-     * @param bool $dateOnly
-     * @param string $dateFormat
-     * @param string $timeFormat
-     * @param bool $timeOnly
-     * @param bool $noText
-     *
-     * @return string
-     */
-    protected function time($timestamp, $dateOnly = false, $dateFormat = null, $timeFormat = null, $timeOnly = false, $noText = false)
-    {
-        if (empty($timestamp)) {
-            return __('Never');
-        }
-
-        $user = $this->c->user;
-
-        $diff = ($user->timezone + $user->dst) * 3600;
-        $timestamp += $diff;
-
-        if (null === $dateFormat) {
-            $dateFormat = $this->c->DATE_FORMATS[$user->date_format];
-        }
-        if(null === $timeFormat) {
-            $timeFormat = $this->c->TIME_FORMATS[$user->time_format];
-        }
-
-        $date = gmdate($dateFormat, $timestamp);
-
-        if(! $noText) {
-            $now = time() + $diff;
-
-            if ($date == gmdate($dateFormat, $now)) {
-                $date = __('Today');
-            } elseif ($date == gmdate($dateFormat, $now - 86400)) {
-                $date = __('Yesterday');
-            }
-        }
-
-        if ($dateOnly) {
-            return $date;
-        } elseif ($timeOnly) {
-            return gmdate($timeFormat, $timestamp);
-        } else {
-            return $date . ' ' . gmdate($timeFormat, $timestamp);
-        }
-    }
 }

+ 13 - 13
app/Models/Pages/Admin.php

@@ -5,7 +5,7 @@ namespace ForkBB\Models\Pages;
 use ForkBB\Core\Container;
 use ForkBB\Models\Page;
 
-abstract class Admin extends Page
+class Admin extends Page
 {
     /**
      * Конструктор
@@ -45,26 +45,26 @@ abstract class Admin extends Page
 
         $nav = [
             'Moderator menu'  => [
-                'index' => [$r->link('Admin'), __('Admin index')],
-                'users' => ['admin_users.php', __('Users')],
+                'index' => [$r->link('Admin'), \ForkBB\__('Admin index')],
+                'users' => ['admin_users.php', \ForkBB\__('Users')],
             ],
         ];
         if ($user->isAdmin || $user->g_mod_ban_users == '1') {
-            $nav['Moderator menu']['bans'] = ['admin_bans.php', __('Bans')];
+            $nav['Moderator menu']['bans'] = ['admin_bans.php', \ForkBB\__('Bans')];
         }
         if ($user->isAdmin || $this->c->config->o_report_method == '0' || $this->c->config->o_report_method == '2') {
-            $nav['Moderator menu']['reports'] = ['admin_reports.php', __('Reports')];
+            $nav['Moderator menu']['reports'] = ['admin_reports.php', \ForkBB\__('Reports')];
         }
 
         if ($user->isAdmin) {
             $nav['Admin menu'] = [
-                'options'     => ['admin_options.php', __('Admin options')],
-                'permissions' => ['admin_permissions.php', __('Permissions')],
-                'categories'  => ['admin_categories.php', __('Categories')],
-                'forums'      => ['admin_forums.php', __('Forums')],
-                'groups'      => [$r->link('AdminGroups'), __('User groups')],
-                'censoring'   => ['admin_censoring.php', __('Censoring')],
-                'maintenance' => ['admin_maintenance.php', __('Maintenance')]
+                'options'     => ['admin_options.php', \ForkBB\__('Admin options')],
+                'permissions' => ['admin_permissions.php', \ForkBB\__('Permissions')],
+                'categories'  => ['admin_categories.php', \ForkBB\__('Categories')],
+                'forums'      => ['admin_forums.php', \ForkBB\__('Forums')],
+                'groups'      => [$r->link('AdminGroups'), \ForkBB\__('User groups')],
+                'censoring'   => ['admin_censoring.php', \ForkBB\__('Censoring')],
+                'maintenance' => ['admin_maintenance.php', \ForkBB\__('Maintenance')]
             ];
         }
 
@@ -84,7 +84,7 @@ abstract class Admin extends Page
         if (empty($titles)) {
             $titles = $this->titles;
         }
-        $titles[] = __('Admin title');
+        $titles[] = \ForkBB\__('Admin title');
         return parent::getPageTitle($titles);
     }
 }

+ 58 - 58
app/Models/Pages/Admin/Groups.php

@@ -135,7 +135,7 @@ class Groups extends Admin
         }
         $this->c->config->o_default_user_group = $v->defaultgroup;
         $this->c->config->save();
-        return $this->c->Redirect->page('AdminGroups')->message(__('Default group redirect'));
+        return $this->c->Redirect->page('AdminGroups')->message(\ForkBB\__('Default group redirect'));
     }
 
     /**
@@ -153,7 +153,7 @@ class Groups extends Admin
         if (empty($args['base'])) {
             $v = $this->c->Validator->setRules([
                 'token'     => 'token:AdminGroupsNew',
-                'basegroup' => ['required|integer|in:' . implode(',', array_keys($this->grBase)), __('New group label')]
+                'basegroup' => ['required|integer|in:' . implode(',', array_keys($this->grBase)), \ForkBB\__('New group label')]
             ]);
 
             if (! $v->validation($_POST)) {
@@ -202,7 +202,7 @@ class Groups extends Admin
         $this->formAction = $this->c->Router->link($marker, $vars);
         $this->formToken  = $this->c->Csrf->create($marker, $vars);
         $this->form       = $this->viewForm($id, $groups[$args['id']]);
-        $this->warn       = empty($groups[$args['id']]['g_moderator']) ? null : __('Moderator info');
+        $this->warn       = empty($groups[$args['id']]['g_moderator']) ? null : \ForkBB\__('Moderator info');
         $this->tabindex   = 0;
 
         return $this;
@@ -331,7 +331,7 @@ class Groups extends Admin
 
         return $this->c->Redirect
             ->page('AdminGroups')
-            ->message($id === -1 ? __('Group added redirect') : __('Group edited redirect'));
+            ->message($id === -1 ? \ForkBB\__('Group added redirect') : \ForkBB\__('Group edited redirect'));
     }
 
     /**
@@ -348,15 +348,15 @@ class Groups extends Admin
                 'type' => 'text',
                 'maxlength' => 50,
                 'value' => isset($data['g_title']) ? $data['g_title'] : '',
-                'title' => __('Group title label'),
+                'title' => \ForkBB\__('Group title label'),
                 'required' => true,
             ],
             'g_user_title' => [
                 'type' => 'text',
                 'maxlength' => 50,
                 'value' => isset($data['g_user_title']) ? $data['g_user_title'] : '',
-                'title' => __('User title label'),
-                'info' => __('User title help', $id == $this->c->GROUP_GUEST ? __('Guest') : __('Member')),
+                'title' => \ForkBB\__('User title label'),
+                'info' => \ForkBB\__('User title help', $id == $this->c->GROUP_GUEST ? \ForkBB\__('Guest') : \ForkBB\__('Member')),
             ],
         ];
 
@@ -365,7 +365,7 @@ class Groups extends Admin
         }
 
         if ($id !== $this->c->GROUP_GUEST) {
-            $options = [0 => __('Disable promotion')];
+            $options = [0 => \ForkBB\__('Disable promotion')];
 
             foreach ($this->groups as $group) {
                 if ($group['g_id'] == $id || empty($this->grBase[$group['g_id']])) {
@@ -378,63 +378,63 @@ class Groups extends Admin
                 'type' => 'select',
                 'options' => $options,
                 'value' => isset($data['g_promote_next_group']) ? $data['g_promote_next_group'] : 0,
-                'title' => __('Promote users label'),
-                'info' => __('Promote users help', __('Disable promotion')),
+                'title' => \ForkBB\__('Promote users label'),
+                'info' => \ForkBB\__('Promote users help', \ForkBB\__('Disable promotion')),
             ];
             $form['g_promote_min_posts'] = [
                 'type' => 'number',
                 'min' => 0,
                 'max' => 9999999999,
                 'value' => isset($data['g_promote_min_posts']) ? $data['g_promote_min_posts'] : 0,
-                'title' => __('Number for promotion label'),
-                'info' => __('Number for promotion help'),
+                'title' => \ForkBB\__('Number for promotion label'),
+                'info' => \ForkBB\__('Number for promotion help'),
             ];
         }
 
-        $y = __('Yes');
-        $n = __('No');
+        $y = \ForkBB\__('Yes');
+        $n = \ForkBB\__('No');
         if ($id !== $this->c->GROUP_GUEST && $id != $this->c->config->o_default_user_group) {
             $form['g_moderator'] = [
                 'type' => 'radio',
                 'value' => isset($data['g_moderator']) ? $data['g_moderator'] : 0,
                 'values' => [1 => $y, 0 => $n],
-                'title' => __('Mod privileges label'),
-                'info' => __('Mod privileges help'),
+                'title' => \ForkBB\__('Mod privileges label'),
+                'info' => \ForkBB\__('Mod privileges help'),
             ];
             $form['g_mod_edit_users'] = [
                 'type' => 'radio',
                 'value' => isset($data['g_mod_edit_users']) ? $data['g_mod_edit_users'] : 0,
                 'values' => [1 => $y, 0 => $n],
-                'title' => __('Edit profile label'),
-                'info' => __('Edit profile help'),
+                'title' => \ForkBB\__('Edit profile label'),
+                'info' => \ForkBB\__('Edit profile help'),
             ];
             $form['g_mod_rename_users'] = [
                 'type' => 'radio',
                 'value' => isset($data['g_mod_rename_users']) ? $data['g_mod_rename_users'] : 0,
                 'values' => [1 => $y, 0 => $n],
-                'title' => __('Rename users label'),
-                'info' => __('Rename users help'),
+                'title' => \ForkBB\__('Rename users label'),
+                'info' => \ForkBB\__('Rename users help'),
             ];
             $form['g_mod_change_passwords'] = [
                 'type' => 'radio',
                 'value' => isset($data['g_mod_change_passwords']) ? $data['g_mod_change_passwords'] : 0,
                 'values' => [1 => $y, 0 => $n],
-                'title' => __('Change passwords label'),
-                'info' => __('Change passwords help'),
+                'title' => \ForkBB\__('Change passwords label'),
+                'info' => \ForkBB\__('Change passwords help'),
             ];
             $form['g_mod_promote_users'] = [
                 'type' => 'radio',
                 'value' => isset($data['g_mod_promote_users']) ? $data['g_mod_promote_users'] : 0,
                 'values' => [1 => $y, 0 => $n],
-                'title' => __('Mod promote users label'),
-                'info' => __('Mod promote users help'),
+                'title' => \ForkBB\__('Mod promote users label'),
+                'info' => \ForkBB\__('Mod promote users help'),
             ];
             $form['g_mod_ban_users'] = [
                 'type' => 'radio',
                 'value' => isset($data['g_mod_ban_users']) ? $data['g_mod_ban_users'] : 0,
                 'values' => [1 => $y, 0 => $n],
-                'title' => __('Ban users label'),
-                'info' => __('Ban users help'),
+                'title' => \ForkBB\__('Ban users label'),
+                'info' => \ForkBB\__('Ban users help'),
             ];
         }
 
@@ -442,29 +442,29 @@ class Groups extends Admin
             'type' => 'radio',
             'value' => isset($data['g_read_board']) ? $data['g_read_board'] : 0,
             'values' => [1 => $y, 0 => $n],
-            'title' => __('Read board label'),
-            'info' => __('Read board help'),
+            'title' => \ForkBB\__('Read board label'),
+            'info' => \ForkBB\__('Read board help'),
         ];
         $form['g_view_users'] = [
             'type' => 'radio',
             'value' => isset($data['g_view_users']) ? $data['g_view_users'] : 0,
             'values' => [1 => $y, 0 => $n],
-            'title' => __('View user info label'),
-            'info' => __('View user info help'),
+            'title' => \ForkBB\__('View user info label'),
+            'info' => \ForkBB\__('View user info help'),
         ];
         $form['g_post_replies'] = [
             'type' => 'radio',
             'value' => isset($data['g_post_replies']) ? $data['g_post_replies'] : 0,
             'values' => [1 => $y, 0 => $n],
-            'title' => __('Post replies label'),
-            'info' => __('Post replies help'),
+            'title' => \ForkBB\__('Post replies label'),
+            'info' => \ForkBB\__('Post replies help'),
         ];
         $form['g_post_topics'] = [
             'type' => 'radio',
             'value' => isset($data['g_post_topics']) ? $data['g_post_topics'] : 0,
             'values' => [1 => $y, 0 => $n],
-            'title' => __('Post topics label'),
-            'info' => __('Post topics help'),
+            'title' => \ForkBB\__('Post topics label'),
+            'info' => \ForkBB\__('Post topics help'),
         ];
 
         if ($id !== $this->c->GROUP_GUEST) {
@@ -472,29 +472,29 @@ class Groups extends Admin
                 'type' => 'radio',
                 'value' => isset($data['g_edit_posts']) ? $data['g_edit_posts'] : 0,
                 'values' => [1 => $y, 0 => $n],
-                'title' => __('Edit posts label'),
-                'info' => __('Edit posts help'),
+                'title' => \ForkBB\__('Edit posts label'),
+                'info' => \ForkBB\__('Edit posts help'),
             ];
             $form['g_delete_posts'] = [
                 'type' => 'radio',
                 'value' => isset($data['g_delete_posts']) ? $data['g_delete_posts'] : 0,
                 'values' => [1 => $y, 0 => $n],
-                'title' => __('Delete posts label'),
-                'info' => __('Delete posts help'),
+                'title' => \ForkBB\__('Delete posts label'),
+                'info' => \ForkBB\__('Delete posts help'),
             ];
             $form['g_delete_topics'] = [
                 'type' => 'radio',
                 'value' => isset($data['g_delete_topics']) ? $data['g_delete_topics'] : 0,
                 'values' => [1 => $y, 0 => $n],
-                'title' => __('Delete topics label'),
-                'info' => __('Delete topics help'),
+                'title' => \ForkBB\__('Delete topics label'),
+                'info' => \ForkBB\__('Delete topics help'),
             ];
             $form['g_set_title'] = [
                 'type' => 'radio',
                 'value' => isset($data['g_set_title']) ? $data['g_set_title'] : 0,
                 'values' => [1 => $y, 0 => $n],
-                'title' => __('Set own title label'),
-                'info' => __('Set own title help'),
+                'title' => \ForkBB\__('Set own title label'),
+                'info' => \ForkBB\__('Set own title help'),
             ];
         }
 
@@ -502,22 +502,22 @@ class Groups extends Admin
             'type' => 'radio',
             'value' => isset($data['g_post_links']) ? $data['g_post_links'] : 0,
             'values' => [1 => $y, 0 => $n],
-            'title' => __('Post links label'),
-            'info' => __('Post links help'),
+            'title' => \ForkBB\__('Post links label'),
+            'info' => \ForkBB\__('Post links help'),
         ];
         $form['g_search'] = [
             'type' => 'radio',
             'value' => isset($data['g_search']) ? $data['g_search'] : 0,
             'values' => [1 => $y, 0 => $n],
-            'title' => __('User search label'),
-            'info' => __('User search help'),
+            'title' => \ForkBB\__('User search label'),
+            'info' => \ForkBB\__('User search help'),
         ];
         $form['g_search_users'] = [
             'type' => 'radio',
             'value' => isset($data['g_search_users']) ? $data['g_search_users'] : 0,
             'values' => [1 => $y, 0 => $n],
-            'title' => __('User list search label'),
-            'info' => __('User list search help'),
+            'title' => \ForkBB\__('User list search label'),
+            'info' => \ForkBB\__('User list search help'),
         ];
 
         if ($id !== $this->c->GROUP_GUEST) {
@@ -525,8 +525,8 @@ class Groups extends Admin
                 'type' => 'radio',
                 'value' => isset($data['g_send_email']) ? $data['g_send_email'] : 0,
                 'values' => [1 => $y, 0 => $n],
-                'title' => __('Send e-mails label'),
-                'info' => __('Send e-mails help'),
+                'title' => \ForkBB\__('Send e-mails label'),
+                'info' => \ForkBB\__('Send e-mails help'),
             ];
         }
 
@@ -535,16 +535,16 @@ class Groups extends Admin
             'min' => 0,
             'max' => 999999,
             'value' => isset($data['g_post_flood']) ? $data['g_post_flood'] : 0,
-            'title' => __('Post flood label'),
-            'info' => __('Post flood help'),
+            'title' => \ForkBB\__('Post flood label'),
+            'info' => \ForkBB\__('Post flood help'),
         ];
         $form['g_search_flood'] = [
             'type' => 'number',
             'min' => 0,
             'max' => 999999,
             'value' => isset($data['g_search_flood']) ? $data['g_search_flood'] : 0,
-            'title' => __('Search flood label'),
-            'info' => __('Search flood help'),
+            'title' => \ForkBB\__('Search flood label'),
+            'info' => \ForkBB\__('Search flood help'),
         ];
 
         if ($id !== $this->c->GROUP_GUEST) {
@@ -553,16 +553,16 @@ class Groups extends Admin
                 'min' => 0,
                 'max' => 999999,
                 'value' => isset($data['g_email_flood']) ? $data['g_email_flood'] : 0,
-                'title' => __('E-mail flood label'),
-                'info' => __('E-mail flood help'),
+                'title' => \ForkBB\__('E-mail flood label'),
+                'info' => \ForkBB\__('E-mail flood help'),
             ];
             $form['g_report_flood'] = [
                 'type' => 'number',
                 'min' => 0,
                 'max' => 999999,
                 'value' => isset($data['g_report_flood']) ? $data['g_report_flood'] : 0,
-                'title' => __('Report flood label'),
-                'info' => __('Report flood help'),
+                'title' => \ForkBB\__('Report flood label'),
+                'info' => \ForkBB\__('Report flood help'),
             ];
         }
 

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

@@ -16,7 +16,7 @@ class Index extends Admin
         $this->c->Lang->load('admin_index');
 
         $this->nameTpl  = 'admin/index';
-        $this->titles   = __('Admin index');
+        $this->titles   = \ForkBB\__('Admin index');
         $this->revision = $this->c->config->i_fork_revision;
         $this->linkStat = $this->c->Router->link('AdminStatistics');
 

+ 6 - 6
app/Models/Pages/Admin/Statistics.php

@@ -62,7 +62,7 @@ class Statistics extends Admin
         $this->c->Lang->load('admin_index');
 
         $this->nameTpl  = 'admin/statistics';
-        $this->titles   = __('Server statistics');
+        $this->titles   = \ForkBB\__('Server statistics');
         $this->isAdmin  = $this->c->user->isAdmin;
         $this->linkInfo = $this->c->Router->link('AdminInfo');
 
@@ -81,11 +81,11 @@ class Statistics extends Admin
             }
 
             $ave = @explode(' ', $ave);
-            $this->serverLoad = isset($ave[2]) ? $ave[0].' '.$ave[1].' '.$ave[2] : __('Not available');
+            $this->serverLoad = isset($ave[2]) ? $ave[0].' '.$ave[1].' '.$ave[2] : \ForkBB\__('Not available');
         } elseif (!in_array(PHP_OS, array('WINNT', 'WIN32')) && preg_match('%averages?: ([\d\.]+),?\s+([\d\.]+),?\s+([\d\.]+)%i', @exec('uptime'), $ave)) {
             $this->serverLoad = $ave[1].' '.$ave[2].' '.$ave[3];
         } else {
-            $this->serverLoad = __('Not available');
+            $this->serverLoad = \ForkBB\__('Not available');
         }
 
         // Get number of current visitors
@@ -93,8 +93,8 @@ class Statistics extends Admin
 
         $stat = $this->c->DB->statistics();
         $this->dbVersion = $stat['db'];
-        $this->tSize     = $this->size($stat['size']);
-        $this->tRecords  = $this->number($stat['records']);
+        $this->tSize     = $stat['size'];
+        $this->tRecords  = $stat['records'];
         unset($stat['db'], $stat['size'], $stat['records']);
         $this->tOther    = $stat;
 
@@ -112,7 +112,7 @@ class Statistics extends Admin
             $this->accelerator = 'XCache';
             $this->linkAcc     = 'https://xcache.lighttpd.net/';
         } else {
-            $this->accelerator = __('NA');
+            $this->accelerator = \ForkBB\__('NA');
             $this->linkAcc     = null;
         }
 

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

@@ -25,7 +25,7 @@ class Auth extends Page
     public function logout($args)
     {
         if (empty($args['token']) || ! $this->c->Csrf->verify($args['token'], 'Logout', $args)) {
-            return $this->c->Redirect->page('Index')->message(__('Bad token'));
+            return $this->c->Redirect->page('Index')->message(\ForkBB\__('Bad token'));
         }
 
         $this->c->Cookie->deleteUser();
@@ -33,7 +33,7 @@ class Auth extends Page
         $this->c->user->updateLastVisit();
 
         $this->c->Lang->load('auth');
-        return $this->c->Redirect->page('Index')->message(__('Logout redirect'));
+        return $this->c->Redirect->page('Index')->message(\ForkBB\__('Logout redirect'));
     }
 
     /**
@@ -59,7 +59,7 @@ class Auth extends Page
         $this->nameTpl    = 'login';
         $this->onlinePos  = 'login';
         $this->robots     = 'noindex';
-        $this->titles     = __('Login');
+        $this->titles     = \ForkBB\__('Login');
         $this->formAction = $this->c->Router->link('Login');
         $this->formToken  = $this->c->Csrf->create('Login');
         $this->forgetLink = $this->c->Router->link('Forget');
@@ -85,13 +85,13 @@ class Auth extends Page
         ])->setRules([
             'token'    => 'token:Login',
             'redirect' => 'referer:Index',
-            'username' => ['required|string', __('Username')],
-            'password' => ['required|string|login_process', __('Passphrase')],
+            'username' => ['required|string', \ForkBB\__('Username')],
+            'password' => ['required|string|login_process', \ForkBB\__('Passphrase')],
             'save'     => 'checkbox',
         ]);
 
         if ($v->validation($_POST)) {
-            return $this->c->Redirect->url($v->redirect)->message(__('Login redirect'));
+            return $this->c->Redirect->url($v->redirect)->message(\ForkBB\__('Login redirect'));
         } else {
             $this->fIswev = $v->getErrors();
             return $this->login([
@@ -170,7 +170,7 @@ class Auth extends Page
         $this->nameTpl    = 'passphrase_reset';
         $this->onlinePos  = 'passphrase_reset';
         $this->robots     = 'noindex';
-        $this->titles     = __('Passphrase reset');
+        $this->titles     = \ForkBB\__('Passphrase reset');
         $this->formAction = $this->c->Router->link('Forget');
         $this->formToken  = $this->c->Csrf->create('Forget');
         $this->email      = $args['_email'];
@@ -193,7 +193,7 @@ class Auth extends Page
             'token' => 'token:Forget',
             'email' => 'required|string:trim,lower|email|check_email',
         ])->setMessages([
-            'email.email' => __('Invalid email'),
+            'email.email' => \ForkBB\__('Invalid email'),
         ]);
 
         if (! $v->validation($_POST)) {
@@ -208,7 +208,7 @@ class Auth extends Page
         $link = $this->c->Router->link('ChangePassword', ['email' => $v->email, 'key' => $key, 'hash' => $hash]);
         $tplData = [
             'fRootLink' => $this->c->Router->link('Index'),
-            'fMailer' => __('Mailer', $this->c->config->o_board_title),
+            'fMailer' => \ForkBB\__('Mailer', $this->c->config->o_board_title),
             'username' => $this->tmpUser->username,
             'link' => $link,
         ];
@@ -219,7 +219,7 @@ class Auth extends Page
                 ->setFolder($this->c->DIR_LANG)
                 ->setLanguage($this->tmpUser->language)
                 ->setTo($v->email, $this->tmpUser->username)
-                ->setFrom($this->c->config->o_webmaster_email, __('Mailer', $this->c->config->o_board_title))
+                ->setFrom($this->c->config->o_webmaster_email, \ForkBB\__('Mailer', $this->c->config->o_board_title))
                 ->setTpl('passphrase_reset.tpl', $tplData)
                 ->send();
         } catch (MailException $e) {
@@ -230,9 +230,9 @@ class Auth extends Page
             $this->tmpUser->activate_string = $key;
             $this->tmpUser->last_email_sent = time();
             $this->tmpUser->update();
-            return $this->c->Message->message(__('Forget mail', $this->c->config->o_admin_email), false, 200);
+            return $this->c->Message->message(\ForkBB\__('Forget mail', $this->c->config->o_admin_email), false, 200);
         } else {
-            return $this->c->Message->message(__('Error mail', $this->c->config->o_admin_email), true, 200);
+            return $this->c->Message->message(\ForkBB\__('Error mail', $this->c->config->o_admin_email), true, 200);
         }
     }
 
@@ -261,7 +261,7 @@ class Auth extends Page
             $v->addError('Invalid email');
         // за последний час уже был запрос на этот email
         } elseif (! empty($user->last_email_sent) && time() - $user->last_email_sent < 3600) {
-            $v->addError(__('Email flood', (int) (($user->last_email_sent + 3600 - time()) / 60)), 'e');
+            $v->addError(\ForkBB\__('Email flood', (int) (($user->last_email_sent + 3600 - time()) / 60)), 'e');
         } else {
             $this->tmpUser = $user;
         }
@@ -288,7 +288,7 @@ class Auth extends Page
                 || $user->activate_string{0} !== 'p'
                 || ! hash_equals($user->activate_string, $args['key'])
             ) {
-                return $this->c->Message->message(__('Bad request'), false);
+                return $this->c->Message->message('Bad request', false);
             }
         }
 
@@ -299,14 +299,14 @@ class Auth extends Page
             $user->email_confirmed = 1;
             $user->update();
             $this->c->{'users_info update'};
-            $this->a['fIswev']['i'][] = __('Account activated');
+            $this->a['fIswev']['i'][] = \ForkBB\__('Account activated');
         }
 
         $this->fIndex     = 'login';
         $this->nameTpl    = 'change_passphrase';
         $this->onlinePos  = 'change_passphrase';
         $this->robots     = 'noindex';
-        $this->titles     = __('Passphrase reset');
+        $this->titles     = \ForkBB\__('Passphrase reset');
         $this->formAction = $this->c->Router->link('ChangePassword', $args);
         $this->formToken  = $this->c->Csrf->create('ChangePassword', $args);
 
@@ -329,7 +329,7 @@ class Auth extends Page
             || $user->activate_string{0} !== 'p'
             || ! hash_equals($user->activate_string, $args['key'])
         ) {
-            return $this->c->Message->message(__('Bad request'), false);
+            return $this->c->Message->message('Bad request', false);
         }
 
         $this->c->Lang->load('auth');
@@ -337,13 +337,13 @@ class Auth extends Page
         $v = $this->c->Validator;
         $v->setRules([
             'token'     => 'token:ChangePassword',
-            'password'  => ['required|string|min:16|password', __('New pass')],
-            'password2' => ['required|same:password', __('Confirm new pass')],
+            'password'  => ['required|string|min:16|password', \ForkBB\__('New pass')],
+            'password2' => ['required|same:password', \ForkBB\__('Confirm new pass')],
         ])->setArguments([
             'token' => $args,
         ])->setMessages([
-            'password.password'  => __('Pass format'),
-            'password2.same' => __('Pass not match'),
+            'password.password'  => \ForkBB\__('Pass format'),
+            'password2.same' => \ForkBB\__('Pass not match'),
         ]);
 
         if (! $v->validation($_POST)) {
@@ -358,7 +358,7 @@ class Auth extends Page
         $user->activate_string = null;
         $user->update();
 
-        $this->a['fIswev']['s'][] = __('Pass updated');
+        $this->a['fIswev']['s'][] = \ForkBB\__('Pass updated');
         return $this->login(['_redirect' => $this->c->Router->link('Index')]);
     }
 }

+ 1 - 5
app/Models/Pages/Ban.php

@@ -18,15 +18,11 @@ class Ban extends Page
     {
         $ban = $user->banInfo;
         
-        if (! empty($ban['expire'])) {
-            $ban['expire'] = strtolower($this->time($ban['expire'], true));
-        }
-
         $this->httpStatus = 403;
         $this->nameTpl    = 'ban';
 #       $this->onlinePos  = 'ban';
 #       $this->robots     = 'noindex';
-        $this->titles     = __('Info');
+        $this->titles     = \ForkBB\__('Info');
         $this->ban        = $ban;
         $this->adminEmail = $this->c->config->o_admin_email;
 

+ 3 - 3
app/Models/Pages/CrumbTrait.php

@@ -26,13 +26,13 @@ trait CrumbTrait
                     if (isset($arg->forum_name)) {
                         $name = $arg->forum_name;
                     } elseif (isset($arg->subject)) {
-                        $name = $arg->cens()->subject;
+                        $name = \ForkBB\cens($arg->subject);
                     } else {
                         $name = 'no name';
                     }
 
                     if ($arg->page > 1) {
-                        $this->titles = $name . ' ' . __('Page', $arg->page);
+                        $this->titles = $name . ' ' . \ForkBB\__('Page', $arg->page);
                     } else {
                         $this->titles = $name;
                     }
@@ -48,7 +48,7 @@ trait CrumbTrait
             $active = null;
         }
         // главная страница
-        $crumbs[] = [$this->c->Router->link('Index'), __('Index'), $active];
+        $crumbs[] = [$this->c->Router->link('Index'), \ForkBB\__('Index'), $active];
 
         return array_reverse($crumbs);
     }

+ 9 - 12
app/Models/Pages/Debug.php

@@ -15,15 +15,12 @@ class Debug extends Page
     {
         if ($this->c->DEBUG > 1) {
             $total = 0;
-            $this->queries = array_map(
-                function($a) use (&$total) {
-                    $total += $a[1];
-                    $a[1] = $this->number($a[1], 3);
-                    return $a;
-                }, 
-                $this->c->DB->getQueries()
-            );
-            $this->total = $this->number($total, 3);
+            $queries = $this->c->DB->getQueries();
+            foreach ($queries as $cur) {
+                $total += $cur[1];
+            }
+            $this->queries = $queries;
+            $this->total   = $total;
         } else {
             $this->queries = null;
         }
@@ -31,9 +28,9 @@ class Debug extends Page
         $this->nameTpl    = 'layouts/debug';
         $this->onlinePos  = null;
         $this->numQueries = $this->c->DB->getCount();
-        $this->memory     = $this->size(memory_get_usage());
-        $this->peak       = $this->size(memory_get_peak_usage());
-        $this->time       = $this->number(microtime(true) - $this->c->START, 3);
+        $this->memory     = memory_get_usage();
+        $this->peak       = memory_get_peak_usage();
+        $this->time       = microtime(true) - $this->c->START;
         
         return $this;
     }

+ 209 - 0
app/Models/Pages/Edit.php

@@ -0,0 +1,209 @@
+<?php
+
+namespace ForkBB\Models\Pages;
+
+use ForkBB\Core\Validator;
+use ForkBB\Models\Post;
+use ForkBB\Models\Page;
+
+class Edit extends Page
+{
+    use CrumbTrait;
+    use PostFormTrait;
+    use PostValidatorTrait;
+
+    /**
+     * Подготовка данных для шаблона редактироания сообщения
+     * 
+     * @param array $args
+     * 
+     * @return Page
+     */
+    public function edit(array $args, Post $post = null)
+    {
+        $post = $post ?: $this->c->ModelPost->load((int) $args['id']);
+
+        if (empty($post) || ! $post->canEdit) {
+            return $this->c->Message->message('Bad request');
+        }
+
+        $topic       = $post->parent;
+        $editSubject = $post->id === $topic->first_post_id;
+
+        if (! isset($args['_vars'])) {
+            $args['_vars'] = [
+                'message'      => $post->message,
+                'subject'      => $topic->subject,
+                'hide_smilies' => $post->hide_smilies,
+                'stick_topic'  => $topic->sticky,
+                'stick_fp'     => $topic->stick_fp,
+                'edit_post'    => $post->edit_post,
+            ];
+        }
+
+        $this->c->Lang->load('post');
+        
+        $this->nameTpl   = 'post';
+        $this->onlinePos = 'topic-' . $topic->id;
+        $this->canonical = $post->linkEdit;
+        $this->robots    = 'noindex';
+        $this->formTitle = $editSubject ? \ForkBB\__('Edit topic') : \ForkBB\__('Edit post');
+        $this->crumbs    = $this->crumbs($this->formTitle, $topic);
+        $this->form      = $this->messageForm($post, 'EditPost', $args, true, $editSubject);
+                
+        return $this;
+    }
+
+    /**
+     * Обработка данных от формы редактирования сообщения
+     * 
+     * @param array $args
+     * 
+     * @return Page
+     */
+    public function editPost(array $args)
+    {
+        $post = $this->c->ModelPost->load((int) $args['id']);
+
+        if (empty($post) || ! $post->canEdit) {
+            return $this->c->Message->message('Bad request');
+        }
+
+        $topic       = $post->parent;
+        $editSubject = $post->id === $topic->first_post_id;
+
+        $this->c->Lang->load('post');
+
+        $v = $this->messageValidator($topic, 'EditPost', $args, true, $editSubject);
+
+        if ($v->validation($_POST) && null === $v->preview) {
+            return $this->endEdit($post, $v);
+        }
+
+        $this->fIswev  = $v->getErrors();
+        $args['_vars'] = $v->getData();
+
+        if (null !== $v->preview && ! $v->getErrors()) {
+            $this->previewHtml = $this->c->Parser->parseMessage(null, (bool) $v->hide_smilies);
+        }
+
+        return $this->edit($args, $post);
+    }
+
+    /**
+     * Сохранение сообщения
+     * 
+     * @param Post $post
+     * @param Validator $v
+     * 
+     * @return Page
+     */
+    protected function endEdit(Post $post, Validator $v)
+    {
+        $now       = time();
+        $user      = $this->c->user;
+        $username  = $user->isGuest ? $v->username : $user->username;
+        $merge     = false;
+        $executive = $user->isAdmin || $user->isModerator($model);
+        
+        // подготовка к объединению/сохранению сообщения
+        if (null === $v->subject) {
+            $createTopic = false;
+            $forum       = $model->parent;
+            $topic       = $model;
+            
+            if (! $user->isGuest && $topic->last_poster === $username) {
+                if ($executive) {
+                    if ($v->merge_post) {
+                        $merge = true;
+                    } 
+                } else {
+                    if ($this->c->config->o_merge_timeout > 0 // ???? стоит завязать на время редактирование сообщений?
+                        && $now - $topic->last_post < $this->c->config->o_merge_timeout
+                    ) {
+                        $merge = true;
+                    }
+                }
+            }
+        // создание темы
+        } else {
+            $createTopic = true;
+            $forum       = $model;
+            $topic       = $this->c->ModelTopic;
+
+            $topic->subject     = $v->subject;
+            $topic->poster      = $username;
+            $topic->last_poster = $username;
+            $topic->posted      = $now;
+            $topic->last_post   = $now;
+            $topic->sticky      = $v->stick_topic ? 1 : 0;
+            $topic->stick_fp    = $v->stick_fp ? 1 : 0;
+#           $topic->poll_type   = ;
+#           $topic->poll_time   = ;
+#           $topic->poll_term   = ;
+#           $topic->poll_kol    = ;
+    
+            $topic->insert();
+        }
+
+        // попытка объеденить новое сообщение с крайним в теме
+        if ($merge) {
+            $lastPost  = $this->c->ModelPost->load($topic->last_post_id);
+            $newLength = mb_strlen($lastPost->message . $v->message, 'UTF-8');
+
+            if ($newLength < $this->c->MAX_POST_SIZE - 100) {
+                $lastPost->message   = $lastPost->message . "\n[after=" . ($now - $topic->last_post) . "]\n" . $v->message; //????
+                $lastPost->edited    = $now;
+                $lastPost->edited_by = $username;
+
+                $lastPost->update();
+            } else {
+                $merge = false;
+            }
+        }
+        
+        // создание нового сообщения
+        if (! $merge) {
+            $post = $this->c->ModelPost;
+        
+            $post->poster       = $username;
+            $post->poster_id    = $this->c->user->id;
+            $post->poster_ip    = $this->c->user->ip;
+            $post->poster_email = $v->email;
+            $post->message      = $v->message; //?????
+            $post->hide_smilies = $v->hide_smilies ? 1 : 0;
+#           $post->edit_post    =
+            $post->posted       = $now;
+#           $post->edited       =
+#           $post->edited_by    =
+            $post->user_agent   = $this->c->user->userAgent;
+            $post->topic_id     = $topic->id;
+        
+            $post->insert();
+        }
+
+        if ($createTopic) {
+            $topic->forum_id      = $forum->id;
+            $topic->first_post_id = $post->id;
+        }
+
+        // обновление данных в теме и разделе
+        $topic->calcStat()->update();
+        $forum->calcStat()->update();
+
+        // обновление данных текущего пользователя
+        if (! $merge && ! $user->isGuest && $forum->no_sum_mess != '1') {
+            $user->num_posts = $user->num_posts + 1;
+
+            if ($user->g_promote_next_group != '0' && $user->num_posts >= $user->g_promote_min_posts) {
+                $user->group_id = $user->g_promote_next_group;
+            }
+        }
+        $user->last_post = $now;
+        $user->update();
+        
+        return $this->c->Redirect
+            ->page('ViewPost', ['id' => $merge ? $lastPost->id : $post->id])
+            ->message(\ForkBB\__('Post redirect'));
+    }
+}

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

@@ -37,7 +37,7 @@ class Forum extends Page
         $user = $this->c->user;
 
         if (empty($topics)) {
-            $this->a['fIswev']['i'][] = __('Empty forum');
+            $this->a['fIswev']['i'][] = \ForkBB\__('Empty forum');
         }
 
         $this->fIndex     = 'index';

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

@@ -30,7 +30,7 @@ class Index extends Page
         $forums = empty($root) ? [] : $root->subforums;
         $ctgs   = [];
         if (empty($forums)) {
-            $this->a['fIswev']['i'][] = __('Empty board');
+            $this->a['fIswev']['i'][] = \ForkBB\__('Empty board');
         } else {
             foreach($forums as $forum) {
                 $ctgs[$forum->cat_id][] = $forum;

+ 38 - 38
app/Models/Pages/Install.php

@@ -90,7 +90,7 @@ class Install extends Page
 
         // версия PHP
         if (version_compare(PHP_VERSION, self::PHP_MIN, '<')) {
-            $this->a['fIswev']['e'][] = __('You are running error', 'PHP', PHP_VERSION, $this->c->FORK_REVISION, self::PHP_MIN);
+            $this->a['fIswev']['e'][] = \ForkBB\__('You are running error', 'PHP', PHP_VERSION, $this->c->FORK_REVISION, self::PHP_MIN);
         }
 
         // доступность папок на запись
@@ -101,14 +101,14 @@ class Install extends Page
         ];
         foreach ($folders as $folder) {
             if (! is_writable($folder)) {
-                $this->a['fIswev']['e'][] = __('Alert folder', $folder);
+                $this->a['fIswev']['e'][] = \ForkBB\__('Alert folder', $folder);
             }
         }
 
         // доступность шаблона конфигурации
         $config = file_get_contents($this->c->DIR_CONFIG . '/main.dist.php');
         if (false === $config) {
-            $this->a['fIswev']['e'][] = __('No access to main.dist.php');
+            $this->a['fIswev']['e'][] = \ForkBB\__('No access to main.dist.php');
         }
         unset($config);
 
@@ -144,7 +144,7 @@ class Install extends Page
         // стиль
         $styles = $this->c->Func->getStyles();
         if (empty($styles)) {
-            $this->a['fIswev']['e'][] = __('No styles');
+            $this->a['fIswev']['e'][] = \ForkBB\__('No styles');
             $defaultStyles = ['ForkBB'];
         } else {
             $defaultStyles = [];
@@ -158,7 +158,7 @@ class Install extends Page
         // типы БД
         $dbTypes = $this->DBTypes(isset($args['dbtype']) ? $args['dbtype'] : null);
         if (empty($dbTypes)) {
-            $this->a['fIswev']['e'][] = __('No DB extensions');
+            $this->a['fIswev']['e'][] = \ForkBB\__('No DB extensions');
         }
         $this->dbTypes = $dbTypes;
         
@@ -172,8 +172,8 @@ class Install extends Page
             $this->dbprefix = '';
             $this->username = '';
             $this->email    = '';
-            $this->title    = __('My ForkBB Forum');
-            $this->descr    = __('Description');
+            $this->title    = \ForkBB\__('My ForkBB Forum');
+            $this->descr    = \ForkBB\__('Description');
             $this->baseurl  = $this->c->BASE_URL;
         } else {
             $this->dbhost   = $args['dbhost'];
@@ -217,23 +217,23 @@ class Install extends Page
             'check_host'   => [$this, 'vCheckHost'],
             'rtrim_url'    => [$this, 'vRtrimURL']
         ])->setRules([
-            'installlang' => 'string:trim',
-            'dbtype' => ['required|string:trim|in:' . implode(',', array_keys($this->DBTypes())), __('Database type')],
-            'dbhost' => ['required|string:trim|check_host', __('Database server hostname')],
-            'dbname' => ['required|string:trim', __('Database name')],
-            'dbuser' => ['string:trim', __('Database username')],
-            'dbpass' => ['string:trim', __('Database password')],
-            'dbprefix' => ['string:trim|max:40|check_prefix', __('Table prefix')],
-            'username' => ['required|string:trim|min:2|max:25', __('Administrator username')],
-            'password' => ['required|string|min:16|password', __('Administrator passphrase')],
-            'email' => 'required|string:trim,lower|email',
-            'title' => ['required|string:trim', __('Board title')],
-            'descr' => ['required|string:trim', __('Board description')],
-            'baseurl' => ['required|string:trim|rtrim_url', __('Base URL')],
-            'defaultlang' => ['required|string:trim|in:' . implode(',', $this->c->Func->getLangs()), __('Default language')],
-            'defaultstyle' => ['required|string:trim|in:' . implode(',', $this->c->Func->getStyles()), __('Default style')],
+            'installlang'  => 'string:trim',
+            'dbtype'       => ['required|string:trim|in:' . implode(',', array_keys($this->DBTypes())), \ForkBB\__('Database type')],
+            'dbhost'       => ['required|string:trim|check_host', \ForkBB\__('Database server hostname')],
+            'dbname'       => ['required|string:trim', \ForkBB\__('Database name')],
+            'dbuser'       => ['string:trim', \ForkBB\__('Database username')],
+            'dbpass'       => ['string:trim', \ForkBB\__('Database password')],
+            'dbprefix'     => ['string:trim|max:40|check_prefix', \ForkBB\__('Table prefix')],
+            'username'     => ['required|string:trim|min:2|max:25', \ForkBB\__('Administrator username')],
+            'password'     => ['required|string|min:16|password', \ForkBB\__('Administrator passphrase')],
+            'email'        => 'required|string:trim,lower|email',
+            'title'        => ['required|string:trim', \ForkBB\__('Board title')],
+            'descr'        => ['required|string:trim', \ForkBB\__('Board description')],
+            'baseurl'      => ['required|string:trim|rtrim_url', \ForkBB\__('Base URL')],
+            'defaultlang'  => ['required|string:trim|in:' . implode(',', $this->c->Func->getLangs()), \ForkBB\__('Default language')],
+            'defaultstyle' => ['required|string:trim|in:' . implode(',', $this->c->Func->getStyles()), \ForkBB\__('Default style')],
         ])->setMessages([
-            'email' => __('Wrong email'),
+            'email' => \ForkBB\__('Wrong email'),
         ]);
 
         if ($v->validation($_POST)) {
@@ -270,9 +270,9 @@ class Install extends Page
         $error = true;
         if (strlen($prefix) == 0) {
         } elseif (! preg_match('%^[a-z][a-z\d_]*$%i', $prefix)) {
-            $error = __('Table prefix error', $prefix);
+            $error = \ForkBB\__('Table prefix error', $prefix);
         } elseif ($v->dbtype == 'sqlite' && strtolower($prefix) == 'sqlite_') {
-            $error = __('Prefix reserved');
+            $error = \ForkBB\__('Prefix reserved');
         }
         return [$prefix, $error];
     }
@@ -327,7 +327,7 @@ class Install extends Page
         try {
             $stmt = $this->c->DB->query('SELECT 1 FROM ::users WHERE id=1 LIMIT 1');
             if (! empty($stmt->fetch())) {
-                return [$dbhost, __('Existing table error', $v->dbprefix, $v->dbname)];
+                return [$dbhost, \ForkBB\__('Existing table error', $v->dbprefix, $v->dbname)];
             }
         } catch (PDOException $e) {
             // все отлично, таблица пользователей не найдена
@@ -860,10 +860,10 @@ class Install extends Page
 
         $groups = [
         //    g_id,                  g_title,              g_user_title,        g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_mod_promote_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood
-            [$this->c->GROUP_ADMIN,  __('Administrators'), __('Administrator'), 0,           0,                0,                  0,                      0,               1,                   1,            1,            1,              1,             1,            1,              1,               1,           1,        1,              1,            0,            0,              0,             0],
-            [$this->c->GROUP_MOD,    __('Moderators'),     __('Moderator'),     1,           1,                1,                  1,                      1,               1,                   1,            1,            1,              1,             1,            1,              1,               1,           1,        1,              1,            0,            0,              0,             0],
-            [$this->c->GROUP_GUEST,  __('Guests'),         NULL,                0,           0,                0,                  0,                      0,               0,                   1,            1,            0,              0,             0,            0,              0,               0,           1,        1,              0,            120,          60,             0,             0],
-            [$this->c->GROUP_MEMBER, __('Members'),        NULL,                0,           0,                0,                  0,                      0,               0,                   1,            1,            1,              1,             1,            1,              1,               0,           1,        1,              1,            30,           30,             60,            60],
+            [$this->c->GROUP_ADMIN,  \ForkBB\__('Administrators'), \ForkBB\__('Administrator'), 0,           0,                0,                  0,                      0,               1,                   1,            1,            1,              1,             1,            1,              1,               1,           1,        1,              1,            0,            0,              0,             0],
+            [$this->c->GROUP_MOD,    \ForkBB\__('Moderators'),     \ForkBB\__('Moderator'),     1,           1,                1,                  1,                      1,               1,                   1,            1,            1,              1,             1,            1,              1,               1,           1,        1,              1,            0,            0,              0,             0],
+            [$this->c->GROUP_GUEST,  \ForkBB\__('Guests'),         NULL,                0,           0,                0,                  0,                      0,               0,                   1,            1,            0,              0,             0,            0,              0,               0,           1,        1,              0,            120,          60,             0,             0],
+            [$this->c->GROUP_MEMBER, \ForkBB\__('Members'),        NULL,                0,           0,                0,                  0,                      0,               0,                   1,            1,            1,              1,             1,            1,              1,               0,           1,        1,              1,            30,           30,             60,            60],
         ];
         foreach ($groups as $group) { //???? $db_type != 'pgsql'
             $this->c->DB->exec('INSERT INTO ::groups (g_id, g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_mod_promote_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood, g_report_flood) VALUES (?i, ?s, ?s, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i, ?i)', $group) ;
@@ -871,7 +871,7 @@ 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, __('Guest'), __('Guest'), __('Guest')]);
+        $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]);
 
         $pun_config = [
@@ -928,11 +928,11 @@ class Install extends Page
             'o_regs_allow' => 1,
             'o_regs_verify' => 1,
             'o_announcement' => 0,
-            'o_announcement_message' => __('Announcement'),
+            'o_announcement_message' => \ForkBB\__('Announcement'),
             'o_rules' => 0,
-            'o_rules_message' => __('Rules'),
+            'o_rules_message' => \ForkBB\__('Rules'),
             'o_maintenance' => 0,
-            'o_maintenance_message' => __('Maintenance message'),
+            'o_maintenance_message' => \ForkBB\__('Maintenance message'),
             'o_default_dst' => 0,
             'o_feed_type' => 2,
             'o_feed_ttl' => 0,
@@ -974,10 +974,10 @@ class Install extends Page
             $this->c->DB->exec('INSERT INTO ::config (conf_name, conf_value) VALUES (?s, ?s)', [$conf_name, $conf_value]);
         }
 
-        $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_topic, disp_position, cat_id) VALUES (?s, ?s, ?i, ?i, ?i, ?i, ?s, ?s, ?i, ?i)', [__('Test forum'), __('This is just a test forum'), 1, 1, $now, 1, $v->username, __('Test post'), 1, 1]);
-        $this->c->DB->exec('INSERT INTO ::topics (poster, subject, posted, first_post_id, last_post, last_post_id, last_poster, forum_id) VALUES(?s, ?s, ?i, ?i, ?i, ?i, ?s, ?i)', [$v->username, __('Test post'), $now, 1, $now, 1, $v->username, 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 ::categories (cat_name, disp_position) VALUES (?s, ?i)', [\ForkBB\__('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_topic, disp_position, cat_id) VALUES (?s, ?s, ?i, ?i, ?i, ?i, ?s, ?s, ?i, ?i)', [\ForkBB\__('Test forum'), \ForkBB\__('This is just a test forum'), 1, 1, $now, 1, $v->username, \ForkBB\__('Test post'), 1, 1]);
+        $this->c->DB->exec('INSERT INTO ::topics (poster, subject, posted, first_post_id, last_post, last_post_id, last_poster, forum_id) VALUES(?s, ?s, ?i, ?i, ?i, ?i, ?s, ?i)', [$v->username, \ForkBB\__('Test post'), $now, 1, $now, 1, $v->username, 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, \ForkBB\__('Test message'), $now, 1]);
 
         $smilies = [
             ':)'         => 'smile.png',

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

@@ -22,7 +22,7 @@ class Maintenance extends Page
         $this->nameTpl            = 'maintenance';
 #       $this->onlinePos          = null; //????
 #       $this->robots             = 'noindex';
-        $this->titles             = __('Maintenance');
+        $this->titles             = \ForkBB\__('Maintenance');
 #       $this->fNavigation        = null; //????
         $this->maintenanceMessage = $this->c->config->o_maintenance_message;
     }

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

@@ -20,8 +20,8 @@ class Message extends Page
         $this->nameTpl     = 'message';
         $this->httpStatus  = $status;
         $this->httpHeaders = $headers;
-        $this->titles      = __('Info');
-        $this->message     = __($message);
+        $this->titles      = \ForkBB\__('Info');
+        $this->message     = \ForkBB\__($message);
         $this->back        = $back;
 
         return $this;

+ 7 - 7
app/Models/Pages/Post.php

@@ -35,9 +35,9 @@ class Post extends Page
         $this->onlinePos = 'forum-' . $forum->id;
         $this->canonical = $this->c->Router->link('NewTopic', ['id' => $forum->id]);
         $this->robots    = 'noindex';
-        $this->crumbs    = $this->crumbs(__('Post new topic'), $forum);
-        $this->form      = $this->messageForm($forum, 'NewTopic', $args, true);
-        $this->formTitle = __('Post new topic');
+        $this->crumbs    = $this->crumbs(\ForkBB\__('Post new topic'), $forum);
+        $this->formTitle = \ForkBB\__('Post new topic');
+        $this->form      = $this->messageForm($forum, 'NewTopic', $args, false, true);
         
         return $this;
     }
@@ -59,7 +59,7 @@ class Post extends Page
 
         $this->c->Lang->load('post');
 
-        $v = $this->messageValidator($forum, 'NewTopic', $args, true);
+        $v = $this->messageValidator($forum, 'NewTopic', $args, false, true);
 
         if ($v->validation($_POST) && null === $v->preview) {
             return $this->endPost($forum, $v);
@@ -109,9 +109,9 @@ class Post extends Page
         $this->onlinePos = 'topic-' . $topic->id;
         $this->canonical = $this->c->Router->link('NewReply', ['id' => $topic->id]);
         $this->robots    = 'noindex';
-        $this->crumbs    = $this->crumbs(__('Post a reply'), $topic);
+        $this->crumbs    = $this->crumbs(\ForkBB\__('Post a reply'), $topic);
+        $this->formTitle = \ForkBB\__('Post a reply');
         $this->form      = $this->messageForm($topic, 'NewReply', $args);
-        $this->formTitle = __('Post a reply');
                 
         return $this;
     }
@@ -263,6 +263,6 @@ class Post extends Page
         
         return $this->c->Redirect
             ->page('ViewPost', ['id' => $merge ? $lastPost->id : $post->id])
-            ->message(__('Post redirect'));
+            ->message(\ForkBB\__('Post redirect'));
     }
 }

+ 26 - 17
app/Models/Pages/PostFormTrait.php

@@ -12,12 +12,13 @@ trait PostFormTrait
      * @param Model $model
      * @param string $marker
      * @param array $args
+     * @param bool $editPost
      * @param bool $editSubject
      * @param bool $quickReply
      * 
      * @return array
      */
-    protected function messageForm(Model $model, $marker, array $args, $editSubject = false, $quickReply = false)
+    protected function messageForm(Model $model, $marker, array $args, $editPost = false, $editSubject = false, $quickReply = false)
     {
         $vars = isset($args['_vars']) ? $args['_vars'] : null;
         unset($args['_vars']);
@@ -32,12 +33,12 @@ trait PostFormTrait
             'btns'   => [
                 'submit'  => [
                     'type'      => 'submit', 
-                    'value'     => __('Submit'), 
+                    'value'     => \ForkBB\__('Submit'), 
                     'accesskey' => 's',
                 ],
                 'preview' => [
                     'type'      => 'submit', 
-                    'value'     => __('Preview'), 
+                    'value'     => \ForkBB\__('Preview'), 
                     'accesskey' => 'p',
                     'class'     => 'f-minor',
                 ],
@@ -50,7 +51,7 @@ trait PostFormTrait
                 'dl'        => 't1',
                 'type'      => 'text',
                 'maxlength' => 25,
-                'title'     => __('Username'),
+                'title'     => \ForkBB\__('Username'),
                 'required'  => true,
                 'pattern'   => '^.{2,25}$',
                 'value'     => isset($vars['username']) ? $vars['username'] : null,
@@ -60,7 +61,7 @@ trait PostFormTrait
                 'dl'        => 't2',
                 'type'      => 'text',
                 'maxlength' => 80,
-                'title'     => __('Email'),
+                'title'     => \ForkBB\__('Email'),
                 'required'  => $this->c->config->p_force_guest_email == '1',
                 'pattern'   => '.+@.+',
                 'value'     => isset($vars['email']) ? $vars['email'] : null,
@@ -72,7 +73,7 @@ trait PostFormTrait
             $fieldset['subject'] = [
                 'type'      => 'text',
                 'maxlength' => 70,
-                'title'     => __('Subject'),
+                'title'     => \ForkBB\__('Subject'),
                 'required'  => true,
                 'value'     => isset($vars['subject']) ? $vars['subject'] : null,
                 'autofocus' => $autofocus,
@@ -82,14 +83,14 @@ trait PostFormTrait
 
         $fieldset['message'] = [
             'type'     => 'textarea',
-            'title'    => __('Message'),
+            'title'    => \ForkBB\__('Message'),
             'required' => true,
             'value'    => isset($vars['message']) ? $vars['message'] : null,
             'bb'       => [
-                ['link', __('BBCode'), __($this->c->config->p_message_bbcode == '1' ? 'on' : 'off')],
-                ['link', __('url tag'), __($this->c->config->p_message_bbcode == '1' && $this->c->user->g_post_links == '1' ? 'on' : 'off')],
-                ['link', __('img tag'), __($this->c->config->p_message_bbcode == '1' && $this->c->config->p_message_img_tag == '1' ? 'on' : 'off')],
-                ['link', __('Smilies'), __($this->c->config->o_smilies == '1' ? 'on' : 'off')],
+                ['link', \ForkBB\__('BBCode'), \ForkBB\__($this->c->config->p_message_bbcode == '1' ? 'on' : 'off')],
+                ['link', \ForkBB\__('url tag'), \ForkBB\__($this->c->config->p_message_bbcode == '1' && $this->c->user->g_post_links == '1' ? 'on' : 'off')],
+                ['link', \ForkBB\__('img tag'), \ForkBB\__($this->c->config->p_message_bbcode == '1' && $this->c->config->p_message_img_tag == '1' ? 'on' : 'off')],
+                ['link', \ForkBB\__('Smilies'), \ForkBB\__($this->c->config->o_smilies == '1' ? 'on' : 'off')],
             ],
             'autofocus' => $autofocus,
         ];
@@ -103,36 +104,44 @@ trait PostFormTrait
             if ($editSubject) {
                 $fieldset['stick_topic'] = [
                     'type'    => 'checkbox',
-                    'label'   => __('Stick topic'),
+                    'label'   => \ForkBB\__('Stick topic'),
                     'value'   => '1',
                     'checked' => isset($vars['stick_topic']) ? (bool) $vars['stick_topic'] : false,
                 ];
                 $fieldset['stick_fp'] = [
                     'type'    => 'checkbox',
-                    'label'   => __('Stick first post'),
+                    'label'   => \ForkBB\__('Stick first post'),
                     'value'   => '1',
                     'checked' => isset($vars['stick_fp']) ? (bool) $vars['stick_fp'] : false,
                 ];
-            } else {
+            } elseif (! $editPost) {
                 $fieldset['merge_post'] = [
                     'type'    => 'checkbox',
-                    'label'   => __('Merge posts'),
+                    'label'   => \ForkBB\__('Merge posts'),
                     'value'   => '1',
                     'checked' => isset($vars['merge_post']) ? (bool) $vars['merge_post'] : true,
                 ];
             }
+            if ($editPost) {
+                $fieldset['edit_post'] = [
+                    'type'    => 'checkbox',
+                    'label'   => \ForkBB\__('EditPost edit'),
+                    'value'   => '1',
+                    'checked' => isset($vars['edit_post']) ? (bool) $vars['edit_post'] : false,
+                ];
+            }
         }
         if (! $quickReply && $this->c->config->o_smilies == '1') {
             $fieldset['hide_smilies'] = [
                 'type'    => 'checkbox',
-                'label'   => __('Hide smilies'),
+                'label'   => \ForkBB\__('Hide smilies'),
                 'value'   => '1',
                 'checked' => isset($vars['hide_smilies']) ? (bool) $vars['hide_smilies'] : false,
             ];
         }
         if ($fieldset) {
             $form['sets'][] = [
-                'legend' => __('Options'),
+                'legend' => \ForkBB\__('Options'),
                 'fields' => $fieldset,
             ];
         }

+ 22 - 11
app/Models/Pages/PostValidatorTrait.php

@@ -41,10 +41,10 @@ trait PostValidatorTrait
         $user->username = $username;
 
         // username = Гость
-        if (preg_match('%^(guest|' . preg_quote(__('Guest'), '%') . ')$%iu', $username)) {
+        if (preg_match('%^(guest|' . preg_quote(\ForkBB\__('Guest'), '%') . ')$%iu', $username)) {
             $v->addError('Username guest');
         // цензура
-        } elseif ($user->cens()->username !== $username) {
+        } elseif (\ForkBB\cens($user->username) !== $username) {
             $v->addError('Username censor');
         // username забанен
         } elseif ($this->c->bans->isBanned($user) > 0) {
@@ -64,7 +64,7 @@ trait PostValidatorTrait
     public function vCheckSubject(Validator $v, $subject, $attr, $executive)
     {
         // после цензуры заголовок темы путой
-        if ($this->c->censorship->censor($subject) == '') {
+        if (\ForkBB\cens($subject) == '') {
             $v->addError('No subject after censoring');
         // заголовок темы только заглавными буквами
         } elseif (! $executive
@@ -93,7 +93,7 @@ trait PostValidatorTrait
     public function vCheckMessage(Validator $v, $message, $attr, $executive)
     {
         // после цензуры текст сообщения пустой
-        if ($this->c->censorship->censor($message) == '') {
+        if (\ForkBB\cens($message) == '') {
             $v->addError('No message after censoring');
         // текст сообщения только заглавными буквами
         } elseif (! $executive
@@ -132,7 +132,7 @@ trait PostValidatorTrait
         $time = time() - (int) $user->last_post;
 
         if ($time < $user->g_post_flood) {
-            $v->addError(__('Flood start', $user->g_post_flood, $user->g_post_flood - $time), 'e');
+            $v->addError(\ForkBB\__('Flood start', $user->g_post_flood, $user->g_post_flood - $time), 'e');
         }
 
         return $submit;
@@ -144,11 +144,12 @@ trait PostValidatorTrait
      * @param Model $model
      * @param string $marker
      * @param array $args
+     * @param bool $editPost
      * @param bool $editSubject
      * 
      * @return Validator
      */
-    protected function messageValidator(Model $model, $marker, array $args, $editSubject = false)
+    protected function messageValidator(Model $model, $marker, array $args, $editPost = false, $editSubject = false)
     {
         if ($this->c->user->isGuest) {
             $ruleEmail    = ($this->c->config->p_force_guest_email == '1' ? 'required|' : '') . 'string:trim,lower|email|check_email';
@@ -168,17 +169,26 @@ trait PostValidatorTrait
             if ($editSubject) {
                 $ruleStickTopic = 'checkbox';
                 $ruleStickFP    = 'checkbox';
-                $ruleMergePost  = 'absent';
             } else {
                 $ruleStickTopic = 'absent';
                 $ruleStickFP    = 'absent';
+            }
+            if (! $editSubject && ! $editPost) {
                 $ruleMergePost  = 'checkbox';
+            } else {
+                $ruleMergePost  = 'absent';
+            }
+            if ($editPost) {
+                $ruleEditPost   = 'checkbox';
+            } else {
+                $ruleEditPost   = 'absent';
             }
             $executive          = true;
         } else {
             $ruleStickTopic     = 'absent';
             $ruleStickFP        = 'absent';
             $ruleMergePost      = 'absent:1';
+            $ruleEditPost       = 'absent';
             $executive          = false;
         }
 
@@ -196,13 +206,14 @@ trait PostValidatorTrait
             'check_timeout'  => [$this, 'vCheckTimeout'],
         ])->setRules([
             'token'        => 'token:' . $marker,
-            'email'        => [$ruleEmail, __('Email')],
-            'username'     => [$ruleUsername, __('Username')],
-            'subject'      => [$ruleSubject, __('Subject')],
+            'email'        => [$ruleEmail, \ForkBB\__('Email')],
+            'username'     => [$ruleUsername, \ForkBB\__('Username')],
+            'subject'      => [$ruleSubject, \ForkBB\__('Subject')],
             'stick_topic'  => $ruleStickTopic,
             'stick_fp'     => $ruleStickFP,
             'merge_post'   => $ruleMergePost,
             'hide_smilies' => $ruleHideSmilies,
+            'edit_post'    => $ruleEditPost,
             'preview'      => 'string',
             'submit'       => 'string|check_timeout',
             'message'      => 'required|string:trim|max:' . $this->c->MAX_POST_SIZE . '|check_message',
@@ -211,7 +222,7 @@ trait PostValidatorTrait
             'subject.check_subject' => $executive,
             'message.check_message' => $executive,
         ])->setMessages([
-            'username.login' => __('Login format'),
+            'username.login' => \ForkBB\__('Login format'),
         ]);
 
         return $v;

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

@@ -13,7 +13,7 @@ class Redirect extends Page
      */
     public function toIndex()
     {
-        return $this->page('Index')->message(__('Redirecting to index'));
+        return $this->page('Index')->message(\ForkBB\__('Redirecting to index'));
     }
 
     /**
@@ -58,7 +58,7 @@ class Redirect extends Page
         }
 
         $this->nameTpl = 'layouts/redirect';
-        $this->titles  = __('Redirecting');
+        $this->titles  = \ForkBB\__('Redirecting');
         $this->robots  = 'noindex';
         $this->message = $message;
         $this->timeout = (int) $this->c->config->o_redirect_delay;  //???? перенести в заголовки?

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

@@ -25,14 +25,14 @@ class Register extends Page
             'token'    => 'token:RegisterForm',
             'agree'    => 'required|token:Register',
             'on'       => 'integer',
-            'email'    => ['required_with:on|string:trim,lower|email|check_email', __('Email')],
-            'username' => ['required_with:on|string:trim,spaces|min:2|max:25|login|check_username', __('Username')],
-            'password' => ['required_with:on|string|min:16|password', __('Passphrase')],
+            'email'    => ['required_with:on|string:trim,lower|email|check_email', \ForkBB\__('Email')],
+            'username' => ['required_with:on|string:trim,spaces|min:2|max:25|login|check_username', \ForkBB\__('Username')],
+            'password' => ['required_with:on|string|min:16|password', \ForkBB\__('Passphrase')],
         ])->setMessages([
             'agree.required'    => ['cancel', 'cancel'],
-            'agree.token'       => [__('Bad agree', $this->c->Router->link('Register')), 'w'],
-            'password.password' => __('Pass format'),
-            'username.login'    => __('Login format'),
+            'agree.token'       => [\ForkBB\__('Bad agree', $this->c->Router->link('Register')), 'w'],
+            'password.password' => \ForkBB\__('Pass format'),
+            'username.login'    => \ForkBB\__('Login format'),
         ]);
 
         // завершение регистрации
@@ -44,13 +44,13 @@ class Register extends Page
 
         // нет согласия с правилами
         if (isset($this->fIswev['cancel'])) {
-            return $this->c->Redirect->page('Index')->message(__('Reg cancel redirect'));
+            return $this->c->Redirect->page('Index')->message(\ForkBB\__('Reg cancel redirect'));
         }
 
         $this->fIndex     = 'register';
         $this->nameTpl    = 'register';
         $this->onlinePos  = 'register';
-        $this->titles     = __('Register');
+        $this->titles     = \ForkBB\__('Register');
         $this->robots     = 'noindex';
         $this->formAction = $this->c->Router->link('RegisterForm');
         $this->formToken  = $this->c->Csrf->create('RegisterForm');
@@ -99,7 +99,7 @@ class Register extends Page
         $user->__username = $username;
 
         // username = Гость
-        if (preg_match('%^(guest|' . preg_quote(__('Guest'), '%') . ')$%iu', $username)) {
+        if (preg_match('%^(guest|' . preg_quote(\ForkBB\__('Guest'), '%') . ')$%iu', $username)) {
             $v->addError('Username guest');
         // цензура
         } elseif ($this->c->censorship->censor($username) !== $username) {
@@ -160,7 +160,7 @@ class Register extends Page
             $tplData = [
                 'fTitle' => $this->c->config->o_board_title,
                 'fRootLink' => $this->c->Router->link('Index'),
-                'fMailer' => __('Mailer', $this->c->config->o_board_title),
+                'fMailer' => \ForkBB\__('Mailer', $this->c->config->o_board_title),
                 'username' => $v->username,
                 'userLink' => $this->c->Router->link('User', ['id' => $newUserId, 'name' => $v->username]),
             ];
@@ -171,7 +171,7 @@ class Register extends Page
                     ->setFolder($this->c->DIR_LANG)
                     ->setLanguage($this->c->config->o_default_lang)
                     ->setTo($this->c->config->o_mailing_list)
-                    ->setFrom($this->c->config->o_webmaster_email, __('Mailer', $this->c->config->o_board_title))
+                    ->setFrom($this->c->config->o_webmaster_email, \ForkBB\__('Mailer', $this->c->config->o_board_title))
                     ->setTpl('new_user.tpl', $tplData)
                     ->send();
             } catch (MailException $e) {
@@ -188,7 +188,7 @@ class Register extends Page
             $tplData = [
                 'fTitle' => $this->c->config->o_board_title,
                 'fRootLink' => $this->c->Router->link('Index'),
-                'fMailer' => __('Mailer', $this->c->config->o_board_title),
+                'fMailer' => \ForkBB\__('Mailer', $this->c->config->o_board_title),
                 'username' => $v->username,
                 'link' => $link,
             ];
@@ -199,7 +199,7 @@ class Register extends Page
                     ->setFolder($this->c->DIR_LANG)
                     ->setLanguage($this->c->user->language)
                     ->setTo($v->email)
-                    ->setFrom($this->c->config->o_webmaster_email, __('Mailer', $this->c->config->o_board_title))
+                    ->setFrom($this->c->config->o_webmaster_email, \ForkBB\__('Mailer', $this->c->config->o_board_title))
                     ->setTpl('welcome.tpl', $tplData)
                     ->send();
             } catch (MailException $e) {
@@ -208,17 +208,17 @@ class Register extends Page
 
             // письмо активации аккаунта отправлено
             if ($isSent) {
-                return $this->c->Message->message(__('Reg email', $this->c->config->o_admin_email), false, 200);
+                return $this->c->Message->message(\ForkBB\__('Reg email', $this->c->config->o_admin_email), false, 200);
             // форма сброса пароля
             } else {
                 $auth = $this->c->Auth;
-                $auth->fIswev = ['w' => [__('Error welcom mail', $this->c->config->o_admin_email)]];
+                $auth->fIswev = ['w' => [\ForkBB\__('Error welcom mail', $this->c->config->o_admin_email)]];
                 return $auth->forget(['_email' => $v->email]);
             }
         // форма логина
         } else {
             $auth = $this->c->Auth;
-            $auth->fIswev = ['s' => [__('Reg complete')]];
+            $auth->fIswev = ['s' => [\ForkBB\__('Reg complete')]];
             return $auth->login(['_username' => $v->username]);
         }
     }
@@ -238,7 +238,7 @@ class Register extends Page
             || $user->activate_string{0} !== 'w'
             || ! hash_equals($user->activate_string, $args['key'])
         ) {
-            return $this->c->Message->message(__('Bad request'), false);
+            return $this->c->Message->message('Bad request', false);
         }
 
         $user->group_id = $this->c->config->o_default_user_group;
@@ -250,7 +250,7 @@ class Register extends Page
         $this->c->Lang->load('register');
 
         $auth = $this->c->Auth;
-        $auth->fIswev = ['s' => [__('Reg complete')]];
+        $auth->fIswev = ['s' => [\ForkBB\__('Reg complete')]];
         return $auth->login(['_username' => $v->username]);
     }
 }

+ 5 - 5
app/Models/Pages/Rules.php

@@ -17,8 +17,8 @@ class Rules extends Page
         $this->nameTpl    = 'rules';
         $this->onlinePos  = 'rules';
         $this->canonical  = $this->c->Router->link('Rules');
-        $this->titles     = __('Forum rules');
-        $this->title      = __('Forum rules');
+        $this->titles     = \ForkBB\__('Forum rules');
+        $this->title      = \ForkBB\__('Forum rules');
         $this->rules      = $this->c->config->o_rules_message;
         $this->formAction = null;
 
@@ -38,9 +38,9 @@ class Rules extends Page
         $this->nameTpl    = 'rules';
         $this->onlinePos  = 'rules';
         $this->robots     = 'noindex';
-        $this->titles     = __('Forum rules');
-        $this->title      = __('Forum rules');
-        $this->rules      = $this->c->config->o_rules == '1' ? $this->c->config->o_rules_message : __('If no rules');
+        $this->titles     = \ForkBB\__('Forum rules');
+        $this->title      = \ForkBB\__('Forum rules');
+        $this->rules      = $this->c->config->o_rules == '1' ? $this->c->config->o_rules_message : \ForkBB\__('If no rules');
         $this->formAction = $this->c->Router->link('RegisterForm');
         $this->formToken  = $this->c->Csrf->create('RegisterForm');
         $this->formHash   = $this->c->Csrf->create('Register');

+ 5 - 4
app/Models/Pages/Topic.php

@@ -139,15 +139,16 @@ class Topic extends Page
         $this->nameTpl      = 'topic';
         $this->onlinePos    = 'topic-' . $topic->id;
         $this->onlineDetail = true;
-        $this->canonical    = $this->c->Router->link('Topic', ['id' => $topic->id, 'name' => $topic->cens()->subject, 'page' => $topic->page]);
+        $this->canonical    = $this->c->Router->link('Topic', ['id' => $topic->id, 'name' => \ForkBB\cens($topic->subject), 'page' => $topic->page]);
         $this->topic        = $topic;
         $this->posts        = $posts;
         $this->crumbs       = $this->crumbs($topic);
         $this->online       = $this->c->Online->calc($this)->info();
         $this->stats        = null;
-        $this->form         = $topic->canReply && $this->c->config->o_quickpost == '1' 
-                                ? $this->messageForm($topic, 'NewReply', ['id' => $topic->id], false, true) 
-                                : null;
+
+        if ($topic->canReply && $this->c->config->o_quickpost == '1') {
+            $this->form     = $this->messageForm($topic, 'NewReply', ['id' => $topic->id], false, false, true);
+        }
 
         if ($topic->showViews) {
             $topic->incViews();

+ 65 - 34
app/Models/Post.php

@@ -23,7 +23,7 @@ class Post extends DataModel
      *
      * @return User
      */
-    protected function getuser()
+    protected function getuser() //????
     {
         $attrs = $this->a; //????
         $attrs['id'] = $attrs['poster_id'];
@@ -70,42 +70,73 @@ class Post extends DataModel
         return $this->c->config->o_signatures == '1' && $this->c->user->show_sig == '1';
     }
 
-    /**
-     * Массив элементов управления
-     *
-     * @return array
-     */
-    protected function getcontrols()
+
+    protected function getcanReport()
+    {
+        return ! $this->c->user->isAdmin && ! $this->c->user->isGuest;
+    }
+
+    protected function getlinkReport()
     {
-        $user = $this->c->user;
-        $controls = [];
-        $vars = ['id' => $this->id];
-        if (! $user->isAdmin && ! $user->isGuest) {
-            $controls['report'] = [$this->c->Router->link('ReportPost', $vars), 'Report'];
+        return $this->c->Router->link('ReportPost', ['id' => $this->id]);
+    }
+
+    protected function getcanDelete()
+    {
+        if ($this->c->user->isGuest) {
+            return false;
+        } elseif ($this->c->user->isAdmin || ($this->c->user->isModerator($this) && ! $this->user->isAdmin)) {
+            return true;
+        } elseif ($this->parent->closed == '1') {
+            return false;
         }
-        if ($user->isAdmin || ($user->isModerator($this) && ! $this->user->isAdmin)) {
-            $controls['delete'] = [$this->c->Router->link('DeletePost', $vars), 'Delete'];
-            $controls['edit'] = [$this->c->Router->link('EditPost', $vars), 'Edit'];
-        } elseif ($this->parent->closed != '1'
-            && $this->user->id == $user->id
-            && ($user->g_deledit_interval == '0'
-                || $this->edit_post == '1'
-                || time() - $this->posted < $user->g_deledit_interval
+
+        return $this->user->id === $this->c->user->id
+            && (($this->id == $this->parent->first_post_id && $this->c->user->g_delete_topics == '1') 
+                || ($this->id != $this->parent->first_post_id && $this->c->user->g_delete_posts == '1')
             )
-        ) {
-            if (($this->id == $this->parent->first_post_id && $user->g_delete_topics == '1')
-                || ($this->id != $this->parent->first_post_id && $user->g_delete_posts == '1')
-            ) {
-                $controls['delete'] = [$this->c->Router->link('DeletePost', $vars), 'Delete'];
-            }
-            if ($user->g_edit_posts == '1') {
-                $controls['edit'] = [$this->c->Router->link('EditPost', $vars), 'Edit'];
-            }
-        }
-        if ($this->parent->canReply) {
-            $controls['quote'] = [$this->c->Router->link('NewReply', ['id' => $this->parent->id, 'quote' => $this->id]), 'Quote'];
+            && ($this->c->user->g_deledit_interval == '0' 
+                || $this->edit_post == '1' 
+                || time() - $this->posted < $this->c->user->g_deledit_interval
+            );
+    }
+
+    protected function getlinkDelete()
+    {
+        return $this->c->Router->link('DeletePost', ['id' => $this->id]);
+    }
+
+    protected function getcanEdit()
+    {
+        if ($this->c->user->isGuest) {
+            return false;
+        } elseif ($this->c->user->isAdmin || ($this->c->user->isModerator($this) && ! $this->user->isAdmin)) {
+            return true;
+        } elseif ($this->parent->closed == '1') {
+            return false;
         }
-        return $controls;
+
+        return $this->user->id === $this->c->user->id
+            && $this->c->user->g_edit_posts == '1'
+            && ($this->c->user->g_deledit_interval == '0' 
+                || $this->edit_post == '1' 
+                || time() - $this->posted < $this->c->user->g_deledit_interval
+            );
+    }
+
+    protected function getlinkEdit()
+    {
+        return $this->c->Router->link('EditPost', ['id' => $this->id]);
+    }
+
+    protected function getcanQuote()
+    {
+        return $this->parent->canReply;
+    }
+
+    protected function getlinkQuote()
+    {
+        return $this->c->Router->link('NewReply', ['id' => $this->parent->id, 'quote' => $this->id]);
     }
 
     /**
@@ -115,6 +146,6 @@ class Post extends DataModel
      */
     public function html()
     {
-        return $this->c->Parser->parseMessage($this->cens()->message, (bool) $this->hide_smilies); //????
+        return $this->c->censorship->censor($this->c->Parser->parseMessage($this->message, (bool) $this->hide_smilies));
     }
 }

+ 8 - 7
app/Models/Post/Load.php

@@ -43,7 +43,7 @@ class Load extends MethodModel
         return implode(', ', $result);
     }
 
-    protected function setData(array $data, array $args)
+    protected function setData(array $args, array $data)
     {
         foreach ($args as $alias => $model) {
             $attrs = [];
@@ -68,7 +68,7 @@ class Load extends MethodModel
         // пост + топик
         if (null === $topic) {
 
-            $fileds = $this->queryFields([
+            $fields = $this->queryFields([
                 'p' => array_map(function($val) {return true;}, $this->c->dbMap->posts), // все поля в true
                 't' => array_map(function($val) {return true;}, $this->c->dbMap->topics), // все поля в true
             ]);
@@ -78,7 +78,7 @@ class Load extends MethodModel
                 ':fields' => $fields,
             ];
 
-            $sql = 'SELECT ?p:fileds
+            $sql = 'SELECT ?p:fields
                     FROM ::posts AS p 
                     INNER JOIN ::topics AS t ON t.id=p.topic_id
                     WHERE p.id=?i:pid';
@@ -103,14 +103,15 @@ class Load extends MethodModel
 
         if (null === $topic) {
             $topic = $this->c->ModelTopic;
-            $this->setData($data, [
-                'p' => $this->model,
-                't' => $topic,
-            ]);
+            $this->setData(['p' => $this->model, 't' => $topic], $data);
         } else {
             $this->model->setAttrs($data);
         }
         $this->model->__parent = $topic;
+
+        if ($topic->moved_to || ! $topic->parent) { //????
+            return null;
+        }
         
         return $this->model;
     }

+ 2 - 2
app/Models/Topic.php

@@ -52,7 +52,7 @@ class Topic extends DataModel
      */
     protected function getlink()
     {
-        return $this->c->Router->link('Topic', ['id' => $this->moved_to ?: $this->id, 'name' => $this->cens()->subject]);
+        return $this->c->Router->link('Topic', ['id' => $this->moved_to ?: $this->id, 'name' => \ForkBB\cens($this->subject)]);
     }
 
     /**
@@ -211,7 +211,7 @@ class Topic extends DataModel
             // 1 страницу в списке тем раздела не отображаем
             return [];
         } else { //????
-            return $this->c->Func->paginate($this->numPages, $page, 'Topic', ['id' => $this->id, 'name' => $this->cens()->subject]);
+            return $this->c->Func->paginate($this->numPages, $page, 'Topic', ['id' => $this->id, 'name' => \ForkBB\cens($this->subject)]);
         }
     }
 

+ 6 - 6
app/Models/User.php

@@ -178,15 +178,15 @@ class User extends DataModel
     public function title()
     {
         if (isset($this->c->bans->userList[mb_strtolower($this->username)])) { //????
-            return __('Banned');
+            return \ForkBB\__('Banned');
         } elseif ($this->title != '') {
-            return $this->cens()->title;
+            return \ForkBB\cens($this->title);
         } elseif ($this->g_user_title != '') {
-            return $this->cens()->g_user_title;
+            return \ForkBB\cens($this->g_user_title);
         } elseif ($this->isGuest) {
-            return __('Guest');
+            return \ForkBB\__('Guest');
         } else {
-            return __('Member');
+            return \ForkBB\__('Member');
         }
     }
 
@@ -207,6 +207,6 @@ class User extends DataModel
      */
     protected function gethtmlSign()
     {
-        return $this->c->Parser->parseSignature($this->cens()->signature); //????
+        return $this->c->censorship->censor($this->c->Parser->parseSignature($this->signature));
     }
 }

+ 1 - 0
app/bootstrap.php

@@ -30,6 +30,7 @@ if (file_exists(__DIR__ . '/config/main.php')) {
 }
 
 require __DIR__ . '/functions.php';
+\ForkBB\_init($c);
 
 // https or http?
 if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {

+ 8 - 8
app/config/defaultBBCode.php

@@ -228,19 +228,19 @@ return [
          $hours = ($attrs['Def'] / 3600) % 24;
          $days = (int) ($attrs['Def'] / 86400);
          if ($days > 0) {
-             $arr[] = $days . __('After time d');
+             $arr[] = $days . \ForkBB\__('After time d');
          }
          if ($hours > 0) {
-             $arr[] = $hours . __('After time H');
+             $arr[] = $hours . \ForkBB\__('After time H');
          }
          if ($min > 0) {
-             $arr[] = (($min < 10) ? '0' . $min : $min) . __('After time i');
+             $arr[] = (($min < 10) ? '0' . $min : $min) . \ForkBB\__('After time i');
          }
          if ($sec > 0) {
-             $arr[] = (($sec < 10) ? '0' . $sec : $sec) . __('After time s');
+             $arr[] = (($sec < 10) ? '0' . $sec : $sec) . \ForkBB\__('After time s');
          }
 
-         $attr = __('After time') . ' ' . implode(' ', $arr);
+         $attr = \ForkBB\__('After time') . ' ' . implode(' ', $arr);
 
          return '</p><p class="f-bb-after">' . $attr . ':</p><p>';
      },
@@ -253,7 +253,7 @@ return [
          'no attr' => true,
      ],
      'handler' => function($body, $attrs) {
-         $header = isset($attrs['Def']) ? '<div class="f-bb-q-header">' . $attrs['Def'] .  ' ' . __('wrote') . '</div>' : '';
+         $header = isset($attrs['Def']) ? '<div class="f-bb-q-header">' . $attrs['Def'] .  ' ' . \ForkBB\__('wrote') . '</div>' : '';
          return "</p><blockquote class=\"f-bb-quote\">{$header}<div class=\"f-bb-q-body\"><p>{$body}</p></div></blockquote><p>";
      },
     ],
@@ -268,7 +268,7 @@ return [
          if (isset($attrs['Def'])) {
              $st = '</p><div class="quotebox" style="padding: 0px;"><div onclick="var e,d,c=this.parentNode,a=c.getElementsByTagName(\'div\')[1],b=this.getElementsByTagName(\'span\')[0];if(a.style.display!=\'\'){while(c.parentNode&&(!d||!e||d==e)){e=d;d=(window.getComputedStyle?getComputedStyle(c, null):c.currentStyle)[\'backgroundColor\'];if(d==\'transparent\'||d==\'rgba(0, 0, 0, 0)\')d=e;c=c.parentNode;}a.style.display=\'\';a.style.backgroundColor=d;b.innerHTML=\'&#9650;\';}else{a.style.display=\'none\';b.innerHTML=\'&#9660;\';}" style="font-weight: bold; cursor: pointer; font-size: 0.9em;"><span style="padding: 0 5px;">&#9660;</span>' . $attrs['Def'] . '</div><div style="padding: 6px; margin: 0; display: none;"><p>';
          } else {
-             $st = '</p><div class="quotebox" style="padding: 0px;"><div onclick="var e,d,c=this.parentNode,a=c.getElementsByTagName(\'div\')[1],b=this.getElementsByTagName(\'span\')[0];if(a.style.display!=\'\'){while(c.parentNode&&(!d||!e||d==e)){e=d;d=(window.getComputedStyle?getComputedStyle(c, null):c.currentStyle)[\'backgroundColor\'];if(d==\'transparent\'||d==\'rgba(0, 0, 0, 0)\')d=e;c=c.parentNode;}a.style.display=\'\';a.style.backgroundColor=d;b.innerHTML=\'&#9650;\';}else{a.style.display=\'none\';b.innerHTML=\'&#9660;\';}" style="font-weight: bold; cursor: pointer; font-size: 0.9em;"><span style="padding: 0 5px;">&#9660;</span>' . __('Hidden text') . '</div><div style="padding: 6px; margin: 0; display: none;"><p>';
+             $st = '</p><div class="quotebox" style="padding: 0px;"><div onclick="var e,d,c=this.parentNode,a=c.getElementsByTagName(\'div\')[1],b=this.getElementsByTagName(\'span\')[0];if(a.style.display!=\'\'){while(c.parentNode&&(!d||!e||d==e)){e=d;d=(window.getComputedStyle?getComputedStyle(c, null):c.currentStyle)[\'backgroundColor\'];if(d==\'transparent\'||d==\'rgba(0, 0, 0, 0)\')d=e;c=c.parentNode;}a.style.display=\'\';a.style.backgroundColor=d;b.innerHTML=\'&#9650;\';}else{a.style.display=\'none\';b.innerHTML=\'&#9660;\';}" style="font-weight: bold; cursor: pointer; font-size: 0.9em;"><span style="padding: 0 5px;">&#9660;</span>' . \ForkBB\__('Hidden text') . '</div><div style="padding: 6px; margin: 0; display: none;"><p>';
          }
 
          return $st . $body . '</p></div></div><p>';
@@ -303,7 +303,7 @@ return [
             }
          }
 
-         return '<a href="' . $body . '" rel="nofollow">&lt;' . __('Image link') . ' - ' . $attrs['Def'] . '&gt;</a>';
+         return '<a href="' . $body . '" rel="nofollow">&lt;' . \ForkBB\__('Image link') . ' - ' . $attrs['Def'] . '&gt;</a>';
      },
     ],
     ['tag' => 'url',

+ 160 - 9
app/functions.php

@@ -1,20 +1,39 @@
 <?php
 
-function _e($val)
+namespace ForkBB;
+
+use ForkBB\Core\Container;
+
+/**
+ * Инициализирует другие функции (передача контейнера)
+ * 
+ * @param Container $c
+ */
+function _init(Container $c)
 {
-    return htmlspecialchars($val, ENT_HTML5 | ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
+    __($c);
+    cens($c);
+    dt($c);
 }
 
-function __($data, ...$args)
+/**
+ * Транслирует строку с подстановкой аргументов
+ * 
+ * @param Container|string $arg
+ * @param mixed ...$args
+ * 
+ * @return string
+ */
+function __($arg, ...$args)
 {
-    static $lang;
+    static $c;
 
-    if (empty($lang)) {
-        $lang = $data;
+    if (empty($c)) {
+        $c = $arg;
         return;
     }
 
-    $tr = $lang->get($data);
+    $tr = $c->Lang->get($arg);
 
     if (is_array($tr)) {
         if (isset($args[0]) && is_int($args[0])) {
@@ -29,9 +48,141 @@ function __($data, ...$args)
     if (empty($args)) {
         return $tr;
     } elseif (is_array($args[0])) {
-        return strtr($tr, array_map('_e', $args[0]));
+        return strtr($tr, array_map('\ForkBB\_e', $args[0]));
     } else {
-        $args = array_map('_e', $args);
+        $args = array_map('\ForkBB\_e', $args);
         return sprintf($tr, ...$args);
     }
 }
+
+/**
+ * Экранирует спецсимволов HTML-сущностями
+ *
+ * @param  string $arg
+ *
+ * @return string
+ */
+function _e($arg)
+{
+    return htmlspecialchars($arg, ENT_HTML5 | ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
+}
+
+/**
+ * Цензура
+ * 
+ * @param Container|string $arg
+ * 
+ * @return string
+ */
+function cens($arg)
+{
+    static $c;
+    
+    if (empty($c)) {
+        $c = $arg;
+        return;
+    }
+
+    return $c->censorship->censor($arg);
+}
+
+/**
+ * Возвращает число в формате текущего пользователя
+ *
+ * @param mixed $number
+ * @param int $decimals
+ *
+ * @return string
+ */
+function num($number, $decimals = 0)
+{
+    return is_numeric($number)
+        ? number_format($number, $decimals, __('lang_decimal_point'), __('lang_thousands_sep'))
+        : 'not a number';
+}
+
+/**
+ * Возвращает дату/время в формате текущего пользователя
+ *
+ * @param Container|int $arg
+ * @param bool $dateOnly
+ * @param string $dateFormat
+ * @param string $timeFormat
+ * @param bool $timeOnly
+ * @param bool $noText
+ *
+ * @return string
+ */
+function dt($arg, $dateOnly = false, $dateFormat = null, $timeFormat = null, $timeOnly = false, $noText = false)
+{
+    static $c;
+    
+    if (empty($c)) {
+        $c = $arg;
+        return;
+    }
+
+    if (empty($arg)) {
+        return __('Never');
+    }
+
+    $diff = ($c->user->timezone + $c->user->dst) * 3600;
+    $arg += $diff;
+
+    if (null === $dateFormat) {
+        $dateFormat = $c->DATE_FORMATS[$c->user->date_format];
+    }
+    if(null === $timeFormat) {
+        $timeFormat = $c->TIME_FORMATS[$c->user->time_format];
+    }
+
+    $date = gmdate($dateFormat, $arg);
+
+    if(! $noText) {
+        $now = time() + $diff;
+
+        if ($date == gmdate($dateFormat, $now)) {
+            $date = __('Today');
+        } elseif ($date == gmdate($dateFormat, $now - 86400)) {
+            $date = __('Yesterday');
+        }
+    }
+
+    if ($dateOnly) {
+        return $date;
+    } elseif ($timeOnly) {
+        return gmdate($timeFormat, $arg);
+    } else {
+        return $date . ' ' . gmdate($timeFormat, $arg);
+    }
+}
+
+/**
+ * Преобразует timestamp в YYYY-MM-DDTHH:mm:ss.sssZ
+ * 
+ * @param int $timestamp
+ * 
+ * @return string
+ */
+function utc($timestamp)
+{
+    return gmdate('Y-m-d\TH:i:s\Z', $timestamp);
+}
+
+/**
+ * Возвращает размер в байтах, Кбайтах, ...
+ *
+ * @param int $size
+ *
+ * @return string
+ */
+function size($size)
+{
+    $units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'];
+
+    for ($i = 0; $size > 1024; ++$i) {
+        $size /= 1024;
+    }
+
+    return __('Size unit '.$units[$i], round($size, 2));
+}

+ 3 - 0
app/lang/English/post.po

@@ -96,6 +96,9 @@ msgstr "Silent edit (don't display \"Edited by ...\" in topic view)"
 msgid "Edit post"
 msgstr "Edit post"
 
+msgid "Edit topic"
+msgstr "Edit topic"
+
 msgid "Merge posts"
 msgstr "Merge with previous if it yours"
 

+ 3 - 0
app/lang/Russian/post.po

@@ -96,6 +96,9 @@ msgstr "Скрытое редактирование (не показывать \
 msgid "Edit post"
 msgstr "Редактирование сообщения"
 
+msgid "Edit topic"
+msgstr "Редактирование темы"
+
 msgid "Merge posts"
 msgstr "Соединить с предыдущим сообщением, если оно ваше"
 

+ 1 - 1
app/lang/Russian/topic.po

@@ -64,7 +64,7 @@ msgid "Delete"
 msgstr "Удалить"
 
 msgid "Edit"
-msgstr "Редактировать"
+msgstr "Изменить"
 
 msgid "Quote"
 msgstr "Цитировать"

+ 2 - 2
app/templates/admin/statistics.tpl

@@ -20,8 +20,8 @@
             <dd>
               {{ $p->dbVersion }}
   @if ($p->tRecords && $p->tSize)
-              <br>{!! __('Database data rows', $p->tRecords) !!}
-              <br>{!! __('Database data size', $p->tSize) !!}
+              <br>{!! __('Database data rows', num($p->tRecords)) !!}
+              <br>{!! __('Database data size', size($p->tSize)) !!}
   @endif
   @if ($p->tOther)
               <br><br>{!! __('Other')!!}

+ 1 - 1
app/templates/ban.tpl

@@ -3,7 +3,7 @@
       <h2>{{ __('Info') }}</h2>
       <p>{!! __('Ban message') !!}</p>
 @if (! empty($p->ban['expire']))
-      <p>{!! __('Ban message 2', $p->ban['expire']) !!}</p>
+      <p>{!! __('Ban message 2', dt($p->ban['expire'], true)) !!}</p>
 @endif
 @if (! empty($p->ban['message']))
       <p>{!! __('Ban message 3') !!}</p>

+ 5 - 5
app/templates/forum.tpl

@@ -81,7 +81,7 @@
             <div class="f-cell f-cmain">
               <div class="f-ficon"></div>
               <div class="f-finfo">
-                <h3><span class="f-fredirtext">{!! __('Moved') !!}</span> <a class="f-ftname" href="{!! $topic->link !!}">{{ $topic->cens()->subject }}</a></h3>
+                <h3><span class="f-fredirtext">{!! __('Moved') !!}</span> <a class="f-ftname" href="{!! $topic->link !!}">{{ cens($topic->subject) }}</a></h3>
               </div>
             </div>
           </li>
@@ -103,7 +103,7 @@
       @if ($topic->poll_type)
                   <span class="f-polltxt">{!! __('Poll') !!}</span>
       @endif
-                  <a class="f-ftname" href="{!! $topic->link !!}">{{ $topic->cens()->subject }}</a>
+                  <a class="f-ftname" href="{!! $topic->link !!}">{{ cens($topic->subject) }}</a>
       @if ($topic->pagination)
                   <span class="f-tpages">
         @foreach ($topic->pagination as $cur)
@@ -124,15 +124,15 @@
             </div>
             <div class="f-cell f-cstats">
               <ul>
-                <li>{!! __('%s Reply', $topic->num_replies, $topic->num()->num_replies) !!}</li>
+                <li>{!! __('%s Reply', $topic->num_replies, num($topic->num_replies)) !!}</li>
       @if ($topic->showViews)
-                <li>{!! __('%s View', $topic->num_views, $topic->num()->num_views) !!}</li>
+                <li>{!! __('%s View', $topic->num_views, num($topic->num_views)) !!}</li>
       @endif
               </ul>
             </div>
             <div class="f-cell f-clast">
               <ul>
-                <li class="f-cltopic"><a href="{!! $topic->linkLast !!}" title="&quot;{{ $topic->cens()->subject }}&quot; - {!! __('Last post') !!}">{{ $topic->dt()->last_post }}</a></li>
+                <li class="f-cltopic"><a href="{!! $topic->linkLast !!}" title="&quot;{{ cens($topic->subject) }}&quot; - {!! __('Last post') !!}">{{ dt($topic->last_post) }}</a></li>
                 <li class="f-clposter">{!! __('by') !!} {{ $topic->last_poster }}</li>
               </ul>
             </div>

+ 3 - 3
app/templates/layouts/debug.tpl

@@ -1,6 +1,6 @@
     <section class="f-debug">
       <h2>{!! __('Debug table') !!}</h2>
-      <p class="f-debugtime">[ {!! __('Querytime', $p->time, $p->numQueries) !!} - {!! __('Memory usage', $p->memory) !!} {!! __('Peak usage', $p->peak) !!} ]</p>
+      <p class="f-debugtime">[ {!! __('Querytime', num($p->time, 3), $p->numQueries) !!} - {!! __('Memory usage', size($p->memory)) !!} {!! __('Peak usage', size($p->peak)) !!} ]</p>
 @if ($p->queries)
       <table>
         <thead>
@@ -12,12 +12,12 @@
         <tbody>
   @foreach ($p->queries as $cur)
           <tr>
-            <td class="tcl">{{ $cur[1] }}</td>
+            <td class="tcl">{{ num($cur[1], 3) }}</td>
             <td class="tcr">{{ $cur[0] }}</td>
           </tr>
   @endforeach
           <tr>
-            <td class="tcl">{{ $p->total }}</td>
+            <td class="tcl">{{ num($p->total, 3) }}</td>
             <td class="tcr"></td>
           </tr>
         </tbody>

+ 5 - 5
app/templates/layouts/stats.tpl

@@ -4,9 +4,9 @@
 @if ($p->stats)
         <dl class="right">
           <dt>{!! __('Board stats') !!}</dt>
-          <dd>{!! __('No of users') !!} <strong>{!! $p->stats->num()->userTotal !!}</strong></dd>
-          <dd>{!! __('No of topics') !!} <strong>{!! $p->stats->num()->topicTotal !!}</strong></dd>
-          <dd>{!! __('No of posts') !!} <strong>{!! $p->stats->num()->postTotal !!}</strong></dd>
+          <dd>{!! __('No of users') !!} <strong>{!! num($p->stats->userTotal) !!}</strong></dd>
+          <dd>{!! __('No of topics') !!} <strong>{!! num($p->stats->topicTotal) !!}</strong></dd>
+          <dd>{!! __('No of posts') !!} <strong>{!! num($p->stats->postTotal) !!}</strong></dd>
         </dl>
 @endif
         <dl class="left">
@@ -19,10 +19,10 @@
   @endif
 @endif
 @if ($p->online)
-          <dd>{!! __('Visitors online', $p->online->num()->numUsers, $p->online->num()->numGuests) !!}</dd>
+          <dd>{!! __('Visitors online', num($p->online->numUsers), num($p->online->numGuests)) !!}</dd>
 @endif
 @if ($p->stats)
-          <dd>{!! __('Most online', $p->online->num()->maxNum, $p->online->dt()->maxTime) !!}</dd>
+          <dd>{!! __('Most online', num($p->online->maxNum), dt($p->online->maxTime)) !!}</dd>
 @endif
         </dl>
 @if ($p->online && $p->online->info)

+ 4 - 4
app/templates/layouts/subforums.tpl

@@ -49,16 +49,16 @@
               </div>
               <div class="f-cell f-cstats">
                 <ul>
-                  <li>{!! __('%s Topic', $cur->tree->num_topics, $cur->tree->num()->num_topics) !!}</li>
-                  <li>{!! __('%s Post', $cur->tree->num_posts, $cur->tree->num()->num_posts)!!}</li>
+                  <li>{!! __('%s Topic', $cur->tree->num_topics, num($cur->tree->num_topics)) !!}</li>
+                  <li>{!! __('%s Post', $cur->tree->num_posts, num($cur->tree->num_posts)) !!}</li>
                 </ul>
               </div>
               <div class="f-cell f-clast">
                 <ul>
     @if ($cur->tree->last_post_id)
-                  <li class="f-cltopic"><a href="{!! $cur->tree->linkLast !!}" title="&quot;{{ $cur->tree->cens()->last_topic }}&quot; - {!! __('Last post') !!}">{{ $cur->tree->cens()->last_topic }}</a></li>
+                  <li class="f-cltopic"><a href="{!! $cur->tree->linkLast !!}" title="&quot;{{ cens($cur->tree->last_topic) }}&quot; - {!! __('Last post') !!}">{{ cens($cur->tree->last_topic) }}</a></li>
                   <li class="f-clposter">{!! __('by') !!} {{ $cur->tree->last_poster }}</li>
-                  <li class="f-cltime">{!! $cur->tree->dt()->last_post !!}</li>
+                  <li class="f-cltime">{!! dt($cur->tree->last_post) !!}</li>
     @else
                   <li class="f-cltopic">{!! __('Never') !!}</li>
     @endif

+ 1 - 1
app/templates/post.tpl

@@ -20,7 +20,7 @@
       <h2>{!! __('Post preview') !!}</h2>
       <div class="f-post-body clearfix">
         <div class="f-post-right f-post-main">
-          {!! $p->cens()->previewHtml !!}
+          {!! cens($p->previewHtml) !!}
         </div>
       </div>
     </section>

+ 20 - 11
app/templates/topic.tpl

@@ -50,14 +50,14 @@
 @endif
     </div>
     <section class="f-main f-topic">
-      <h2>{{ $p->topic->cens()->subject }}</h2>
+      <h2>{{ cens($p->topic->subject) }}</h2>
 @foreach ($p->posts as $post)
       <article id="p{!! $post->id !!}" class="clearfix f-post @if ($post->user->gender == 1) f-user-male @elseif ($post->user->gender == 2) f-user-female @endif @if ($post->user->online) f-user-online @endif">
         <header class="f-post-header clearfix">
-          <h3>{{ $p->topic->cens()->subject }} - #{!! $post->postNumber !!}</h3>
-          <span class="f-post-posted"><time datetime="{{ $post->utc()->posted }}">{{ $post->dt()->posted }}</time></span>
+          <h3>{{ cens($p->topic->subject) }} - #{!! $post->postNumber !!}</h3>
+          <span class="f-post-posted"><time datetime="{{ utc($post->posted) }}">{{ dt($post->posted) }}</time></span>
   @if ($post->edited)
-          <span class="f-post-edited" title="{!! __('Last edit', $post->user->username, $post->dt()->edited) !!}">{!! __('Edited') !!}</span>
+          <span class="f-post-edited" title="{!! __('Last edit', $post->user->username, dt($post->edited)) !!}">{!! __('Edited') !!}</span>
   @endif
           <span class="f-post-number"><a href="{!! $post->link !!}" rel="bookmark">#{!! $post->postNumber !!}</a></span>
         </header>
@@ -76,14 +76,14 @@
   @endif
               <li class="f-usertitle"><span>{{ $post->user->title() }}</span></li>
   @if ($post->showUserInfo && $post->user->num_posts)
-              <li class="f-postcount"><span>{!! __('%s post', $post->user->num_posts, $post->user->num()->num_posts) !!}</span></li>
+              <li class="f-postcount"><span>{!! __('%s post', $post->user->num_posts, num($post->user->num_posts)) !!}</span></li>
   @endif
             </ul>
   @if ($post->showUserInfo)
             <ul class="f-user-info-add">
-              <li><span>{!! __('Registered:') !!} {{ $post->user->dt(true)->registered }}</span></li>
+              <li><span>{!! __('Registered:') !!} {{ dt($post->user->registered, true) }}</span></li>
     @if ($post->user->location)
-              <li><span>{!! __('From') !!} {{ $post->user->cens()->location }}</span></li>
+              <li><span>{!! __('From') !!} {{ cens($post->user->location) }}</span></li>
     @endif
               <li><span></span></li>
             </ul>
@@ -103,12 +103,21 @@
           <div class="f-post-left">
             <span></span>
           </div>
-  @if ($post->controls)
+  @if ($post->canReport || $post->canDelete || $post->canEdit || $post->canQuote)
           <div class="f-post-right clearfix">
             <ul>
-    @foreach ($post->controls as $key => $control)
-              <li class="f-post{!! $key !!}"><a class="f-btn" href="{!! $control[0] !!}">{!! __($control[1]) !!}</a></li>
-    @endforeach
+    @if ($post->canReport)
+              <li class="f-postreport"><a class="f-btn f-minor" href="{!! $post->linkReport !!}">{!! __('Report') !!}</a></li>
+    @endif
+    @if ($post->canDelete)
+              <li class="f-postdelete"><a class="f-btn" href="{!! $post->linkDelete !!}">{!! __('Delete') !!}</a></li>
+    @endif
+    @if ($post->canEdit)
+              <li class="f-postedit"><a class="f-btn" href="{!! $post->linkEdit !!}">{!! __('Edit') !!}</a></li>
+    @endif
+    @if ($post->canQuote)
+              <li class="f-postquote"><a class="f-btn" href="{!! $post->linkQuote !!}">{!! __('Quote') !!}</a></li>
+    @endif
             </ul>
           </div>
   @endif

+ 11 - 9
public/style/ForkBB/style.css

@@ -166,6 +166,11 @@ body,
   background-color: #814A00;
 }
 
+.f-btn.f-minor:hover,
+.f-btn.f-minor:focus {
+  opacity: 1;
+}
+
 .clearfix:before,
 .clearfix:after {
   content: " ";
@@ -1382,12 +1387,10 @@ li + li .f-btn {
 }
 
 .f-preview h2 {
-  padding: 0.625rem;
   font-family: Arial, Helvetica, sans-serif;
   font-size: 1rem;
   line-height: 1.5;
-  border-bottom: 0.0625rem dotted #AA7939;
-  background-color: #F8F4E3;
+  padding: 1rem 0.625rem 0.3125rem 0.625rem
 }
 
 .f-preview .f-post-body {
@@ -1396,26 +1399,25 @@ li + li .f-btn {
 }
 
 .f-preview .f-post-right {
-  padding: 0.625rem;
+  padding-right: 0.625rem;
+  border-top: 0.0625rem solid #AA7939;
 }
 
 /*****************************/
 .post-form {
   border-top: 0.0625rem solid #AA7939;
+  margin-bottom: 1rem;
 }
 
 .post-form > h2 {
-/*  display: none; */
-  margin-bottom: -0.625rem;
-  padding-left: 0.625rem;
   font-family: Arial, Helvetica, sans-serif;
   font-size: 1rem;
   padding-right: 0.625rem;
-  margin-top: 1rem;
+  padding: 1rem 0.625rem 0.3125rem 0.625rem
 }
 
 .post-form > .f-fdiv {
-  margin: 1rem 0;
+/*  margin: 1rem 0; */
   background-color: #F8F4E3;
 }