2017-12-03

This commit is contained in:
Visman 2017-12-03 22:50:06 +07:00
parent 4f7b2404f6
commit 66ac330784
17 changed files with 782 additions and 216 deletions

View file

@ -106,7 +106,7 @@ class Mail
$domainASCII = idn_to_ascii($domain);
if ($strict) {
$mx = dns_get_record($domainASCII, DNS_MX);
$mx = @dns_get_record($domainASCII, DNS_MX); //????
if (empty($mx)) {
return false;
}

View file

@ -30,8 +30,10 @@ class Parser extends Parserus
*/
protected function init()
{
$bbcodes = include $this->c->DIR_CONFIG . '/defaultBBCode.php';
$this->setBBCodes($bbcodes);
if ($this->c->config->p_message_bbcode == '1' || $this->c->config->p_sig_bbcode == '1') {
$bbcodes = include $this->c->DIR_CONFIG . '/defaultBBCode.php';
$this->setBBCodes($bbcodes);
}
if ($this->c->user->show_smilies == '1'
&& ($this->c->config->o_smilies_sig == '1' || $this->c->config->o_smilies == '1')

View file

@ -65,9 +65,9 @@ class Router
public function __construct($base)
{
$this->baseUrl = $base;
$this->host = parse_url($base, PHP_URL_HOST);
$this->prefix = parse_url($base, PHP_URL_PATH);
$this->length = strlen($this->prefix);
$this->host = parse_url($base, PHP_URL_HOST);
$this->prefix = parse_url($base, PHP_URL_PATH);
$this->length = strlen($this->prefix);
}
/**
@ -107,14 +107,23 @@ class Router
$s = $this->links[$marker];
foreach ($args as $key => $val) {
if ($key == '#') {
$s .= '#' . rawurlencode($val); //????
$s .= '#' . rawurlencode($val);
continue;
} elseif ($key == 'page' && $val === 1) {
continue;
}
$s = preg_replace(
'%\{' . preg_quote($key, '%') . '(?::[^{}]+)?\}%',
rawurlencode($val),
$s = preg_replace_callback( //????
'%\{' . preg_quote($key, '%') . '(?::[^{}]+)?\}%',
function($match) use ($val) {
if (is_string($val)) {
$val = trim(preg_replace('%[^\p{L}\p{N}_]+%u', '-', $val), '_-');
} elseif (is_numeric($val)) { //????
$val = (string) $val;
} else {
$val = null;
}
return isset($val[0]) ? rawurlencode($val) : '-';
},
$s
);
}

View file

@ -73,6 +73,18 @@ class Validator
*/
protected $raw;
/**
* Данные для текущей обработки
* @var array
*/
protected $curData = [];
/**
* Флаг ошибки
* @var mixed
*/
protected $error;
/**
* Конструктор
*
@ -208,9 +220,10 @@ class Validator
if (empty($this->rules)) {
throw new RuntimeException('Rules not found');
}
$this->errors = [];
$this->status = [];
$this->raw = $raw;
$this->errors = [];
$this->status = [];
$this->curData = [];
$this->raw = $raw;
foreach ($this->fields as $field) {
$this->__get($field);
}
@ -258,24 +271,68 @@ class Validator
}
}
$error = false;
foreach ($rules as $validator => $attr) {
$args = $this->getArguments($field, $validator);
list($value, $error) = $this->validators[$validator]($this, $value, $attr, $args);
if (false !== $error) {
// данные для обработчика ошибок
$this->error = null;
$this->curData[] = [
'field' => $field,
'rule' => $validator,
'attr' => $attr,
];
$value = $this->validators[$validator]($this, $value, $attr, $this->getArguments($field, $validator));
array_pop($this->curData);
if (null !== $this->error) {
break;
}
}
if (! is_bool($error)) {
$this->error($error, $field, $validator, $attr);
$this->status[$field] = false;
} else {
$this->status[$field] = true;
$this->status[$field] = true !== $this->error; // в $this->error может быть состояние false
$this->result[$field] = $value;
return $value;
}
/**
* Добавление ошибки
*
* @param mixed $error
* @param string $type
*
* @throws RuntimeException
*/
public function addError($error, $type = 'v')
{
if (empty($vars = end($this->curData))) {
throw new RuntimeException('The array of variables is empty');
}
$this->result[$field] = $value;
return $value;
// нет ошибки, для выхода из цикла проверки правил
if (true === $error) {
$this->error = false;
return;
}
extract($vars);
// псевдоним имени поля
$alias = isset($this->aliases[$field]) ? $this->aliases[$field] : $field;
// текст ошибки
if (isset($this->messages[$field . '.' . $rule])) {
$error = $this->messages[$field . '.' . $rule];
} elseif (isset($this->messages[$field])) {
$error = $this->messages[$field];
}
if (is_array($error)) {
$type = $error[1];
$error = $error[0];
}
$this->errors[$type][] = __($error, [':alias' => $alias, ':attr' => $attr]);
$this->error = true;
}
/**
@ -297,33 +354,6 @@ class Validator
}
}
/**
* Обработка ошибки
*
* @param mixed $error
* @param string $field
* @param string $rule
* @param string $attr
*/
protected function error($error, $field, $rule, $attr)
{
// псевдоним имени поля
$alias = isset($this->aliases[$field]) ? $this->aliases[$field] : $field;
// текст ошибки
if (isset($this->messages[$field . '.' . $rule])) {
$error = $this->messages[$field . '.' . $rule];
} elseif (isset($this->messages[$field])) {
$error = $this->messages[$field];
}
$type = 'v';
// ошибка содержит тип
if (is_array($error)) {
$type = $error[1];
$error = $error[0];
}
$this->errors[$type][] = __($error, [':alias' => $alias, ':attr' => $attr]);
}
/**
* Возвращает статус проверки поля
*
@ -367,48 +397,53 @@ class Validator
protected function vAbsent($v, $value)
{
if (null === $value) {
return [$value, false];
} else {
return [null, 'The :alias should be absent'];
if (null !== $value) {
$this->addError('The :alias should be absent');
}
return null;
}
protected function vRequired($v, $value)
{
if (is_string($value)) {
if (strlen(preg_replace('%^\s+|\s+$%u', '', $value)) > 0) {
return [$value, false];
return $value;
}
} elseif (is_array($value)) {
if (! empty($value)) {
return [$value, false];
return $value;
}
} elseif (null !== $value) {
return [$value, false];
return $value;
}
return [null, 'The :alias is required'];
$this->addError('The :alias is required');
return null;
}
protected function vRequiredWith($v, $value, $attr)
protected function vRequiredWith($v, $value, $attr) //???????????????????????
{
foreach (explode(',', $attr) as $field) {
if (null !== $this->__get($field)) {
return $this->vRequired($v, $value);
}
if (null !== $this->__get($field)) { // если есть хотя бы одно поле,
return $this->vRequired($v, $value); // то проверяем данное поле
} // на обязательное наличие
}
list(, $error) = $this->vRequired($v, $value);
if (false === $error) {
return [null, 'The :alias is not required'];
} else {
return [$value, true];
if (null === $value) { // если данное поле отсутствует,
$this->addError(true); // то прерываем его проверку
}
return $value;
# list(, $error) = $this->vRequired($v, $value);
# if (false === $error) {
# return [null, 'The :alias is not required'];
# } else {
# return [$value, true];
# }
}
protected function vString($v, $value, $attr)
{
if (null === $value) {
return [null, false];
return null;
} elseif (is_string($value)) {
foreach(explode(',', $attr) as $action) {
switch ($action) {
@ -423,83 +458,87 @@ class Validator
break;
}
}
return [$value, false];
return $value;
} else {
return [null, 'The :alias must be string'];
$this->addError('The :alias must be string');
return null;
}
}
protected function vNumeric($v, $value)
{
if (null === $value) {
return [null, false];
return null;
} elseif (is_numeric($value)) {
return [0 + $value, false];
return 0 + $value;
} else {
return [null, 'The :alias must be numeric'];
$this->addError('The :alias must be numeric');
return null;
}
}
protected function vInteger($v, $value)
{
if (null === $value) {
return [null, false];
return null;
} elseif (is_numeric($value) && is_int(0 + $value)) {
return [(int) $value, false];
return (int) $value;
} else {
return [null, 'The :alias must be integer'];
$this->addError('The :alias must be integer');
return null;
}
}
protected function vArray($v, $value)
{
if (null === $value) {
return [null, false];
} elseif (is_array($value)) {
return [$value, false];
if (null !== $value && ! is_array($value)) {
$this->addError('The :alias must be array');
return null;
} else {
return [null, 'The :alias must be array'];
return $value;
}
}
protected function vMin($v, $value, $attr)
{
if (is_numeric($value)) {
if (0 + $value < $attr) {
return [$value, 'The :alias minimum is :attr'];
}
} elseif (is_string($value)) {
if (is_string($value)) {
if (mb_strlen($value, 'UTF-8') < $attr) {
return [$value, 'The :alias minimum is :attr characters'];
$this->addError('The :alias minimum is :attr characters');
}
} elseif (is_numeric($value)) {
if (0 + $value < $attr) {
$this->addError('The :alias minimum is :attr');
}
} elseif (is_array($value)) {
if (count($value) < $attr) {
return [$value, 'The :alias minimum is :attr elements'];
$this->addError('The :alias minimum is :attr elements');
}
} else {
return [null, null === $value ? false : 'The :alias minimum is :attr'];
} elseif (null !== $value) {
$this->addError('The :alias minimum is :attr');
return null;
}
return [$value, false];
return $value;
}
protected function vMax($v, $value, $attr)
{
if (is_numeric($value)) {
if (0 + $value > $attr) {
return [$value, 'The :alias maximum is :attr'];
}
} elseif (is_string($value)) {
if (is_string($value)) {
if (mb_strlen($value, 'UTF-8') > $attr) {
return [$value, 'The :alias maximum is :attr characters'];
$this->addError('The :alias maximum is :attr characters');
}
} elseif (is_numeric($value)) {
if (0 + $value > $attr) {
$this->addError('The :alias maximum is :attr');
}
} elseif (is_array($value)) {
if (count($value) > $attr) {
return [$value, 'The :alias maximum is :attr elements'];
$this->addError('The :alias maximum is :attr elements');
}
} else {
return [null, null === $value ? false : 'The :alias maximum is :attr'];
} elseif (null !== $value) {
$this->addError('The :alias maximum is :attr'); //????
return null;
}
return [$value, false];
return $value;
}
protected function vToken($v, $value, $attr, $args)
@ -507,17 +546,17 @@ class Validator
if (! is_array($args)) {
$args = [];
}
$value = (string) $value;
if ($this->c->Csrf->verify($value, $attr, $args)) {
return [$value, false];
if (! is_string($value) || ! $this->c->Csrf->verify($value, $attr, $args)) {
$this->addError('Bad token', 'e');
return null;
} else {
return [$value, ['Bad token', 'e']];
return $value;
}
}
protected function vCheckbox($v, $value)
{
return [! empty($value), false];
return ! empty($value);
}
protected function vReferer($v, $value, $attr, $args)
@ -525,39 +564,42 @@ class Validator
if (! is_array($args)) {
$args = [];
}
return [$this->c->Router->validate($value, $attr, $args), false];
return $this->c->Router->validate($value, $attr, $args);
}
protected function vEmail($v, $value)
{
if (null === $value) {
return [$value, false];
return null;
}
$email = $this->c->Mail->valid($value, true);
if (false === $email) {
return [(string) $value, 'The :alias is not valid email'];
$this->addError('The :alias is not valid email');
return $value;
} else {
return [$email, false];
return $email;
}
}
protected function vSame($v, $value, $attr)
{
if (! $this->getStatus($attr) || $value === $this->__get($attr)) {
return [$value, false];
return $value;
} else {
return [null, 'The :alias must be same with original'];
$this->addError('The :alias must be same with original');
return null;
}
}
protected function vRegex($v, $value, $attr)
{
if (null === $value) {
return [$value, false];
} elseif (is_string($value) && preg_match($attr, $value)) {
return [$value, false];
if (null !== $value
&& (! is_string($value) || ! preg_match($attr, $value))
) {
$this->addError('The :alias is not valid format');
return null;
} else {
return [null, 'The :alias is not valid format'];
return $value;
}
}
@ -573,19 +615,17 @@ class Validator
protected function vIn($v, $value, $attr)
{
if (null === $value || in_array($value, explode(',', $attr))) {
return [$value, false];
} else {
return [$value, 'The :alias contains an invalid value'];
if (null !== $value && ! in_array($value, explode(',', $attr))) {
$this->addError('The :alias contains an invalid value');
}
return $value;
}
protected function vNotIn($v, $value, $attr)
{
if (null === $value || ! in_array($value, explode(',', $attr))) {
return [$value, false];
} else {
return [$value, 'The :alias contains an invalid value'];
if (null !== $value && in_array($value, explode(',', $attr))) {
$this->addError('The :alias contains an invalid value');
}
return $value;
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace ForkBB\Models\Forum;
use ForkBB\Models\MethodModel;
use RuntimeException;
class CalcStat extends MethodModel
{
/**
* Пересчитывает статистику
*
* @throws RuntimeException
*
* @return Forum
*/
public function calcStat()
{
if ($this->model->id < 1) {
throw new RuntimeException('The model does not have ID');
}
$vars = [':fid' => $this->model->id];
$sql = 'SELECT COUNT(id) as num_topics, SUM(num_replies) as num_replies
FROM ::topics
WHERE forum_id=?i:fid';
$result = $this->c->DB->query($sql, $vars)->fetch();
$this->model->num_topics = $result['num_topics'];
$this->model->num_posts = $result['num_topics'] + $result['num_replies'];
$sql = 'SELECT last_post, last_post_id, last_poster, subject as last_topic
FROM ::topics
WHERE forum_id=?i:fid AND moved_to IS NULL
ORDER BY last_post DESC
LIMIT 1';
$result = $this->c->DB->query($sql, $vars)->fetch();
if (empty($result)) {
$this->model->last_post = null;
$this->model->last_post_id = null;
$this->model->last_poster = null;
$this->model->last_topic = null;
} else {
$this->model->last_post = $result['last_post'];
$this->model->last_post_id = $result['last_post_id'];
$this->model->last_poster = $result['last_poster'];
$this->model->last_topic = $result['last_topic'];
}
return $this->model;
}
}

79
app/Models/Forum/Save.php Normal file
View file

@ -0,0 +1,79 @@
<?php
namespace ForkBB\Models\Forum;
use ForkBB\Models\MethodModel;
use RuntimeException;
class Save extends MethodModel
{
/**
* Обновляет данные пользователя
*
* @throws RuntimeException
*
* @return Forum
*/
public function update()
{
if (empty($this->model->id)) {
throw new RuntimeException('The model does not have ID');
}
$modified = $this->model->getModified();
if (empty($modified)) {
return $this->model;
}
$values = $this->model->getAttrs();
$fileds = $this->c->dbMap->forums;
$set = $vars = [];
foreach ($modified as $name) {
if (! isset($fileds[$name])) {
continue;
}
$vars[] = $values[$name];
$set[] = $name . '=?' . $fileds[$name];
}
if (empty($set)) {
return $this->model;
}
$vars[] = $this->model->id;
$this->c->DB->query('UPDATE ::forums SET ' . implode(', ', $set) . ' WHERE id=?i', $vars);
$this->model->resModified();
return $this->model;
}
/**
* Добавляет новую запись в таблицу пользователей
*
* @throws RuntimeException
*
* @return int
*/
public function insert()
{
$modified = $this->model->getModified();
if (null !== $this->model->id || in_array('id', $modified)) {
throw new RuntimeException('The model has ID');
}
$values = $this->model->getAttrs();
$fileds = $this->c->dbMap->forums;
$set = $set2 = $vars = [];
foreach ($modified as $name) {
if (! isset($fileds[$name])) {
continue;
}
$vars[] = $values[$name];
$set[] = $name;
$set2[] = '?' . $fileds[$name];
}
if (empty($set)) {
throw new RuntimeException('The model is empty');
}
$this->c->DB->query('INSERT INTO ::forums (' . implode(', ', $set) . ') VALUES (' . implode(', ', $set2) . ')', $vars);
$this->model->id = $this->c->DB->lastInsertId();
$this->model->resModified();
return $this->model->id;
}
}

View file

@ -112,12 +112,11 @@ class Auth extends Page
*/
public function vLoginProcess(Validator $v, $password)
{
$error = false;
if (! empty($v->getErrors())) {
} elseif (! ($user = $this->c->ModelUser->load($v->username, 'username')) instanceof User) {
$error = __('Wrong user/pass');
$v->addError('Wrong user/pass');
} elseif ($user->isUnverified) {
$error = [__('Account is not activated'), 'w'];
$v->addError('Account is not activated', 'w');
} else {
$authorized = false;
$hash = $user->password;
@ -133,7 +132,7 @@ class Auth extends Page
}
// ошибка в пароле
if (! $authorized) {
$error = __('Wrong user/pass');
$v->addError('Wrong user/pass');
} else {
// перезаписываем ip админа и модератора - Visman
if ($user->isAdmMod
@ -149,7 +148,7 @@ class Auth extends Page
$this->c->Cookie->setUser($user);
}
}
return [$password, $error];
return $password;
}
/**
@ -248,26 +247,25 @@ class Auth extends Page
public function vCheckEmail(Validator $v, $email)
{
if (! empty($v->getErrors())) {
return [$email, false];
return $email;
}
$error = false;
$user = $this->c->ModelUser;
$user->__email = $email;
// email забанен
if ($this->c->bans->isBanned($user) > 0) {
$error = __('Banned email');
$v->addError('Banned email');
// нет пользователя с таким email
} elseif (! $user->load($email, 'email') instanceof User) {
$error = __('Invalid email');
$v->addError('Invalid email');
// за последний час уже был запрос на этот email
} elseif (! empty($user->last_email_sent) && time() - $user->last_email_sent < 3600) {
$error = [__('Email flood', (int) (($user->last_email_sent + 3600 - time()) / 60)), 'e'];
$v->addError(__('Email flood', (int) (($user->last_email_sent + 3600 - time()) / 60)), 'e');
} else {
$this->tmpUser = $user;
}
return [$email, $error];
return $email;
}
/**

View file

@ -3,6 +3,7 @@
namespace ForkBB\Models\Pages;
use ForkBB\Core\Validator;
use ForkBB\Models\Model;
use ForkBB\Models\Page;
class Post extends Page
@ -21,16 +22,7 @@ class Post extends Page
$forum = $this->c->forums->forum($args['id']);
// раздел отсутствует в доступных или является ссылкой
if (empty($forum) || $forum->redirect_url) {
return $this->c->Message->message('Bad request');
}
$user = $this->c->user;
if (! $user->isAdmin
&& (null === $forum->post_topics && $user->g_post_topics == '0' || $forum->post_topics == '0')
&& ! $user->isModerator($forum)
) {
if (empty($forum) || $forum->redirect_url || ! $forum->canCreateTopic) {
return $this->c->Message->message('Bad request');
}
@ -42,53 +34,87 @@ class Post extends Page
$this->robots = 'noindex';
$this->crumbs = $this->crumbs(__('Post new topic'), $forum);
$this->form = $this->messageForm($forum, 'NewTopic', $args, true);
$this->titleForm = __('Post new topic');
return $this;
}
public function newTopicPost(array $args)
{
$forum = $this->c->forums->forum($args['id']);
// раздел отсутствует в доступных или является ссылкой
if (empty($forum) || $forum->redirect_url || ! $forum->canCreateTopic) {
return $this->c->Message->message('Bad request');
}
$this->c->Lang->load('post');
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';
}
$v = $this->c->Validator->addValidators([
'check_email' => [$this, 'vCheckEmail'],
'check_username' => [$this, 'vCheckUsername'],
'check_subject' => [$this, 'vCheckSubject'],
])->setRules([
'token' => 'token:NewTopic',
'message' => 'required|string:trim|max:65536',
'email' => [$ruleEmail, __('Email')],
'username' => [$ruleUsername, __('Username')],
'subject' => ['required|string:trim,spaces|min:1|max:70|check_subject', __('Subject')],
])->setArguments([
'token' => $args,
])->setMessages([
'username.login' => __('Login format'),
]);
$v = $this->messageValidator($forum, 'NewTopic', $args, true);
if (! $v->validation($_POST)) {
if (! $v->validation($_POST) || isset($v->preview)) {
$this->fIswev = $v->getErrors();
$args['_vars'] = $v->getData();
return $this->newTopic($args);
}
$now = time();
$poster = $v->username ?: $this->c->user->username;
exit('ok');
// создание темы
$topic = $this->c->ModelTopic;
$topic->subject = $v->subject;
$topic->poster = $poster;
$topic->last_poster = $poster;
$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();
// создание сообщения
$post = $this->c->ModelPost;
$post->poster = $poster;
$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();
// обновление созданной темы
$topic->forum_id = $forum->id; //????
$topic->first_post_id = $post->id;
$topic->last_post_id = $post->id;
$topic->update();
$forum->calcStat()->update();
return $this->c->Redirect
->page('Topic', ['id' => $topic->id, 'name' => $topic->cens()->subject])
->message(__('Post redirect'));
}
public function newReply(array $args)
{
$topic = $this->c->ModelTopic->load($args['id']); //????
$topic = $this->c->ModelTopic->load((int) $args['id']);
if (empty($topic->id) || $topic->moved_to || ! $topic->canReply) { //????
if (empty($topic) || $topic->moved_to || ! $topic->canReply) {
return $this->c->Message->message('Bad request');
}
@ -100,13 +126,31 @@ class Post extends Page
$this->robots = 'noindex';
$this->crumbs = $this->crumbs(__('Post a reply'), $topic);
$this->form = $this->messageForm($topic, 'NewReply', $args);
$this->titleForm = __('Post a reply');
return $this;
}
public function newReplyPost(array $args)
{
$topic = $this->c->ModelTopic->load((int) $args['id']);
if (empty($topic) || $topic->moved_to || ! $topic->canReply) {
return $this->c->Message->message('Bad request');
}
$this->c->Lang->load('post');
$v = $this->messageValidator($topic, 'NewReply', $args);
if (! $v->validation($_POST) || isset($v->preview)) {
$this->fIswev = $v->getErrors();
$args['_vars'] = $v->getData();
return $this->newReply($args);
}
exit('ok');
}
/**
@ -119,15 +163,14 @@ class Post extends Page
*/
public function vCheckEmail(Validator $v, $email)
{
$error = false;
$user = $this->c->ModelUser;
$user->__email = $email;
// email забанен
if ($this->c->bans->isBanned($user) > 0) {
$error = __('Banned email');
$v->addError('Banned email');
}
return [$email, $error];
return $email;
}
/**
@ -140,43 +183,167 @@ class Post extends Page
*/
public function vCheckUsername(Validator $v, $username)
{
$error = false;
$user = $this->c->ModelUser;
$user->__username = $username;
// username = Гость
if (preg_match('%^(guest|' . preg_quote(__('Guest'), '%') . ')$%iu', $username)) {
$error = __('Username guest');
$v->addError('Username guest');
// цензура
} elseif ($user->cens()->$username !== $username) {
$error = __('Username censor');
$v->addError('Username censor');
// username забанен
} elseif ($this->c->bans->isBanned($user) > 0) {
$error = __('Banned username');
$v->addError('Banned username');
}
return [$username, $error];
return $username;
}
/**
* Дополнительная проверка subject
*
* @param Validator $v
* @param string $username
* @param string $subject
*
* @return array
*/
public function vCheckSubject(Validator $v, $subject)
{
$error = false;
if ($this->c->censorship->censor($subject) == '') {
$error = __('No subject after censoring');
} elseif ($this->c->config->p_subject_all_caps == '0'
&& mb_strtolower($subject, 'UTF-8') !== $subject
&& mb_strtoupper($subject, 'UTF-8') === $subject
$v->addError('No subject after censoring');
} elseif (! $this->tmpAdmMod
&& $this->c->config->p_subject_all_caps == '0'
&& preg_match('%\p{Lu}%u', $subject)
&& ! preg_match('%\p{Ll}%u', $subject)
) {
$error = __('All caps subject');
$v->addError('All caps subject');
}
return [$subject, $error];
return $subject;
}
/**
* Дополнительная проверка message
*
* @param Validator $v
* @param string $message
*
* @return array
*/
public function vCheckMessage(Validator $v, $message)
{
if ($this->c->censorship->censor($message) == '') {
$v->addError('No message after censoring');
} elseif (! $this->tmpAdmMod
&& $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 {
$bbWList = $this->c->config->p_message_bbcode == '1' ? null : [];
$bbBList = $this->c->config->p_message_img_tag == '1' ? [] : ['img'];
$this->c->Parser->setAttr('isSign', false)
->setWhiteList($bbWList)
->setBlackList($bbBList)
->parse($message, ['strict' => true])
->stripEmptyTags(" \n\t\r\v", true);
if ($this->c->config->o_make_links == '1') {
$this->c->Parser->detectUrls();
}
if ($v->hide_smilies != '1' && $this->c->config->o_smilies == '1') {
$this->c->Parser->detectSmilies();
}
$errors = $this->c->Parser->getErrors();
if ($errors) {
foreach($errors as $error) {
$v->addError($error);
}
} else {
$this->parser = $this->c->Parser;
}
}
return $message;
}
/**
* Проверка данных поступивших из формы сообщения
*
* @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)) {
$this->tmpAdmMod = true;
$ruleStickTopic = 'checkbox';
if ($editSubject) {
$ruleStickFP = 'checkbox';
$ruleMergePost = 'absent';
} else {
$ruleStickFP = 'absent';
$ruleMergePost = 'checkbox';
}
} else {
$ruleStickTopic = 'absent';
$ruleStickFP = 'absent';
$ruleMergePost = 'absent';
}
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'],
])->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,
'submit' => 'string', //????
'preview' => 'string', //????
'message' => 'required|string:trim|max:65536|check_message',
])->setArguments([
'token' => $args,
])->setMessages([
'username.login' => __('Login format'),
]);
return $v;
}
/**
@ -189,7 +356,7 @@ class Post extends Page
*
* @return array
*/
protected function messageForm($model, $marker, array $args, $editSubject = false)
protected function messageForm(Model $model, $marker, array $args, $editSubject = false)
{
$vars = isset($args['_vars']) ? $args['_vars'] : null;
unset($args['_vars']);

View file

@ -72,18 +72,17 @@ class Register extends Page
*/
public function vCheckEmail(Validator $v, $email)
{
$error = false;
$user = $this->c->ModelUser;
$user->__email = $email;
// email забанен
if ($this->c->bans->isBanned($user) > 0) {
$error = __('Banned email');
$v->addError('Banned email');
// найден хотя бы 1 юзер с таким же email
} elseif (empty($v->getErrors()) && $user->load($email, 'email') !== 0) {
$error = __('Dupe email');
$v->addError('Dupe email');
}
return [$email, $error];
return $email;
}
/**
@ -96,24 +95,23 @@ class Register extends Page
*/
public function vCheckUsername(Validator $v, $username)
{
$error = false;
$user = $this->c->ModelUser;
$user->__username = $username;
// username = Гость
if (preg_match('%^(guest|' . preg_quote(__('Guest'), '%') . ')$%iu', $username)) {
$error = __('Username guest');
$v->addError('Username guest');
// цензура
} elseif ($this->c->censorship->censor($username) !== $username) {
$error = __('Username censor');
$v->addError('Username censor');
// username забанен
} elseif ($this->c->bans->isBanned($user) > 0) {
$error = __('Banned username');
$v->addError('Banned username');
// есть пользователь с похожим именем
} elseif (empty($v->getErrors()) && ! $user->isUnique()) {
$error = __('Username not unique');
$v->addError('Username not unique');
}
return [$username, $error];
return $username;
}
/**
@ -134,6 +132,7 @@ class Register extends Page
}
$user = $this->c->ModelUser;
$user->username = $v->username;
$user->password = password_hash($v->password, PASSWORD_DEFAULT);
$user->group_id = $groupId;

View file

@ -106,7 +106,7 @@ class Topic extends Page
{
$topic = $this->c->ModelTopic->load((int) $args['id'], $type === 'post');
if (! $topic->id || ! $topic->last_post_id) {
if (empty($topic) || ! $topic->last_post_id) {
return $this->c->Message->message('Bad request');
}
@ -189,7 +189,7 @@ class Topic extends Page
$fieldset = [];
if ($user->isAdmin || $user->isModerator($topic)) {
$fieldset['merge'] = [
$fieldset['merge_post'] = [
'type' => 'checkbox',
'label' => __('Merge posts'),
'value' => '1',

79
app/Models/Post/Save.php Normal file
View file

@ -0,0 +1,79 @@
<?php
namespace ForkBB\Models\Post;
use ForkBB\Models\MethodModel;
use RuntimeException;
class Save extends MethodModel
{
/**
* Обновляет данные пользователя
*
* @throws RuntimeException
*
* @return Post
*/
public function update()
{
if (empty($this->model->id)) {
throw new RuntimeException('The model does not have ID');
}
$modified = $this->model->getModified();
if (empty($modified)) {
return $this->model;
}
$values = $this->model->getAttrs();
$fileds = $this->c->dbMap->posts;
$set = $vars = [];
foreach ($modified as $name) {
if (! isset($fileds[$name])) {
continue;
}
$vars[] = $values[$name];
$set[] = $name . '=?' . $fileds[$name];
}
if (empty($set)) {
return $this->model;
}
$vars[] = $this->model->id;
$this->c->DB->query('UPDATE ::posts SET ' . implode(', ', $set) . ' WHERE id=?i', $vars);
$this->model->resModified();
return $this->model;
}
/**
* Добавляет новую запись в таблицу пользователей
*
* @throws RuntimeException
*
* @return int
*/
public function insert()
{
$modified = $this->model->getModified();
if (null !== $this->model->id || in_array('id', $modified)) {
throw new RuntimeException('The model has ID');
}
$values = $this->model->getAttrs();
$fileds = $this->c->dbMap->posts;
$set = $set2 = $vars = [];
foreach ($modified as $name) {
if (! isset($fileds[$name])) {
continue;
}
$vars[] = $values[$name];
$set[] = $name;
$set2[] = '?' . $fileds[$name];
}
if (empty($set)) {
throw new RuntimeException('The model is empty');
}
$this->c->DB->query('INSERT INTO ::posts (' . implode(', ', $set) . ') VALUES (' . implode(', ', $set2) . ')', $vars);
$this->model->id = $this->c->DB->lastInsertId();
$this->model->resModified();
return $this->model->id;
}
}

View file

@ -12,7 +12,7 @@ class Load extends MethodModel
* @param int $id
* @param bool $isPost
*
* @return Topic
* @return null|Topic
*/
public function load($id, $isPost = false)
{
@ -60,7 +60,7 @@ class Load extends MethodModel
// тема отсутствует или недоступна
if (empty($data)) {
return $this->model->setAttrs([]);
return null;
}
if (! $this->c->user->isGuest) {
@ -72,7 +72,7 @@ class Load extends MethodModel
// раздел недоступен
if (empty($forum)) {
return $this->model->setAttrs([]);
return null;
}
if (! empty($forForum)) {

79
app/Models/Topic/Save.php Normal file
View file

@ -0,0 +1,79 @@
<?php
namespace ForkBB\Models\Topic;
use ForkBB\Models\MethodModel;
use RuntimeException;
class Save extends MethodModel
{
/**
* Обновляет данные пользователя
*
* @throws RuntimeException
*
* @return Topic
*/
public function update()
{
if (empty($this->model->id)) {
throw new RuntimeException('The model does not have ID');
}
$modified = $this->model->getModified();
if (empty($modified)) {
return $this->model;
}
$values = $this->model->getAttrs();
$fileds = $this->c->dbMap->topics;
$set = $vars = [];
foreach ($modified as $name) {
if (! isset($fileds[$name])) {
continue;
}
$vars[] = $values[$name];
$set[] = $name . '=?' . $fileds[$name];
}
if (empty($set)) {
return $this->model;
}
$vars[] = $this->model->id;
$this->c->DB->query('UPDATE ::topics SET ' . implode(', ', $set) . ' WHERE id=?i', $vars);
$this->model->resModified();
return $this->model;
}
/**
* Добавляет новую запись в таблицу пользователей
*
* @throws RuntimeException
*
* @return int
*/
public function insert()
{
$modified = $this->model->getModified();
if (null !== $this->model->id || in_array('id', $modified)) {
throw new RuntimeException('The model has ID');
}
$values = $this->model->getAttrs();
$fileds = $this->c->dbMap->topics;
$set = $set2 = $vars = [];
foreach ($modified as $name) {
if (! isset($fileds[$name])) {
continue;
}
$vars[] = $values[$name];
$set[] = $name;
$set2[] = '?' . $fileds[$name];
}
if (empty($set)) {
throw new RuntimeException('The model is empty');
}
$this->c->DB->query('INSERT INTO ::topics (' . implode(', ', $set) . ') VALUES (' . implode(', ', $set2) . ')', $vars);
$this->model->id = $this->c->DB->lastInsertId();
$this->model->resModified();
return $this->model->id;
}
}

View file

@ -88,6 +88,7 @@ class LoadUserFromCookie extends MethodModel
}
$this->model->setAttrs($data);
$this->model->__ip = $ip;
$this->model->__userAgent = $this->getUserAgent();
}
/**
@ -100,6 +101,17 @@ class LoadUserFromCookie extends MethodModel
return filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP) ?: 'unknow';
}
/**
* Возврат юзер агента браузера пользователя
*
* @return string
*/
protected function getUserAgent()
{
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
return is_string($ua) ? trim($ua) : '';
}
/**
* Проверка на робота
* Если робот, то возврат имени
@ -108,7 +120,7 @@ class LoadUserFromCookie extends MethodModel
*/
protected function isBot()
{
$agent = trim($_SERVER['HTTP_USER_AGENT']);
$agent = $this->getUserAgent();
if ($agent == '') {
return false;
}

View file

@ -11,15 +11,17 @@ class Save extends MethodModel
* Обновляет данные пользователя
*
* @throws RuntimeException
*
* @return User
*/
public function save()
public function update()
{
if (empty($this->model->id)) {
throw new RuntimeException('The model does not have ID');
}
$modified = $this->model->getModified();
if (empty($modified)) {
return;
return $this->model;
}
$values = $this->model->getAttrs();
$fileds = $this->c->dbMap->users;
@ -32,17 +34,21 @@ class Save extends MethodModel
$set[] = $name . '=?' . $fileds[$name];
}
if (empty($set)) {
return;
return $this->model;
}
$vars[] = $this->model->id;
$this->c->DB->query('UPDATE ::users SET ' . implode(', ', $set) . ' WHERE id=?i', $vars);
$this->model->resModified();
return $this->model;
}
/**
* Добавляет новую запись в таблицу пользователей
*
* @throws RuntimeException
*
* @return int
*/
public function insert()
{
@ -62,10 +68,12 @@ class Save extends MethodModel
$set2[] = '?' . $fileds[$name];
}
if (empty($set)) {
return;
throw new RuntimeException('The model is empty');
}
$this->c->DB->query('INSERT INTO ::users (' . implode(', ', $set) . ') VALUES (' . implode(', ', $set2) . ')', $vars);
$this->model->id = $this->c->DB->lastInsertId();
$this->model->resModified();
return $this->c->DB->lastInsertId();
return $this->model->id;
}
}

View file

@ -15,12 +15,19 @@
<div class="f-nav-links">
@yield ('crumbs')
</div>
<section class="f-main f-topic">
<h2>{{ '' }}</h2>
@if ($p->parser)
<section class="f-main f-preview">
<h2>{!! __('Post preview') !!}</h2>
<div class="f-post-body clearfix">
<div class="f-post-right f-post-main">
{!! $p->parser->getHtml() !!}
</div>
</div>
</section>
@endif
@if ($form = $p->form)
<section class="post-form">
<h2>{!! __('Quick post') !!}</h2>
<h2>{!! $p->titleForm !!}</h2>
<div class="f-fdiv">
@include ('layouts/form')
</div>

View file

@ -928,6 +928,7 @@ select {
.f-ftlist .f-cell {
padding-top: 0.625rem;
padding-bottom: 0.625rem;
overflow: hidden;
}
.f-ftlist .f-hcell {
@ -1285,13 +1286,44 @@ li + li .f-btn {
}
} /* @media screen and (min-width: 50rem) */
/****************/
/* Предпросмотр */
/****************/
.f-preview {
border-top: 0.0625rem solid #AA7939;
}
.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;
}
.f-preview .f-post-body {
float: none;
margin: 0;
}
.f-preview .f-post-right {
padding: 0.625rem;
}
/*****************************/
.post-form {
border-top: 0.0625rem solid #AA7939;
}
.post-form > h2 {
display: none;
/* 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;
}
.post-form > .f-fdiv {