Visman 7 anni fa
parent
commit
87b8c632ed

+ 4 - 0
app/Core/Parser.php

@@ -50,6 +50,10 @@ class Parser extends Parserus
 
             $this->setSmilies($smilies)->setSmTpl($info['smTpl'], $info['smTplTag'], $info['smTplBl']);
         }
+
+        $this->setAttr('baseUrl', $this->c->BASE_URL);
+        $this->setAttr('showImg', $this->c->user->show_img != '0');
+        $this->setAttr('showImgSign', $this->c->user->show_img_sig != '0');
     }
 
     /**

+ 1 - 1
app/Core/Validator.php

@@ -150,7 +150,7 @@ class Validator
             $rules = [];
             // псевдоним содержится в списке правил
             if (is_array($raw)) {
-                list($raw, $this->aliases[$field]) = $raw;
+                list($raw, $this->aliases[$field]) = $raw; //????
             }
             // перебор правил для текущего поля
             foreach (explode('|', $raw) as $rule) {

+ 3 - 331
app/Models/Pages/Post.php

@@ -11,6 +11,8 @@ use ForkBB\Models\Page;
 class Post extends Page
 {
     use CrumbTrait;
+    use PostFormTrait;
+    use PostValidatorTrait;
 
     /**
      * Подготовка данных для шаблона создания темы
@@ -123,8 +125,7 @@ class Post extends Page
      */
     public function newReplyPost(array $args)
     {
-        $tid   = (int) $args['id'];
-        $topic = $this->c->ModelTopic->load($tid);
+        $topic = $this->c->ModelTopic->load((int) $args['id']);
         
         if (empty($topic) || $topic->moved_to || ! $topic->canReply) {
             return $this->c->Message->message('Bad request');
@@ -264,333 +265,4 @@ class Post extends Page
             ->page('ViewPost', ['id' => $merge ? $lastPost->id : $post->id])
             ->message(__('Post redirect'));
     }
-
-    /**
-     * Дополнительная проверка email
-     * 
-     * @param Validator $v
-     * @param string $email
-     * 
-     * @return string
-     */
-    public function vCheckEmail(Validator $v, $email)
-    {
-        $user = $this->c->ModelUser;
-        $user->email = $email;
-
-        // email забанен
-        if ($this->c->bans->isBanned($user) > 0) {
-            $v->addError('Banned email');
-        }
-        return $email;
-    }
-
-    /**
-     * Дополнительная проверка username
-     * 
-     * @param Validator $v
-     * @param string $username
-     * 
-     * @return string
-     */
-    public function vCheckUsername(Validator $v, $username)
-    {
-        $user = $this->c->ModelUser;
-        $user->username = $username;
-
-        // username = Гость
-        if (preg_match('%^(guest|' . preg_quote(__('Guest'), '%') . ')$%iu', $username)) {
-            $v->addError('Username guest');
-        // цензура
-        } elseif ($user->cens()->username !== $username) {
-            $v->addError('Username censor');
-        // username забанен
-        } elseif ($this->c->bans->isBanned($user) > 0) {
-            $v->addError('Banned username');
-        }
-        return $username;
-    }
-
-    /**
-     * Дополнительная проверка subject
-     * 
-     * @param Validator $v
-     * @param string $subject
-     * 
-     * @return string
-     */
-    public function vCheckSubject(Validator $v, $subject, $attr, $executive)
-    {
-        // после цензуры заголовок темы путой
-        if ($this->c->censorship->censor($subject) == '') {
-            $v->addError('No subject after censoring');
-        // заголовок темы только заглавными буквами
-        } elseif (! $executive
-            && $this->c->config->p_subject_all_caps == '0'
-            && preg_match('%\p{Lu}%u', $subject)
-            && ! preg_match('%\p{Ll}%u', $subject)
-        ) {
-            $v->addError('All caps subject');
-        }
-        return $subject;
-    }
-
-    /**
-     * Дополнительная проверка message
-     * 
-     * @param Validator $v
-     * @param string $message
-     * 
-     * @return string
-     */
-    public function vCheckMessage(Validator $v, $message, $attr, $executive)
-    {
-        // после цензуры текст сообщения пустой
-        if ($this->c->censorship->censor($message) == '') {
-            $v->addError('No message after censoring');
-        // текст сообщения только заглавными буквами
-        } elseif (! $executive
-            && $this->c->config->p_message_all_caps == '0'
-            && preg_match('%\p{Lu}%u', $message)
-            && ! preg_match('%\p{Ll}%u', $message)
-        ) {
-            $v->addError('All caps message');
-        // проверка парсером
-        } else {
-            $message = $this->c->Parser->prepare($message); //????
-
-            foreach($this->c->Parser->getErrors() as $error) {
-                $v->addError($error);
-            } 
-        }
-
-        return $message;
-    }
-
-    /**
-     * Проверка времени ограничения флуда
-     * 
-     * @param Validator $v
-     * @param null|string $submit
-     * 
-     * @return null|string
-     */
-    public function vCheckTimeout(Validator $v, $submit)
-    {
-        if (null === $submit) {
-            return null;
-        }
-
-        $user = $this->c->user;
-        $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');
-        }
-
-        return $submit;
-    }
-
-    /**
-     * Подготовка валидатора к проверке данных из формы создания темы/сообщения
-     * 
-     * @param Model $model
-     * @param string $marker
-     * @param array $args
-     * @param bool $editSubject
-     * 
-     * @return Validator
-     */
-    protected function messageValidator(Model $model, $marker, array $args, $editSubject = false)
-    {
-        if ($this->c->user->isGuest) {
-            $ruleEmail    = ($this->c->config->p_force_guest_email == '1' ? 'required|' : '') . 'string:trim,lower|email|check_email';
-            $ruleUsername = 'required|string:trim,spaces|min:2|max:25|login|check_username';
-        } else {
-            $ruleEmail    = 'absent';
-            $ruleUsername = 'absent';
-        }
-
-        if ($editSubject) {
-            $ruleSubject = 'required|string:trim,spaces|min:1|max:70|check_subject';
-        } else {
-            $ruleSubject = 'absent';
-        }
-
-        if ($this->c->user->isAdmin || $this->c->user->isModerator($model)) {
-            if ($editSubject) {
-                $ruleStickTopic = 'checkbox';
-                $ruleStickFP    = 'checkbox';
-                $ruleMergePost  = 'absent';
-            } else {
-                $ruleStickTopic = 'absent';
-                $ruleStickFP    = 'absent';
-                $ruleMergePost  = 'checkbox';
-            }
-            $executive          = true;
-        } else {
-            $ruleStickTopic     = 'absent';
-            $ruleStickFP        = 'absent';
-            $ruleMergePost      = 'absent:1';
-            $executive          = false;
-        }
-
-        if ($this->c->config->o_smilies == '1') {
-            $ruleHideSmilies = 'checkbox';
-        } else {
-            $ruleHideSmilies = 'absent';
-        }
-            
-        $v = $this->c->Validator->addValidators([
-            'check_email'    => [$this, 'vCheckEmail'],
-            'check_username' => [$this, 'vCheckUsername'],
-            'check_subject'  => [$this, 'vCheckSubject'],
-            'check_message'  => [$this, 'vCheckMessage'],
-            'check_timeout'  => [$this, 'vCheckTimeout'],
-        ])->setRules([
-            'token'        => 'token:' . $marker,
-            'email'        => [$ruleEmail, __('Email')],
-            'username'     => [$ruleUsername, __('Username')],
-            'subject'      => [$ruleSubject, __('Subject')],
-            'stick_topic'  => $ruleStickTopic,
-            'stick_fp'     => $ruleStickFP,
-            'merge_post'   => $ruleMergePost,
-            'hide_smilies' => $ruleHideSmilies,
-            'preview'      => 'string', //????
-            'submit'       => 'string|check_timeout', //????
-            'message'      => 'required|string:trim|max:' . $this->c->MAX_POST_SIZE . '|check_message',
-        ])->setArguments([
-            'token'                 => $args,
-            'subject.check_subject' => $executive,
-            'message.check_message' => $executive,
-        ])->setMessages([
-            'username.login' => __('Login format'),
-        ]);
-
-        return $v;
-    }
-
-    /**
-     * Возвращает данные для построения формы создания темы/сообщения
-     * 
-     * @param Model $model
-     * @param string $marker
-     * @param array $args
-     * @param bool $editSubject
-     * 
-     * @return array
-     */
-    protected function messageForm(Model $model, $marker, array $args, $editSubject = false)
-    {
-        $vars = isset($args['_vars']) ? $args['_vars'] : null;
-        unset($args['_vars']);
-
-        $autofocus = true;
-        $form = [
-            'action' => $this->c->Router->link($marker, $args),
-            'hidden' => [
-                'token' => $this->c->Csrf->create($marker, $args),
-            ],
-            'sets'   => [],
-            'btns'   => [
-                'submit'  => ['submit', __('Submit'), 's'],
-                'preview' => ['submit', __('Preview'), 'p'],
-            ],
-        ];
-
-        $fieldset = [];
-        if ($this->c->user->isGuest) {
-            $fieldset['username'] = [
-                'dl'        => 't1',
-                'type'      => 'text',
-                'maxlength' => 25,
-                'title'     => __('Username'),
-                'required'  => true,
-                'pattern'   => '^.{2,25}$',
-                'value'     => isset($vars['username']) ? $vars['username'] : null,
-                'autofocus' => $autofocus,
-            ];
-            $fieldset['email'] = [
-                'dl'        => 't2',
-                'type'      => 'text',
-                'maxlength' => 80,
-                'title'     => __('Email'),
-                'required'  => $this->c->config->p_force_guest_email == '1',
-                'pattern'   => '.+@.+',
-                'value'     => isset($vars['email']) ? $vars['email'] : null,
-            ];
-            $autofocus = null;
-        }
-
-        if ($editSubject) {
-            $fieldset['subject'] = [
-                'type'      => 'text',
-                'maxlength' => 70,
-                'title'     => __('Subject'),
-                'required'  => true,
-                'value'     => isset($vars['subject']) ? $vars['subject'] : null,
-                'autofocus' => $autofocus,
-            ];
-            $autofocus = null;
-        }
-
-        $fieldset['message'] = [
-            'type'     => 'textarea',
-            'title'    => __('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')],
-            ],
-            'autofocus' => $autofocus,
-        ];
-        $form['sets'][] = [
-            'fields' => $fieldset,
-        ];
-        $autofocus = null;
-        
-        $fieldset = [];
-        if ($this->c->user->isAdmin || $this->c->user->isModerator($model)) {
-            if ($editSubject) {
-                $fieldset['stick_topic'] = [
-                    'type'    => 'checkbox',
-                    'label'   => __('Stick topic'),
-                    'value'   => '1',
-                    'checked' => isset($vars['stick_topic']) ? (bool) $vars['stick_topic'] : false,
-                ];
-                $fieldset['stick_fp'] = [
-                    'type'    => 'checkbox',
-                    'label'   => __('Stick first post'),
-                    'value'   => '1',
-                    'checked' => isset($vars['stick_fp']) ? (bool) $vars['stick_fp'] : false,
-                ];
-            } else {
-                $fieldset['merge_post'] = [
-                    'type'    => 'checkbox',
-                    'label'   => __('Merge posts'),
-                    'value'   => '1',
-                    'checked' => isset($vars['merge_post']) ? (bool) $vars['merge_post'] : true,
-                ];
-            }
-        }
-        if ($this->c->config->o_smilies == '1') {
-            $fieldset['hide_smilies'] = [
-                'type'    => 'checkbox',
-                'label'   => __('Hide smilies'),
-                'value'   => '1',
-                'checked' => isset($vars['hide_smilies']) ? (bool) $vars['hide_smilies'] : false,
-            ];
-        }
-        if ($fieldset) {
-            $form['sets'][] = [
-                'legend' => __('Options'),
-                'fields' => $fieldset,
-            ];
-        }
-
-        return $form;
-    }
 }

+ 142 - 0
app/Models/Pages/PostFormTrait.php

@@ -0,0 +1,142 @@
+<?php
+
+namespace ForkBB\Models\Pages;
+
+use ForkBB\Models\Model;
+
+trait PostFormTrait 
+{
+    /**
+     * Возвращает данные для построения формы создания темы/сообщения
+     * 
+     * @param Model $model
+     * @param string $marker
+     * @param array $args
+     * @param bool $editSubject
+     * @param bool $quickReply
+     * 
+     * @return array
+     */
+    protected function messageForm(Model $model, $marker, array $args, $editSubject = false, $quickReply = false)
+    {
+        $vars = isset($args['_vars']) ? $args['_vars'] : null;
+        unset($args['_vars']);
+
+        $autofocus = $quickReply ? null : true;
+        $form = [
+            'action' => $this->c->Router->link($marker, $args),
+            'hidden' => [
+                'token' => $this->c->Csrf->create($marker, $args),
+            ],
+            'sets'   => [],
+            'btns'   => [
+                'submit'  => [
+                    'type'      => 'submit', 
+                    'value'     => __('Submit'), 
+                    'accesskey' => 's',
+                ],
+                'preview' => [
+                    'type'      => 'submit', 
+                    'value'     => __('Preview'), 
+                    'accesskey' => 'p',
+                    'class'     => 'f-minor',
+                ],
+            ],
+        ];
+
+        $fieldset = [];
+        if ($this->c->user->isGuest) {
+            $fieldset['username'] = [
+                'dl'        => 't1',
+                'type'      => 'text',
+                'maxlength' => 25,
+                'title'     => __('Username'),
+                'required'  => true,
+                'pattern'   => '^.{2,25}$',
+                'value'     => isset($vars['username']) ? $vars['username'] : null,
+                'autofocus' => $autofocus,
+            ];
+            $fieldset['email'] = [
+                'dl'        => 't2',
+                'type'      => 'text',
+                'maxlength' => 80,
+                'title'     => __('Email'),
+                'required'  => $this->c->config->p_force_guest_email == '1',
+                'pattern'   => '.+@.+',
+                'value'     => isset($vars['email']) ? $vars['email'] : null,
+            ];
+            $autofocus = null;
+        }
+
+        if ($editSubject) {
+            $fieldset['subject'] = [
+                'type'      => 'text',
+                'maxlength' => 70,
+                'title'     => __('Subject'),
+                'required'  => true,
+                'value'     => isset($vars['subject']) ? $vars['subject'] : null,
+                'autofocus' => $autofocus,
+            ];
+            $autofocus = null;
+        }
+
+        $fieldset['message'] = [
+            'type'     => 'textarea',
+            'title'    => __('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')],
+            ],
+            'autofocus' => $autofocus,
+        ];
+        $form['sets'][] = [
+            'fields' => $fieldset,
+        ];
+        $autofocus = null;
+        
+        $fieldset = [];
+        if ($this->c->user->isAdmin || $this->c->user->isModerator($model)) {
+            if ($editSubject) {
+                $fieldset['stick_topic'] = [
+                    'type'    => 'checkbox',
+                    'label'   => __('Stick topic'),
+                    'value'   => '1',
+                    'checked' => isset($vars['stick_topic']) ? (bool) $vars['stick_topic'] : false,
+                ];
+                $fieldset['stick_fp'] = [
+                    'type'    => 'checkbox',
+                    'label'   => __('Stick first post'),
+                    'value'   => '1',
+                    'checked' => isset($vars['stick_fp']) ? (bool) $vars['stick_fp'] : false,
+                ];
+            } else {
+                $fieldset['merge_post'] = [
+                    'type'    => 'checkbox',
+                    'label'   => __('Merge posts'),
+                    'value'   => '1',
+                    'checked' => isset($vars['merge_post']) ? (bool) $vars['merge_post'] : true,
+                ];
+            }
+        }
+        if (! $quickReply && $this->c->config->o_smilies == '1') {
+            $fieldset['hide_smilies'] = [
+                'type'    => 'checkbox',
+                'label'   => __('Hide smilies'),
+                'value'   => '1',
+                'checked' => isset($vars['hide_smilies']) ? (bool) $vars['hide_smilies'] : false,
+            ];
+        }
+        if ($fieldset) {
+            $form['sets'][] = [
+                'legend' => __('Options'),
+                'fields' => $fieldset,
+            ];
+        }
+
+        return $form;
+    }
+}

+ 219 - 0
app/Models/Pages/PostValidatorTrait.php

@@ -0,0 +1,219 @@
+<?php
+
+namespace ForkBB\Models\Pages;
+
+use ForkBB\Core\Validator;
+use ForkBB\Models\Model;
+
+trait PostValidatorTrait 
+{
+    /**
+     * Дополнительная проверка email
+     * 
+     * @param Validator $v
+     * @param string $email
+     * 
+     * @return string
+     */
+    public function vCheckEmail(Validator $v, $email)
+    {
+        $user = $this->c->ModelUser;
+        $user->email = $email;
+
+        // email забанен
+        if ($this->c->bans->isBanned($user) > 0) {
+            $v->addError('Banned email');
+        }
+        return $email;
+    }
+
+    /**
+     * Дополнительная проверка username
+     * 
+     * @param Validator $v
+     * @param string $username
+     * 
+     * @return string
+     */
+    public function vCheckUsername(Validator $v, $username)
+    {
+        $user = $this->c->ModelUser;
+        $user->username = $username;
+
+        // username = Гость
+        if (preg_match('%^(guest|' . preg_quote(__('Guest'), '%') . ')$%iu', $username)) {
+            $v->addError('Username guest');
+        // цензура
+        } elseif ($user->cens()->username !== $username) {
+            $v->addError('Username censor');
+        // username забанен
+        } elseif ($this->c->bans->isBanned($user) > 0) {
+            $v->addError('Banned username');
+        }
+        return $username;
+    }
+
+    /**
+     * Дополнительная проверка subject
+     * 
+     * @param Validator $v
+     * @param string $subject
+     * 
+     * @return string
+     */
+    public function vCheckSubject(Validator $v, $subject, $attr, $executive)
+    {
+        // после цензуры заголовок темы путой
+        if ($this->c->censorship->censor($subject) == '') {
+            $v->addError('No subject after censoring');
+        // заголовок темы только заглавными буквами
+        } elseif (! $executive
+            && $this->c->config->p_subject_all_caps == '0'
+            && preg_match('%\p{Lu}%u', $subject)
+            && ! preg_match('%\p{Ll}%u', $subject)
+        ) {
+            $v->addError('All caps subject');
+        } elseif (! $executive
+            && $this->c->user->g_post_links != '1'
+            && preg_match('%https?://|www\.%ui', $subject)
+        ) {
+            $v->addError('You can not post links in subject');
+        }
+        return $subject;
+    }
+
+    /**
+     * Дополнительная проверка message
+     * 
+     * @param Validator $v
+     * @param string $message
+     * 
+     * @return string
+     */
+    public function vCheckMessage(Validator $v, $message, $attr, $executive)
+    {
+        // после цензуры текст сообщения пустой
+        if ($this->c->censorship->censor($message) == '') {
+            $v->addError('No message after censoring');
+        // текст сообщения только заглавными буквами
+        } elseif (! $executive
+            && $this->c->config->p_message_all_caps == '0'
+            && preg_match('%\p{Lu}%u', $message)
+            && ! preg_match('%\p{Ll}%u', $message)
+        ) {
+            $v->addError('All caps message');
+        // проверка парсером
+        } else {
+            $message = $this->c->Parser->prepare($message); //????
+
+            foreach($this->c->Parser->getErrors() as $error) {
+                $v->addError($error);
+            } 
+        }
+
+        return $message;
+    }
+
+    /**
+     * Проверка времени ограничения флуда
+     * 
+     * @param Validator $v
+     * @param null|string $submit
+     * 
+     * @return null|string
+     */
+    public function vCheckTimeout(Validator $v, $submit)
+    {
+        if (null === $submit) {
+            return null;
+        }
+
+        $user = $this->c->user;
+        $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');
+        }
+
+        return $submit;
+    }
+
+    /**
+     * Подготовка валидатора к проверке данных из формы создания темы/сообщения
+     * 
+     * @param Model $model
+     * @param string $marker
+     * @param array $args
+     * @param bool $editSubject
+     * 
+     * @return Validator
+     */
+    protected function messageValidator(Model $model, $marker, array $args, $editSubject = false)
+    {
+        if ($this->c->user->isGuest) {
+            $ruleEmail    = ($this->c->config->p_force_guest_email == '1' ? 'required|' : '') . 'string:trim,lower|email|check_email';
+            $ruleUsername = 'required|string:trim,spaces|min:2|max:25|login|check_username';
+        } else {
+            $ruleEmail    = 'absent';
+            $ruleUsername = 'absent';
+        }
+
+        if ($editSubject) {
+            $ruleSubject = 'required|string:trim,spaces|min:1|max:70|check_subject';
+        } else {
+            $ruleSubject = 'absent';
+        }
+
+        if ($this->c->user->isAdmin || $this->c->user->isModerator($model)) {
+            if ($editSubject) {
+                $ruleStickTopic = 'checkbox';
+                $ruleStickFP    = 'checkbox';
+                $ruleMergePost  = 'absent';
+            } else {
+                $ruleStickTopic = 'absent';
+                $ruleStickFP    = 'absent';
+                $ruleMergePost  = 'checkbox';
+            }
+            $executive          = true;
+        } else {
+            $ruleStickTopic     = 'absent';
+            $ruleStickFP        = 'absent';
+            $ruleMergePost      = 'absent:1';
+            $executive          = false;
+        }
+
+        if ($this->c->config->o_smilies == '1') {
+            $ruleHideSmilies = 'checkbox';
+        } else {
+            $ruleHideSmilies = 'absent';
+        }
+            
+        $v = $this->c->Validator->addValidators([
+            'check_email'    => [$this, 'vCheckEmail'],
+            'check_username' => [$this, 'vCheckUsername'],
+            'check_subject'  => [$this, 'vCheckSubject'],
+            'check_message'  => [$this, 'vCheckMessage'],
+            'check_timeout'  => [$this, 'vCheckTimeout'],
+        ])->setRules([
+            'token'        => 'token:' . $marker,
+            'email'        => [$ruleEmail, __('Email')],
+            'username'     => [$ruleUsername, __('Username')],
+            'subject'      => [$ruleSubject, __('Subject')],
+            'stick_topic'  => $ruleStickTopic,
+            'stick_fp'     => $ruleStickFP,
+            'merge_post'   => $ruleMergePost,
+            'hide_smilies' => $ruleHideSmilies,
+            'preview'      => 'string',
+            'submit'       => 'string|check_timeout',
+            'message'      => 'required|string:trim|max:' . $this->c->MAX_POST_SIZE . '|check_message',
+        ])->setArguments([
+            'token'                 => $args,
+            'subject.check_subject' => $executive,
+            'message.check_message' => $executive,
+        ])->setMessages([
+            'username.login' => __('Login format'),
+        ]);
+
+        return $v;
+    }
+}

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

@@ -7,6 +7,7 @@ use ForkBB\Models\Page;
 class Topic extends Page
 {
     use CrumbTrait;
+    use PostFormTrait;
 
     /**
      * Переход к первому новому сообщению темы (или в конец)
@@ -135,75 +136,6 @@ class Topic extends Page
 
         $this->c->Lang->load('topic');
 
-        $user = $this->c->user;
-
-        // данные для формы быстрого ответа
-        $form = null;
-        if ($topic->canReply && $this->c->config->o_quickpost == '1') {
-            $form = [
-                'action' => $this->c->Router->link('NewReply', ['id' => $topic->id]),
-                'hidden' => [
-                    'token' => $this->c->Csrf->create('NewReply', ['id' => $topic->id]),
-                ],
-                'sets'   => [],
-                'btns'   => [
-                    'submit'  => ['submit', __('Submit'), 's'],
-                    'preview' => ['submit', __('Preview'), 'p'],
-                ],
-            ];
-
-            $fieldset = [];
-            if ($user->isGuest) {
-                $fieldset['username'] = [
-                    'dl'        => 't1',
-                    'type'      => 'text',
-                    'maxlength' => 25,
-                    'title'     => __('Username'),
-                    'required'  => true,
-                    'pattern'   => '^.{2,25}$',
-                ];
-                $fieldset['email'] = [
-                    'dl'        => 't2',
-                    'type'      => 'text',
-                    'maxlength' => 80,
-                    'title'     => __('Email'),
-                    'required'  => $this->c->config->p_force_guest_email == '1',
-                    'pattern'   => '.+@.+',
-                ];
-            }
-
-            $fieldset['message'] = [
-                'type'     => 'textarea',
-                'title'    => __('Message'),
-                'required' => true,
-                'bb'       => [
-                    ['link', __('BBCode'), __($this->c->config->p_message_bbcode == '1' ? 'on' : 'off')],
-                    ['link', __('url tag'), __($this->c->config->p_message_bbcode == '1' && $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')],
-                ],
-            ];
-            $form['sets'][] = [
-                'fields' => $fieldset,
-            ];
-
-            $fieldset = [];
-            if ($user->isAdmin || $user->isModerator($topic)) {
-                $fieldset['merge_post'] = [
-                    'type'    => 'checkbox',
-                    'label'   => __('Merge posts'),
-                    'value'   => '1',
-                    'checked' => true,
-                ];
-            }
-            if ($fieldset) {
-                $form['sets'][] = [
-                    'legend' => __('Options'),
-                    'fields' => $fieldset,
-                ];
-            }
-        }
-
         $this->nameTpl      = 'topic';
         $this->onlinePos    = 'topic-' . $topic->id;
         $this->onlineDetail = true;
@@ -213,7 +145,9 @@ class Topic extends Page
         $this->crumbs       = $this->crumbs($topic);
         $this->online       = $this->c->Online->calc($this)->info();
         $this->stats        = null;
-        $this->form         = $form;
+        $this->form         = $topic->canReply && $this->c->config->o_quickpost == '1' 
+                                ? $this->messageForm($topic, 'NewReply', ['id' => $topic->id], false, true) 
+                                : null;
 
         if ($topic->showViews) {
             $topic->incViews();

+ 2 - 2
app/Models/Post.php

@@ -21,7 +21,7 @@ class Post extends DataModel
     /**
      * Автор сообщения
      *
-     * @return Models\User
+     * @return User
      */
     protected function getuser()
     {
@@ -103,7 +103,7 @@ class Post extends DataModel
             }
         }
         if ($this->parent->canReply) {
-            $controls['quote'] = [$this->c->Router->link('NewReply', ['id' => $this->parent->id, 'quote' => $this->id]), 'Reply'];
+            $controls['quote'] = [$this->c->Router->link('NewReply', ['id' => $this->parent->id, 'quote' => $this->id]), 'Quote'];
         }
         return $controls;
     }

+ 86 - 15
app/Models/Post/Load.php

@@ -7,6 +7,54 @@ use ForkBB\Models\Topic;
 
 class Load extends MethodModel
 {
+    protected $aliases;
+
+    protected function queryFields(array $args)
+    {
+        $result  = [];
+        $fields  = [];
+        $aliases = [];
+        foreach ($args as $alias => $rawFields) {
+            $aliases[$alias] = [];
+            foreach ($rawFields as $originalName => $replName) {
+                if (null === $replName || false === $replName) {
+                    continue;
+                }
+
+                $name = $alias . '.' . $originalName;
+
+                if (true === $replName && isset($fields[$originalName])) {
+                    $replName = "alias_{$alias}_{$originalName}";
+                }
+
+                if (true === $replName) {
+                    $result[] = $name;
+                    $aliases[$alias][$originalName] = true;
+                    $fields[$originalName] = $alias;
+                } else {
+                    $result[] = $name . ' AS '. $replName;
+                    $aliases[$alias][$replName] = $originalName;
+                    $fields[$replName] = $alias;
+                }
+            }
+        }
+        $this->aliases = $aliases;
+
+        return implode(', ', $result);
+    }
+
+    protected function setData(array $data, array $args)
+    {
+        foreach ($args as $alias => $model) {
+            $attrs = [];
+            foreach ($this->aliases[$alias] as $key => $repl) {
+                $name = true === $repl ? $key : $repl;
+                $attrs[$name] = $data[$key];
+            }
+            $model->setAttrs($attrs);
+        }
+    }
+
     /**
      * Заполняет модель данными из БД
      *
@@ -17,30 +65,53 @@ class Load extends MethodModel
      */
     public function load($id, Topic $topic = null)
     {
-        $vars = [
-            ':pid' => $id,
-        ];
-        $where = 'p.id=?i:pid';
-
-        if ($topic) {
-            $vars[':tid'] = $topic->id;
-            $where       .= ' AND p.topic_id=?i:tid';
-        }
+        // пост + топик
+        if (null === $topic) {
 
-        $sql = 'SELECT p.* FROM ::posts AS p WHERE ' . $where;
+            $fileds = $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
+            ]);
 
-        $data = $this->c->DB->query($sql, $vars)->fetch();
+            $vars = [
+                ':pid' => $id,
+                ':fields' => $fields,
+            ];
 
+            $sql = 'SELECT ?p:fileds
+                    FROM ::posts AS p 
+                    INNER JOIN ::topics AS t ON t.id=p.topic_id
+                    WHERE p.id=?i:pid';
+        // только пост
+        } else {
+            $vars = [
+                ':pid' => $id,
+                ':tid' => $topic->id,
+            ];
+
+            $sql = 'SELECT p.* 
+                    FROM ::posts AS p 
+                    WHERE p.id=?i:pid AND p.topic_id=?i:tid';
+        }
+
+        $data = $this->c->DB->query($sql, $vars)->fetch();
+                    
         // пост отсутствует или недоступен
         if (empty($data)) {
             return null;
         }
 
-        $this->model->setAttrs($data);
-        if ($topic) {
-            $this->model->__parent = $topic;
+        if (null === $topic) {
+            $topic = $this->c->ModelTopic;
+            $this->setData($data, [
+                'p' => $this->model,
+                't' => $topic,
+            ]);
+        } else {
+            $this->model->setAttrs($data);
         }
-
+        $this->model->__parent = $topic;
+        
         return $this->model;
     }
 }

+ 10 - 2
app/Models/User.php

@@ -63,6 +63,10 @@ class User extends DataModel
      */
     public function isModerator(Model $model)
     {
+        if ($this->g_moderator != '1') {
+            return false;
+        }
+        
         while (! $model instanceof Forum) {
             $model = $model->parent;
             if (! $model instanceof Model) {
@@ -135,11 +139,15 @@ class User extends DataModel
     /**
      * Ссылка на профиль пользователя
      *
-     * @return string
+     * @return null|string
      */
     protected function getlink()
     {
-        return $this->c->Router->link('User', ['id' => $this->id, 'name' => $this->username]);
+        if ($this->isGuest) {
+            return null;
+        } else {
+            return $this->c->Router->link('User', ['id' => $this->id, 'name' => $this->username]);
+        }
     }
 
     /**

+ 12 - 17
app/config/defaultBBCode.php

@@ -30,17 +30,17 @@ return [
          'no attr' => true,
      ],
      'handler' => function($body, $attrs) {
-         return '</p><div class="codebox"><pre><code>' . trim($body, "\n\r") . '</code></pre></div><p>';
+         return '</p><pre class="f-bb-code"><code>' . trim($body, "\n\r") . '</code></pre><p>';
      },
     ],
     ['tag' => 'b',
      'handler' => function($body) {
-         return '<strong>' . $body . '</strong>';
+         return '<b>' . $body . '</b>';
      },
     ],
     ['tag' => 'i',
      'handler' => function($body) {
-         return '<em>' . $body . '</em>';
+         return '<i>' . $body . '</i>';
      },
     ],
     ['tag' => 'em',
@@ -50,12 +50,12 @@ return [
     ],
     ['tag' => 'u',
      'handler' => function($body) {
-         return '<span class="bbu">' . $body . '</span>';
+         return '<span class="f-bb-u">' . $body . '</span>';
      },
     ],
     ['tag' => 's',
      'handler' => function($body) {
-         return '<span class="bbs">' . $body . '</span>';
+         return '<s>' . $body . '</s>';
      },
     ],
     ['tag' => 'del',
@@ -71,7 +71,7 @@ return [
     ['tag' => 'h',
      'type' => 'h',
      'handler' => function($body) {
-         return '</p><h5>' . $body . '</h5><p>';
+         return '</p><p class="f-bb-header">' . $body . '</p><p>';
      },
     ],
     ['tag' => 'hr',
@@ -205,11 +205,11 @@ return [
 
          switch ($attrs['Def'][0]) {
              case 'a':
-                 return '</p><ol class="alpha">' . $body . '</ol><p>';
+                 return '</p><ol class="f-bb-l-lat">' . $body . '</ol><p>';
              case '1':
-                 return '</p><ol class="decimal">' . $body . '</ol><p>';
+                 return '</p><ol class="f-bb-l-dec">' . $body . '</ol><p>';
              default:
-                 return '</p><ul>' . $body . '</ul><p>';
+                 return '</p><ul class="f-bb-l-disc">' . $body . '</ul><p>';
          }
      },
     ],
@@ -242,7 +242,7 @@ return [
 
          $attr = __('After time') . ' ' . implode(' ', $arr);
 
-         return '<span style="color: #808080"><em>' . $attr . ':</em></span><br />';
+         return '</p><p class="f-bb-after">' . $attr . ':</p><p>';
      },
     ],
     ['tag' => 'quote',
@@ -253,13 +253,8 @@ return [
          'no attr' => true,
      ],
      'handler' => function($body, $attrs) {
-         if (isset($attrs['Def'])) {
-             $st = '</p><div class="quotebox"><cite>' . $attrs['Def'] .  ' ' . __('wrote') . '</cite><blockquote><div><p>';
-         } else {
-             $st = '</p><div class="quotebox"><blockquote><div><p>';
-         }
-
-         return $st . $body . '</p></div></blockquote></div><p>';
+         $header = isset($attrs['Def']) ? '<div class="f-bb-q-header">' . $attrs['Def'] .  ' ' . __('wrote') . '</div>' : '';
+         return "</p><blockquote class=\"f-bb-quote\">{$header}<div class=\"f-bb-q-body\"><p>{$body}</p></div></blockquote><p>";
      },
     ],
     ['tag' => 'spoiler',

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

@@ -12,6 +12,9 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Language: en\n"
 
+msgid "You can not post links in subject"
+msgstr "You can not post links in subject."
+
 msgid "No subject"
 msgstr "Topics must contain a subject."
 

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

@@ -12,6 +12,9 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Language: ru\n"
 
+msgid "You can not post links in subject"
+msgstr "Вы не можете публиковать ссылки в заголовке темы."
+
 msgid "No subject"
 msgstr "Тема должна содержать заголовок."
 

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

@@ -9,7 +9,7 @@
             <dt>{!! __('Environment label') !!}</dt>
             <dd>
               {!! __('Environment data OS', PHP_OS) !!}<br>
-              {!! __('Environment data version', phpversion()) !!} - <a href="{!! $p->linkInfo !!}">{!! __('Show info') !!}</a><br>
+              {!! __('Environment data version', PHP_VERSION) !!} - <a href="{!! $p->linkInfo !!}">{!! __('Show info') !!}</a><br>
   @if ($p->linkAcc)
               {!! __('Environment data acc') !!} <a href="{!! $p->linkAcc !!}">{{ $p->accelerator }}</a>
   @else

+ 1 - 1
app/templates/layouts/form.tpl

@@ -37,7 +37,7 @@
 @endforeach
           <p>
 @foreach ($form['btns'] as $key => $cur)
-            <input class="f-btn" type="{{ $cur[0] }}" name="{{ $key }}" value="{{ $cur[1] }}" accesskey="{{ $cur[2] }}">
+            <input class="f-btn @if(isset($cur['class'])) {{ $cur['class'] }} @endif" type="{{ $cur['type'] }}" name="{{ $key }}" value="{{ $cur['value'] }}" @if (isset($cur['accesskey'])) accesskey="{{ $cur['accesskey'] }}" @endif>
 @endforeach
           </p>
         </form>

+ 2 - 2
app/templates/topic.tpl

@@ -64,7 +64,7 @@
         <div class="f-post-body clearfix">
           <address class="f-post-left clearfix">
             <ul class="f-user-info">
-  @if ($post->showUserLink)
+  @if ($post->showUserLink && $post->user->link)
               <li class="f-username"><a href="{!! $post->user->link !!}">{{ $post->user->username }}</a></li>
   @else
               <li class="f-username">{{ $post->user->username }}</li>
@@ -75,7 +75,7 @@
               </li>
   @endif
               <li class="f-usertitle"><span>{{ $post->user->title() }}</span></li>
-  @if ($post->showUserInfo)
+  @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>
   @endif
             </ul>

+ 73 - 1
public/style/ForkBB/style.css

@@ -150,6 +150,10 @@ body,
           user-select: none;
 }
 
+.f-btn.f-minor {
+  opacity: 0.5;
+}
+
 .f-wrap a:hover,
 .f-wrap a:focus,
 .f-btn:hover,
@@ -1202,11 +1206,13 @@ select {
 }
 
 .f-post-right {
-  padding: 0.625rem;
+  padding: 0.625rem 0.625rem 0 0.625rem;
 }
 
 .f-post-footer .f-post-right {
   text-align: right;
+  padding-top: 0;
+  padding-bottom: 0.625rem;
 }
 
 .f-post-footer .f-post-left {
@@ -1254,6 +1260,72 @@ li + li .f-btn {
 .f-post-signature {
   font-size: 0.875rem;
   opacity: 0.5;
+  padding-top: 0;
+}
+
+/*********************/
+.f-bb-u {
+  text-decoration: underline;
+}
+
+.f-bb-header {
+  font-weight: bold;
+  font-size: 1.25rem;
+}
+
+.f-bb-after {
+  opacity: 0.5;
+  font-style: italic;
+}
+
+.f-bb-quote {
+  border-left: 0.1875rem solid #AA7939;
+  border-top: 0.0625rem solid #AA7939;
+}
+
+.f-bb-q-header {
+  padding: 0.625rem;
+  background-color: #D3B58D;
+}
+
+.f-bb-q-body {
+  padding: 0.625rem;
+}
+
+.f-bb-code {
+  border: 0.0625rem solid #AA7939;
+  padding: 0.625rem;
+  background-color: #D3B58D;
+  overflow: auto;
+  max-height: 45rem;
+}
+
+.f-bb-l-disc {
+  list-style-type: disc;
+  list-style-position: outside;
+  padding-left: 1.5rem;
+}
+
+.f-bb-l-dec {
+  list-style-type: decimal;
+  list-style-position: outside;
+  padding-left: 1.5rem;
+}
+
+.f-bb-l-lat {
+  list-style-type: lower-alpha;
+  list-style-position: outside;
+  padding-left: 1.5rem;
+}
+
+.f-post-main p,
+.f-post-main blockquote,
+.f-post-main br,
+.f-post-main pre,
+.f-post-main ol,
+.f-post-main ul {
+  margin-bottom: 0.625rem;
+  word-wrap: break-word;
 }
 
 @media screen and (min-width: 50rem) {