2018-04-04

This commit is contained in:
Visman 2018-04-04 23:23:20 +07:00
parent 5b4a0efd5e
commit 2b7b77f191
9 changed files with 316 additions and 123 deletions

View file

@ -83,11 +83,14 @@ class Routing
$r->add('GET', '/user/{id:[2-9]|[1-9]\d+}/{name}', 'Profile:view', 'User');
$r->add(['GET', 'POST'], '/user/{id:[2-9]|[1-9]\d+}/edit/profile', 'Profile:edit', 'EditUserProfile');
$r->add(['GET', 'POST'], '/user/{id:[2-9]|[1-9]\d+}/edit/config', 'Profile:config', 'EditBoardConfig');
$r->add(['GET', 'POST'], '/user/{id:[2-9]|[1-9]\d+}/change/email', 'Profile:email', 'ChangeUserEmail');
} elseif (! $user->isGuest) {
// только свой профиль
$r->add('GET', '/user/{id:' . $user->id . '}/{name}', 'Profile:view', 'User');
$r->add(['GET', 'POST'], '/user/{id:' . $user->id . '}/edit/profile', 'Profile:edit', 'EditUserProfile');
$r->add(['GET', 'POST'], '/user/{id:' . $user->id . '}/edit/config', 'Profile:config', 'EditBoardConfig');
$r->add(['GET', 'POST'], '/user/{id:' . $user->id . '}/change/email', 'Profile:email', 'ChangeUserEmail');
}
// пометка разделов прочитанными
if (! $user->isGuest) {

View file

@ -7,26 +7,6 @@ use ForkBB\Models\Model;
trait PostValidatorTrait
{
/**
* Дополнительная проверка email
*
* @param Validator $v
* @param string $email
*
* @return string
*/
public function vCheckEmail(Validator $v, $email)
{
$user = $this->c->users->create();
$user->email = $email;
// email забанен
if ($this->c->bans->isBanned($user) > 0) {
$v->addError('Banned email');
}
return $email;
}
/**
* Дополнительная проверка subject
*
@ -170,7 +150,7 @@ trait PostValidatorTrait
$v = $this->c->Validator->reset()
->addValidators([
'check_email' => [$this, 'vCheckEmail'],
'check_email' => [$this->c->Validators, 'vCheckEmail'],
'check_username' => [$this->c->Validators, 'vCheckUsername'],
'check_subject' => [$this, 'vCheckSubject'],
'check_message' => [$this, 'vCheckMessage'],
@ -196,6 +176,7 @@ trait PostValidatorTrait
'token' => $args,
'subject.check_subject' => $executive,
'message.check_message' => $executive,
'email.check_email' => $this->user,
])->addMessages([
'username.login' => 'Login format',
]);

View file

@ -24,43 +24,6 @@ class Profile extends Page
return $this->view($args, $method, true);
}
/**
* Дополнительная проверка signature
*
* @param Validator $v
* @param string $signature
*
* @return string
*/
public function vCheckSignature(Validator $v, $signature)
{
if ('' != $signature) {
// после цензуры текст сообщения пустой
if (\ForkBB\cens($signature) == '') {
$v->addError('No signature after censoring');
// количество строк
} elseif (\substr_count($signature, "\n") >= $this->c->config->p_sig_lines) {
$v->addError('Signature has too many lines');
// текст сообщения только заглавными буквами
} elseif (! $this->c->user->isAdmin
&& '0' == $this->c->config->p_sig_all_caps
&& \preg_match('%\p{Lu}%u', $signature)
&& ! \preg_match('%\p{Ll}%u', $signature)
) {
$v->addError('All caps signature');
// проверка парсером
} else {
$signature = $this->c->Parser->prepare($signature, true); //????
foreach($this->c->Parser->getErrors() as $error) {
$v->addError($error);
}
}
}
return $signature;
}
/**
* Подготавливает данные для шаблона просмотра профиля
*
@ -78,10 +41,10 @@ class Profile extends Page
return $this->c->Message->message('Bad request');
}
$rules = $this->c->ProfileRules->setUser($this->curUser);
$this->rules = $this->c->ProfileRules->setUser($this->curUser);
if ($isEdit) {
if (! $rules->editProfile) {
if (! $this->rules->editProfile) {
return $this->c->Message->message('Bad request');
}
@ -91,19 +54,19 @@ class Profile extends Page
$this->c->Lang->load('profile');
if ($isEdit && 'POST' === $method) {
if ($rules->rename) {
if ($this->rules->rename) {
$ruleUsername = 'required|string:trim,spaces|min:2|max:25|login|check_username';
} else {
$ruleUsername = 'absent';
}
if ($rules->setTitle) {
if ($this->rules->setTitle) {
$ruleTitle = 'string:trim|max:50|no_url';
} else {
$ruleTitle = 'absent';
}
if ($rules->useAvatar) {
if ($this->rules->useAvatar) {
$ruleAvatar = "image|max:{$this->c->Files->maxImgSize('K')}";
$ruleDelAvatar = $this->curUser->avatar ? 'checkbox' : 'absent';
} else {
@ -117,13 +80,13 @@ class Profile extends Page
$ruleAdminNote = 'absent';
}
if ($rules->editWebsite) {
if ($this->rules->editWebsite) {
$ruleWebsite = 'string:trim|max:100'; // ???? валидация url?
} else {
$ruleWebsite = 'absent';
}
if ($rules->useSignature) {
if ($this->rules->useSignature) {
$ruleSignature = "string:trim|max:{$this->c->config->p_sig_length}|check_signature";
} else {
$ruleSignature = 'absent';
@ -198,72 +161,247 @@ class Profile extends Page
}
}
$crumbs = [];
if ($isEdit) {
$this->robots = 'noindex';
$this->crumbs = $this->crumbs(
[$this->c->Router->link('EditUserProfile', ['id' => $this->curUser->id]), \ForkBB\__('Editing profile')],
[$this->curUser->link, \ForkBB\__('User %s', $this->curUser->username)],
[$this->c->Router->link('Userlist'), \ForkBB\__('User list')]
);
$this->robots = 'noindex';
$crumbs[] = [$this->c->Router->link('EditUserProfile', ['id' => $this->curUser->id]), \ForkBB\__('Editing profile')];
} else {
$this->canonical = $this->curUser->link;
$this->crumbs = $this->crumbs(
[$this->curUser->link, \ForkBB\__('User %s', $this->curUser->username)],
[$this->c->Router->link('Userlist'), \ForkBB\__('User list')]
);
}
$this->fIndex = $rules->my ? 'profile' : 'userlist';
$this->nameTpl = 'profile';
$this->onlinePos = 'profile-' . $this->curUser->id; // ????
$this->title = \ForkBB\__('%s\'s profile', $this->curUser->username);
$this->form = $this->profileForm($isEdit, $rules);
$this->crumbs = $this->extCrumbs(...$crumbs);
$this->fIndex = $this->rules->my ? 'profile' : 'userlist';
$this->nameTpl = 'profile';
$this->onlinePos = 'profile-' . $this->curUser->id; // ????
$this->title = \ForkBB\__('%s\'s profile', $this->curUser->username);
$this->form = $this->profileForm($isEdit);
$this->actionBtns = $this->btns($isEdit ? 'edit' : 'view');
return $this;
}
/**
* Подготавливает данные для шаблона просмотра профиля
*
* @param array $args
* @param string $method
*
* @return Page
*/
public function email(array $args, $method)
{
$this->curUser = $this->c->users->load((int) $args['id']);
if (! $this->curUser instanceof User || ($this->curUser->isUnverified && ! $this->user->isAdmMod)) {
return $this->c->Message->message('Bad request');
}
$this->rules = $this->c->ProfileRules->setUser($this->curUser);
if (! $this->rules->editEmail) {
return $this->c->Message->message('Bad request');
}
$this->c->Lang->load('profile');
if ('POST' === $method) {
$v = $this->c->Validator->reset()
->addValidators([
'check_password' => [$this, 'vCheckPassword'],
'check_email' => [$this->c->Validators, 'vCheckEmail'],
])->addRules([
'token' => 'token:ChangeUserEmail',
'password' => 'required|string:trim|check_password',
'new_email' => 'required|string:trim,lower|email|check_email',
])->addAliases([
'new_email' => 'New email',
'password' => 'Your password',
])->addArguments([
'token' => ['id' => $this->curUser->id],
'new_email.check_email' => $this->curUser,
])->addMessages([
]);
if ($v->validation($_POST)) {
}
$this->fIswev = $v->getErrors();
}
$form = [
'action' => $this->c->Router->link('ChangeUserEmail', ['id' => $this->curUser->id]),
'hidden' => [
'token' => $this->c->Csrf->create('ChangeUserEmail', ['id' => $this->curUser->id]),
],
'sets' => [
[
'class' => 'data-edit',
'fields' => [
'new_email' => [
'id' => 'new_email',
'type' => 'text',
'maxlength' => 80,
'caption' => \ForkBB\__('New email'),
'required' => true,
'pattern' => '.+@.+',
'value' => isset($v->new_email) ? $v->new_email : $this->curUser->email,
],
'password' => [
'id' => 'password',
'type' => 'password',
'caption' => \ForkBB\__('Your password'),
'required' => true,
],
],
],
],
'btns' => [
'submit' => [
'type' => 'submit',
'value' => \ForkBB\__('Submit'),
'accesskey' => 's',
],
],
];
$this->robots = 'noindex';
$this->crumbs = $this->extCrumbs(
[$this->c->Router->link('ChangeUserEmail', ['id' => $this->curUser->id]), \ForkBB\__('Change email')]
);
$this->fIndex = $this->rules->my ? 'profile' : 'userlist';
$this->nameTpl = 'profile';
$this->onlinePos = 'profile-' . $this->curUser->id; // ????
$this->title = \ForkBB\__('%s\'s profile', $this->curUser->username);
$this->form = $form;
$this->actionBtns = $this->btns('edit');
return $this;
}
/**
* Дополнительная проверка signature
*
* @param Validator $v
* @param string $signature
*
* @return string
*/
public function vCheckSignature(Validator $v, $signature)
{
if ('' != $signature) {
// после цензуры текст сообщения пустой
if (\ForkBB\cens($signature) == '') {
$v->addError('No signature after censoring');
// количество строк
} elseif (\substr_count($signature, "\n") >= $this->c->config->p_sig_lines) {
$v->addError('Signature has too many lines');
// текст сообщения только заглавными буквами
} elseif (! $this->c->user->isAdmin
&& '0' == $this->c->config->p_sig_all_caps
&& \preg_match('%\p{Lu}%u', $signature)
&& ! \preg_match('%\p{Ll}%u', $signature)
) {
$v->addError('All caps signature');
// проверка парсером
} else {
$signature = $this->c->Parser->prepare($signature, true); //????
foreach($this->c->Parser->getErrors() as $error) {
$v->addError($error);
}
}
}
return $signature;
}
/**
* Проверяет пароль на совпадение с текущим пользователем
*
* @param Validator $v
* @param string $password
*
* @return string
*/
public function vCheckPassword(Validator $v, $password)
{
if (! \password_verify($password, $this->user->password)) {
$v->addError('Invalid password');
}
return $password;
}
/**
* Дополняет массив хлебных крошек
*
* @param mixed ...$args
*
* @return array
*/
protected function extCrumbs(...$args)
{
$args[] = [$this->curUser->link, \ForkBB\__('User %s', $this->curUser->username)];
$args[] = [$this->c->Router->link('Userlist'), \ForkBB\__('User list')];
return $this->crumbs(...$args);
}
/**
* Формирует массив кнопок
*
* @param string $type
*
* @return array
*/
protected function btns($type)
{
$btns = [];
if ($rules->banUser) {
if ($this->rules->banUser) {
$btns['ban-user'] = [
$this->c->Router->link('', ['id' => $this->curUser->id]),
\ForkBB\__('Ban user'),
];
}
if ($rules->deleteUser) {
if ($this->rules->deleteUser) {
$btns['delete-user'] = [
$this->c->Router->link('', ['id' => $this->curUser->id]),
\ForkBB\__('Delete user'),
];
}
if (! $isEdit && $rules->editProfile) {
if ('edit' != $type && $this->rules->editProfile) {
$btns['edit-profile'] = [
$this->c->Router->link('EditUserProfile', ['id' => $this->curUser->id]),
\ForkBB\__('Edit '),
];
}
if ($isEdit) {
if ('view' != $type) {
$btns['view-profile'] = [
$this->curUser->link,
\ForkBB\__('View '),
];
}
if ($rules->editConfig) {
if ('config' != $type && $this->rules->editConfig) {
$btns['edit-settings'] = [
$this->c->Router->link('EditBoardConfig', ['id' => $this->curUser->id]),
\ForkBB\__('Configure '),
];
}
$this->actionBtns = $btns;
return $this;
return $btns;
}
/**
* Создает массив данных для просмотра/редактирования профиля
*
* @param bool $isEdit
* @param Rules $rules
*
* @return array
*/
protected function profileForm($isEdit, $rules)
protected function profileForm($isEdit)
{
$clSuffix = $isEdit ? '-edit' : '';
@ -292,7 +430,7 @@ class Profile extends Page
'class' => 'usertitle',
'type' => 'wrap',
];
if ($isEdit && $rules->rename) {
if ($isEdit && $this->rules->rename) {
$fields['username'] = [
'id' => 'username',
'type' => 'text',
@ -311,7 +449,7 @@ class Profile extends Page
'value' => $this->curUser->username,
];
}
if ($isEdit && $rules->setTitle) {
if ($isEdit && $this->rules->setTitle) {
$fields['title'] = [
'id' => 'title',
'type' => 'text',
@ -332,7 +470,7 @@ class Profile extends Page
$fields[] = [
'type' => 'endwrap',
];
if ($rules->useAvatar) {
if ($this->rules->useAvatar) {
if ($isEdit && ! $this->curUser->avatar) {
$fields['avatar'] = [
'id' => 'avatar',
@ -355,6 +493,7 @@ class Profile extends Page
if ($this->curUser->avatar) {
$fields['delete_avatar'] = [
'id' => 'delete_avatar',
'type' => 'checkbox',
'label' => \ForkBB\__('Delete avatar'),
'value' => '1',
@ -482,7 +621,7 @@ class Profile extends Page
// контактная информация
$fields = [];
if ($rules->viewOEmail) {
if ($this->rules->viewOEmail) {
$fields['open-email'] = [
'id' => 'open-email',
'class' => 'pline',
@ -492,7 +631,7 @@ class Profile extends Page
'href' => 'mailto:' . $this->curUser->email,
];
}
if ($rules->viewEmail) {
if ($this->rules->viewEmail) {
if (0 === $this->curUser->email_setting) {
$fields['email'] = [
'id' => 'email',
@ -514,6 +653,14 @@ class Profile extends Page
}
}
if ($isEdit) {
if ($this->rules->editEmail) {
$fields[] = [
'id' => 'change_email',
'type' => 'link',
'value' => \ForkBB\__('To change email'),
'href' => $this->c->Router->link('ChangeUserEmail', ['id' => $this->curUser->id]),
];
}
$fields['email_setting'] = [
'id' => 'email_setting',
'class' => 'block',
@ -527,7 +674,7 @@ class Profile extends Page
'caption' => \ForkBB\__('Email settings label'),
];
}
if ($rules->editWebsite && $isEdit) {
if ($this->rules->editWebsite && $isEdit) {
$fields['url'] = [
'id' => 'website',
'type' => 'text',
@ -535,7 +682,7 @@ class Profile extends Page
'caption' => \ForkBB\__('Website'),
'value' => isset($v->url) ? $v->url : $this->curUser->url,
];
} elseif ($rules->viewWebsite && $this->curUser->url) {
} elseif ($this->rules->viewWebsite && $this->curUser->url) {
$fields['url'] = [
'id' => 'website',
'class' => 'pline',
@ -555,7 +702,7 @@ class Profile extends Page
}
// подпись
if ($rules->useSignature) {
if ($this->rules->useSignature) {
$fields = [];
if ($isEdit) {
$fields['signature'] = [
@ -592,7 +739,7 @@ class Profile extends Page
'value' => \ForkBB\dt($this->curUser->registered, true),
'caption' => \ForkBB\__('Registered info'),
];
if ($rules->viewLastVisit) {
if ($this->rules->viewLastVisit) {
$fields['lastvisit'] = [
'id' => 'lastvisit',
'class' => 'pline',
@ -645,7 +792,7 @@ class Profile extends Page
];
}
}
if ($rules->viewIP) {
if ($this->rules->viewIP) {
$fields['ip'] = [
'id' => 'ip',
'class' => 'pline',

View file

@ -20,7 +20,7 @@ class Register extends Page
$v = $this->c->Validator->reset()
->addValidators([
'check_email' => [$this, 'vCheckEmail'],
'check_email' => [$this->c->Validators, 'vCheckEmail'],
'check_username' => [$this->c->Validators, 'vCheckUsername'],
])->addRules([
'token' => 'token:RegisterForm',
@ -67,26 +67,6 @@ class Register extends Page
return $this;
}
/**
* Дополнительная проверка email
*
* @param Validator $v
* @param string $email
*
* @return string
*/
public function vCheckEmail(Validator $v, $email)
{
// email забанен
if ($this->c->bans->isBanned($this->c->users->create(['email' => $email])) > 0) {
$v->addError('Banned email');
// найден хотя бы 1 юзер с таким же email
} elseif (empty($v->getErrors()) && 0 !== $this->c->users->load($email, 'email')) {
$v->addError('Dupe email');
}
return $email;
}
/**
* Завершение регистрации
*

View file

@ -58,6 +58,11 @@ class Profile extends Rules
);
}
protected function geteditEmail()
{
return $this->admin || $this->my; // ???? разрешать ли модераторам менять email?
}
protected function getviewLastVisit()
{
return $this->my || $this->user->isAdmMod;

View file

@ -66,6 +66,43 @@ class Validators
return $username;
}
/**
* Дополнительная проверка email
* WARNING!!!
* Если передан гость 4-ым параметром, то проверка уникальности email не проводится
*
* @param Validator $v
* @param string $email
* @param string $z
* @param mixed $originalUser
*
* @return string
*/
public function vCheckEmail(Validator $v, $email, $z, $originalUser)
{
// email забанен
if ($this->c->bans->isBanned($this->c->users->create(['email' => $email])) > 0) {
$v->addError('Banned email');
// проверка email на уникальность
} elseif (empty($v->getErrors())) {
$id = null;
if ($originalUser instanceof User && ! $originalUser->isGuest) {
$id = $originalUser->id;
} elseif (! $originalUser instanceof User) {
$id = true;
}
if ($id) {
$user = $this->c->users->load($email, 'email');
if (($user instanceof User && $id !== $user->id) || (! $user instanceof User && 0 !== $user)) {
$v->addError('Dupe email');
}
}
}
return $email;
}
/**
* Дополнительная проверка на отсутствие url в значении
*

View file

@ -425,3 +425,21 @@ msgstr "IP"
msgid "IP title"
msgstr "IP"
msgid "No signature after censoring"
msgstr "Text of signature was deleted after censoring"
msgid "Signature has too many lines"
msgstr "Signature has too many lines"
msgid "All caps signature"
msgstr "Signature has only capital letters"
msgid "To change email"
msgstr "Change email address"
msgid "Your password"
msgstr "Your password"
msgid "Invalid password"
msgstr "Invalid password"

View file

@ -100,10 +100,10 @@ msgid "Email instructions"
msgstr "На новый почтовый адрес будет отправлено письмо со ссылкой для активации. Вы должны будете перейти по ссылке, указанной в письме, для завершения процедуры изменения параметров."
msgid "Change email"
msgstr "Изменение почтового адреса"
msgstr "Изменение адреса электронной почты"
msgid "New email"
msgstr "Новый почтовый адрес"
msgstr "Новый email"
msgid "Avatars disabled"
msgstr "Администратор выключил использование аватар."
@ -425,3 +425,21 @@ msgstr "IP"
msgid "IP title"
msgstr "IP"
msgid "No signature after censoring"
msgstr "Весь текст подписи удален после цензуры"
msgid "Signature has too many lines"
msgstr "В подписи слишком много строк"
msgid "All caps signature"
msgstr "Текст подписи содержит только заглавные буквы"
msgid "To change email"
msgstr "Изменить адрес электронной почты"
msgid "Your password"
msgstr "Ваш пароль"
msgid "Invalid password"
msgstr "Неверный пароль"

View file

@ -2168,6 +2168,10 @@ select {
background-color: #FFBABA;
}
#id-dl-delete_avatar .f-child2 {
font-size: 1rem;
}
@media screen and (min-width: 40rem) {
.f-fs-header-edit dl,
.f-fs-data-edit dl {