2018-04-15

This commit is contained in:
Visman 2018-04-15 15:45:42 +07:00
parent 1f25a27879
commit 461a764d62
10 changed files with 1362 additions and 1222 deletions

View file

@ -80,22 +80,22 @@ class Routing
$r->add('GET', '/userlist[/{sort:username|registered|num_posts}/{dir:ASC|DESC}/{group:\-1|[1-9]\d*}/{name}][/{page:[1-9]\d*}]', 'Userlist:view', 'Userlist');
$r->add('POST', '/userlist', 'Userlist:view');
// юзеры
$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+}/edit/email', 'Profile:email', 'EditUserEmail');
$r->add(['GET', 'POST'], '/user/{id:[2-9]|[1-9]\d+}/edit/passphrase', 'Profile:pass', 'EditUserPass');
$r->add('GET', '/user/{id:[2-9]|[1-9]\d+}/{name}', 'ProfileView:view', 'User');
$r->add(['GET', 'POST'], '/user/{id:[2-9]|[1-9]\d+}/edit/profile', 'ProfileEdit:edit', 'EditUserProfile');
$r->add(['GET', 'POST'], '/user/{id:[2-9]|[1-9]\d+}/edit/config', 'ProfileConfig:config', 'EditUserBoardConfig');
$r->add(['GET', 'POST'], '/user/{id:[2-9]|[1-9]\d+}/edit/email', 'ProfileEmail:email', 'EditUserEmail');
$r->add(['GET', 'POST'], '/user/{id:[2-9]|[1-9]\d+}/edit/passphrase', 'ProfilePass:pass', 'EditUserPass');
} 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 . '}/edit/email', 'Profile:email', 'EditUserEmail');
$r->add(['GET', 'POST'], '/user/{id:' . $user->id . '}/edit/passphrase', 'Profile:pass', 'EditUserPass');
$r->add('GET', '/user/{id:' . $user->id . '}/{name}', 'ProfileView:view', 'User');
$r->add(['GET', 'POST'], '/user/{id:' . $user->id . '}/edit/profile', 'ProfileEdit:edit', 'EditUserProfile');
$r->add(['GET', 'POST'], '/user/{id:' . $user->id . '}/edit/config', 'ProfileConfig:config', 'EditUserBoardConfig');
$r->add(['GET', 'POST'], '/user/{id:' . $user->id . '}/edit/email', 'ProfileEmail:email', 'EditUserEmail');
$r->add(['GET', 'POST'], '/user/{id:' . $user->id . '}/edit/passphrase', 'ProfilePass:pass', 'EditUserPass');
}
// смена своего email
if (! $user->isGuest) {
$r->add('GET', '/user/{id:' . $user->id . '}/{email}/{key}/{hash}', 'Profile:setEmail', 'SetNewEmail');
$r->add('GET', '/user/{id:' . $user->id . '}/{email}/{key}/{hash}', 'ProfileEmail:setEmail', 'SetNewEmail');
}
// пометка разделов прочитанными
if (! $user->isGuest) {

View file

@ -6,7 +6,7 @@ use ForkBB\Core\Container;
use ForkBB\Models\Model;
use RuntimeException;
class Page extends Model
abstract class Page extends Model
{
/**
* Конструктор

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,291 @@
<?php
namespace ForkBB\Models\Pages\Profile;
use ForkBB\Core\Validator;
use ForkBB\Models\Pages\Profile;
class Config extends Profile
{
/**
* Подготавливает данные для шаблона настройки форума
*
* @param array $args
* @param string $method
*
* @return Page
*/
public function config(array $args, $method)
{
if (false === $this->initProfile($args['id']) || ! $this->rules->editConfig) {
return $this->c->Message->message('Bad request');
}
$this->c->Lang->load('profile_other');
if ('POST' === $method) {
$v = $this->c->Validator->reset()
->addValidators([
])->addRules([
'token' => 'token:EditUserBoardConfig',
'language' => 'required|string:trim|in:' . \implode(',', $this->c->Func->getLangs()),
'style' => 'required|string:trim|in:' . \implode(',', $this->c->Func->getStyles()),
'timezone' => 'required|numeric|in:-12,-11,-10,-9.5,-9,-8.5,-8,-7,-6,-5,-4,-3.5,-3,-2,-1,0,1,2,3,3.5,4,4.5,5,5.5,5.75,6,6.5,7,8,8.75,9,9.5,10,10.5,11,11.5,12,12.75,13,14',
'dst' => 'required|integer|in:0,1',
'time_format' => 'required|integer|in:' . \implode(',', \array_keys($this->c->TIME_FORMATS)),
'date_format' => 'required|integer|in:' . \implode(',', \array_keys($this->c->DATE_FORMATS)),
'show_smilies' => 'required|integer|in:0,1',
'show_sig' => 'required|integer|in:0,1',
'show_avatars' => 'required|integer|in:0,1',
'show_img' => 'required|integer|in:0,1',
'show_img_sig' => 'required|integer|in:0,1',
'disp_topics' => 'integer|min:10|max:50',
'disp_posts' => 'integer|min:10|max:50',
])->addAliases([
'language' => 'Language',
'style' => 'Style',
'timezone' => 'Time zone',
'dst' => 'DST label',
'time_format' => 'Time format',
'date_format' => 'Date format',
'show_smilies' => 'Smilies label',
'show_sig' => 'Sigs label',
'show_avatars' => 'Avatars label',
'show_img' => 'Images label',
'show_img_sig' => 'Images sigs label',
'disp_topics' => 'Topics per page label',
'disp_posts' => 'Posts per page label',
])->addArguments([
'token' => ['id' => $this->curUser->id],
])->addMessages([
]);
if ($v->validation($_POST)) {
$data = $v->getData();
unset($data['token']);
$this->curUser->replAttrs($data, true);
$this->c->DB->beginTransaction();
$this->c->users->update($this->curUser);
$this->c->DB->commit();
return $this->c->Redirect->page('EditUserBoardConfig', ['id' => $this->curUser->id])->message('Board configuration redirect');
}
$this->fIswev = $v->getErrors();
}
$this->crumbs = $this->crumbsExt([$this->c->Router->link('EditUserBoardConfig', ['id' => $this->curUser->id]), \ForkBB\__('Board configuration')]);
$this->form = $this->form();
$this->actionBtns = $this->btns('config');
return $this;
}
/**
* Создает массив данных для формы
*
* @return array
*/
protected function form()
{
$form = [
'action' => $this->c->Router->link('EditUserBoardConfig', ['id' => $this->curUser->id]),
'hidden' => [
'token' => $this->c->Csrf->create('EditUserBoardConfig', ['id' => $this->curUser->id]),
],
'sets' => [],
'btns' => [
'save' => [
'type' => 'submit',
'value' => \ForkBB\__('Save changes'),
'accesskey' => 's',
],
],
];
$yn = [1 => \ForkBB\__('Yes'), 0 => \ForkBB\__('No')];
$langs = $this->c->Func->getLangs();
$langs = \array_combine($langs, $langs);
$styles = $this->c->Func->getStyles();
$styles = \array_combine($styles, $styles);
$timeFormat = [];
foreach ($this->c->TIME_FORMATS as $key => $value) {
$timeFormat[$key] = \ForkBB\dt(\time(), false, null, $value, true, true) . ($key ? '' : ' (' . \ForkBB\__('Default') . ')');
}
$dateFormat = [];
foreach ($this->c->DATE_FORMATS as $key => $value) {
$dateFormat[$key] = \ForkBB\dt(\time(), true, $value, null, false, true) . ($key ? '' : ' (' . \ForkBB\__('Default') . ')');
}
$form['sets'][] = [
'id' => 'essentials',
'legend' => \ForkBB\__('Essentials'),
'class' => 'data-edit',
'fields' => [
'language' => [
'id' => 'language',
'type' => 'select',
'options' => $langs,
'value' => $this->curUser->language,
'caption' => \ForkBB\__('Language'),
],
'style' => [
'id' => 'style',
'type' => 'select',
'options' => $styles,
'value' => $this->curUser->style,
'caption' => \ForkBB\__('Style'),
],
'timezone' => [
'id' => 'timezone',
'type' => 'select',
'options' => [
'-12' => \ForkBB\__('UTC-12:00'),
'-11' => \ForkBB\__('UTC-11:00'),
'-10' => \ForkBB\__('UTC-10:00'),
'-9.5' => \ForkBB\__('UTC-09:30'),
'-9' => \ForkBB\__('UTC-09:00'),
'-8.5' => \ForkBB\__('UTC-08:30'),
'-8' => \ForkBB\__('UTC-08:00'),
'-7' => \ForkBB\__('UTC-07:00'),
'-6' => \ForkBB\__('UTC-06:00'),
'-5' => \ForkBB\__('UTC-05:00'),
'-4' => \ForkBB\__('UTC-04:00'),
'-3.5' => \ForkBB\__('UTC-03:30'),
'-3' => \ForkBB\__('UTC-03:00'),
'-2' => \ForkBB\__('UTC-02:00'),
'-1' => \ForkBB\__('UTC-01:00'),
'0' => \ForkBB\__('UTC'),
'1' => \ForkBB\__('UTC+01:00'),
'2' => \ForkBB\__('UTC+02:00'),
'3' => \ForkBB\__('UTC+03:00'),
'3.5' => \ForkBB\__('UTC+03:30'),
'4' => \ForkBB\__('UTC+04:00'),
'4.5' => \ForkBB\__('UTC+04:30'),
'5' => \ForkBB\__('UTC+05:00'),
'5.5' => \ForkBB\__('UTC+05:30'),
'5.75' => \ForkBB\__('UTC+05:45'),
'6' => \ForkBB\__('UTC+06:00'),
'6.5' => \ForkBB\__('UTC+06:30'),
'7' => \ForkBB\__('UTC+07:00'),
'8' => \ForkBB\__('UTC+08:00'),
'8.75' => \ForkBB\__('UTC+08:45'),
'9' => \ForkBB\__('UTC+09:00'),
'9.5' => \ForkBB\__('UTC+09:30'),
'10' => \ForkBB\__('UTC+10:00'),
'10.5' => \ForkBB\__('UTC+10:30'),
'11' => \ForkBB\__('UTC+11:00'),
'11.5' => \ForkBB\__('UTC+11:30'),
'12' => \ForkBB\__('UTC+12:00'),
'12.75' => \ForkBB\__('UTC+12:45'),
'13' => \ForkBB\__('UTC+13:00'),
'14' => \ForkBB\__('UTC+14:00'),
],
'value' => $this->curUser->timezone,
'caption' => \ForkBB\__('Time zone'),
],
'dst' => [
'id' => 'dst',
'type' => 'radio',
'value' => $this->curUser->dst,
'values' => $yn,
'caption' => \ForkBB\__('DST label'),
'info' => \ForkBB\__('DST help'),
],
'time_format' => [
'id' => 'time_format',
'type' => 'select',
'options' => $timeFormat,
'value' => $this->curUser->time_format,
'caption' => \ForkBB\__('Time format'),
],
'date_format' => [
'id' => 'date_format',
'type' => 'select',
'options' => $dateFormat,
'value' => $this->curUser->date_format,
'caption' => \ForkBB\__('Date format'),
],
],
];
$form['sets'][] = [
'id' => 'viewing-posts',
'legend' => \ForkBB\__('Viewing posts'),
'class' => 'data-edit',
'fields' => [
'show_smilies' => [
'id' => 'show_smilies',
'type' => 'radio',
'value' => $this->curUser->show_smilies,
'values' => $yn,
'caption' => \ForkBB\__('Smilies label'),
'info' => \ForkBB\__('Smilies info'),
],
'show_sig' => [
'id' => 'show_sig',
'type' => 'radio',
'value' => $this->curUser->show_sig,
'values' => $yn,
'caption' => \ForkBB\__('Sigs label'),
'info' => \ForkBB\__('Sigs info'),
],
'show_avatars' => [
'id' => 'show_avatars',
'type' => 'radio',
'value' => $this->curUser->show_avatars,
'values' => $yn,
'caption' => \ForkBB\__('Avatars label'),
'info' => \ForkBB\__('Avatars info'),
],
'show_img' => [
'id' => 'show_img',
'type' => 'radio',
'value' => $this->curUser->show_img,
'values' => $yn,
'caption' => \ForkBB\__('Images label'),
'info' => \ForkBB\__('Images info'),
],
'show_img_sig' => [
'id' => 'show_img_sig',
'type' => 'radio',
'value' => $this->curUser->show_img_sig,
'values' => $yn,
'caption' => \ForkBB\__('Images sigs label'),
'info' => \ForkBB\__('Images sigs info'),
],
],
];
$form['sets'][] = [
'id' => 'pagination',
'legend' => \ForkBB\__('Pagination'),
'class' => 'data-edit',
'fields' => [
'disp_topics' => [
'id' => 'disp_topics',
'type' => 'number',
'min' => 10,
'max' => 50,
'value' => $this->curUser->disp_topics,
'caption' => \ForkBB\__('Topics per page label'),
'info' => \ForkBB\__('For default'),
],
'disp_posts' => [
'id' => 'disp_posts',
'type' => 'number',
'min' => 10,
'max' => 50,
'value' => $this->curUser->disp_posts,
'caption' => \ForkBB\__('Posts per page label'),
'info' => \ForkBB\__('For default'),
],
],
];
return $form;
}
}

View file

@ -0,0 +1,436 @@
<?php
namespace ForkBB\Models\Pages\Profile;
use ForkBB\Core\Image;
use ForkBB\Core\Validator;
use ForkBB\Core\Exceptions\MailException;
use ForkBB\Models\Pages\Profile;
use ForkBB\Models\User\Model as User;
class Edit extends Profile
{
/**
* Подготавливает данные для шаблона редактирования профиля
*
* @param array $args
* @param string $method
*
* @return Page
*/
public function edit(array $args, $method)
{
if (false === $this->initProfile($args['id']) || ! $this->rules->editProfile) {
return $this->c->Message->message('Bad request');
}
$this->c->Lang->load('profile_other');
if ('POST' === $method) {
if ($this->rules->rename) {
$ruleUsername = 'required|string:trim,spaces|username';
} else {
$ruleUsername = 'absent';
}
if ($this->rules->setTitle) {
$ruleTitle = 'string:trim|max:50|noURL';
} else {
$ruleTitle = 'absent';
}
if ($this->rules->useAvatar) {
$ruleAvatar = "image|max:{$this->c->Files->maxImgSize('K')}";
$ruleDelAvatar = $this->curUser->avatar ? 'checkbox' : 'absent';
} else {
$ruleAvatar = 'absent';
$ruleDelAvatar = 'absent';
}
if ($this->user->isAdmMod) {
$ruleAdminNote = 'string:trim|max:30';
} else {
$ruleAdminNote = 'absent';
}
if ($this->rules->editWebsite) {
$ruleWebsite = 'string:trim|max:100'; // ???? валидация url?
} else {
$ruleWebsite = 'absent';
}
if ($this->rules->useSignature) {
$ruleSignature = "string:trim|max:{$this->c->config->p_sig_length}|check_signature";
} else {
$ruleSignature = 'absent';
}
$v = $this->c->Validator->reset()
->addValidators([
'check_signature' => [$this, 'vCheckSignature'],
])->addRules([
'token' => 'token:EditUserProfile',
'username' => $ruleUsername,
'title' => $ruleTitle,
'upload_avatar' => $ruleAvatar,
'delete_avatar' => $ruleDelAvatar,
'admin_note' => $ruleAdminNote,
'realname' => 'string:trim|max:40|noURL:1',
'gender' => 'required|integer|in:0,1,2',
'location' => 'string:trim|max:30|noURL:1',
'email_setting' => 'required|integer|in:0,1,2',
'url' => $ruleWebsite,
'signature' => $ruleSignature,
])->addAliases([
'username' => 'Username',
'title' => 'Title',
'upload_avatar' => 'New avatar',
'delete_avatar' => 'Delete avatar',
'admin_note' => 'Admin note',
'realname' => 'Realname',
'gender' => 'Gender',
'location' => 'Location',
'email_setting' => 'Email settings label',
'url' => 'Website',
'signature' => 'Signature',
])->addArguments([
'token' => ['id' => $this->curUser->id],
'username.username' => $this->curUser,
])->addMessages([
]);
$valid = $v->validation($_FILES + $_POST);
$data = $v->getData();
unset($data['token'], $data['upload_avatar']);
if ($valid) {
if ($v->delete_avatar || $v->upload_avatar instanceof Image) {
$this->curUser->deleteAvatar();
}
if ($v->upload_avatar instanceof Image) {
$v->upload_avatar
->rename(false)
->rewrite(true)
->resize((int) $this->c->config->o_avatars_width, (int) $this->c->config->o_avatars_height)
->toFile($this->c->DIR_PUBLIC . "{$this->c->config->o_avatars_dir}/{$this->curUser->id}.(jpg|png|gif)");
}
$this->curUser->replAttrs($data, true);
$this->c->DB->beginTransaction();
$this->c->users->update($this->curUser);
$this->c->DB->commit();
return $this->c->Redirect->page('EditUserProfile', ['id' => $this->curUser->id])->message('Profile redirect');
} else {
$this->fIswev = $v->getErrors();
$this->curUser->replAttrs($data);
}
}
$this->crumbs = $this->crumbsExt([$this->c->Router->link('EditUserProfile', ['id' => $this->curUser->id]), \ForkBB\__('Editing profile')]);
$this->form = $this->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;
}
/**
* Создает массив данных для формы
*
* @return array
*/
protected function form()
{
$form = [
'action' => $this->c->Router->link('EditUserProfile', ['id' => $this->curUser->id]),
'hidden' => [
'token' => $this->c->Csrf->create('EditUserProfile', ['id' => $this->curUser->id]),
],
'sets' => [],
'btns' => [
'save' => [
'type' => 'submit',
'value' => \ForkBB\__('Save changes'),
'accesskey' => 's',
],
],
];
// имя, титул и аватара
$fields = [];
$fields[] = [
'class' => 'usertitle',
'type' => 'wrap',
];
if ($this->rules->rename) {
$fields['username'] = [
'id' => 'username',
'type' => 'text',
'maxlength' => 25,
'caption' => \ForkBB\__('Username'),
'required' => true,
'pattern' => '^.{2,25}$',
'value' => $this->curUser->username,
];
} else {
$fields['username'] = [
'id' => 'username',
'class' => 'pline',
'type' => 'str',
'caption' => \ForkBB\__('Username'),
'value' => $this->curUser->username,
];
}
if ($this->rules->editPass) {
$fields[] = [
'id' => 'change_pass',
'type' => 'link',
'value' => \ForkBB\__('Change passphrase'),
'href' => $this->c->Router->link('EditUserPass', ['id' => $this->curUser->id]),
];
}
if ($this->rules->setTitle) {
$fields['title'] = [
'id' => 'title',
'type' => 'text',
'maxlength' => 50,
'caption' => \ForkBB\__('Title'),
'value' => $this->curUser->title,
'info' => \ForkBB\__('Leave blank'),
];
} else {
$fields['title'] = [
'id' => 'title',
'class' => 'pline',
'type' => 'str',
'caption' => \ForkBB\__('Title'),
'value' => $this->curUser->title(),
];
}
$fields[] = [
'type' => 'endwrap',
];
if ($this->rules->useAvatar) {
if (! $this->curUser->avatar) {
$fields['avatar'] = [
'id' => 'avatar',
'class' => 'pline',
'type' => 'str',
'caption' => \ForkBB\__('Avatar'),
'value' => \ForkBB\__('Not uploaded'),
];
} elseif ($this->curUser->avatar) {
$fields['avatar'] = [
'id' => 'avatar',
'type' => 'yield',
'caption' => \ForkBB\__('Avatar'),
'value' => 'avatar',
];
}
$form['enctype'] = 'multipart/form-data';
$form['hidden']['MAX_FILE_SIZE'] = $this->c->Files->maxImgSize();
if ($this->curUser->avatar) {
$fields['delete_avatar'] = [
'id' => 'delete_avatar',
'type' => 'checkbox',
'label' => \ForkBB\__('Delete avatar'),
'value' => '1',
'checked' => false,
];
}
$fields['upload_avatar'] = [
'id' => 'upload_avatar',
'type' => 'file',
'caption' => \ForkBB\__('New avatar'),
'info' => \ForkBB\__('New avatar info',
\ForkBB\num($this->c->config->o_avatars_width),
\ForkBB\num($this->c->config->o_avatars_height),
\ForkBB\num($this->c->config->o_avatars_size),
\ForkBB\size($this->c->config->o_avatars_size)
),
];
}
$form['sets'][] = [
'id' => 'header',
'class' => 'header-edit',
# 'legend' => \ForkBB\__('Options'),
'fields' => $fields,
];
// примечание администрации
if ($this->user->isAdmMod) {
$form['sets'][] = [
'id' => 'note',
'class' => 'data-edit',
'legend' => \ForkBB\__('Admin note'),
'fields' => [
'admin_note' => [
'id' => 'admin_note',
'type' => 'text',
'maxlength' => 30,
'caption' => \ForkBB\__('Admin note'),
'value' => $this->curUser->admin_note,
],
],
];
}
// личное
$fields = [];
$fields['realname'] = [
'id' => 'realname',
'type' => 'text',
'maxlength' => 40,
'caption' => \ForkBB\__('Realname'),
'value' => $this->curUser->realname,
];
$genders = [
0 => \ForkBB\__('Do not display'),
1 => \ForkBB\__('Male'),
2 => \ForkBB\__('Female'),
];
$fields['gender'] = [
'id' => 'gender',
'class' => 'block',
'type' => 'radio',
'value' => $this->curUser->gender,
'values' => $genders,
'caption' => \ForkBB\__('Gender'),
];
$fields['location'] = [
'id' => 'location',
'type' => 'text',
'maxlength' => 30,
'caption' => \ForkBB\__('Location'),
'value' => $this->curUser->location,
];
$form['sets'][] = [
'id' => 'personal',
'class' => 'data-edit',
'legend' => \ForkBB\__('Personal information'),
'fields' => $fields,
];
// контактная информация
$fields = [];
if ($this->rules->viewOEmail) {
$fields['open-email'] = [
'id' => 'open-email',
'class' => 'pline',
'type' => 'str',
'caption' => \ForkBB\__('Email info'),
'value' => \ForkBB\cens($this->curUser->email),
];
}
if ($this->rules->editEmail) {
$fields[] = [
'id' => 'change_email',
'type' => 'link',
'value' => \ForkBB\__('To change email'),
'href' => $this->c->Router->link('EditUserEmail', ['id' => $this->curUser->id]),
];
}
$fields['email_setting'] = [
'id' => 'email_setting',
'class' => 'block',
'type' => 'radio',
'value' => $this->curUser->email_setting,
'values' => [
0 => \ForkBB\__('Display e-mail label'),
1 => \ForkBB\__('Hide allow form label'),
2 => \ForkBB\__('Hide both label'),
],
'caption' => \ForkBB\__('Email settings label'),
];
if ($this->rules->editWebsite) {
$fields['url'] = [
'id' => 'website',
'type' => 'text',
'maxlength' => 100,
'caption' => \ForkBB\__('Website'),
'value' => $this->curUser->url,
];
} elseif ($this->rules->viewWebsite && $this->curUser->url) {
$fields['url'] = [
'id' => 'website',
'class' => 'pline',
'type' => 'link',
'caption' => \ForkBB\__('Website'),
'value' => \ForkBB\cens($this->curUser->url),
'href' => \ForkBB\cens($this->curUser->url),
];
}
$form['sets'][] = [
'id' => 'contacts',
'class' => 'data-edit',
'legend' => \ForkBB\__('Contact details'),
'fields' => $fields,
];
// подпись
if ($this->rules->useSignature) {
$fields = [];
$fields['signature'] = [
'id' => 'signature',
'type' => 'textarea',
'value' => $this->curUser->signature,
'caption' => \ForkBB\__('Signature'),
'info' => \ForkBB\__('Sig max size', \ForkBB\num($this->c->config->p_sig_length), \ForkBB\num($this->c->config->p_sig_lines)),
];
$form['sets'][] = [
'id' => 'signature',
'class' => 'data-edit',
'legend' => \ForkBB\__('Signature'),
'fields' => $fields,
];
}
return $form;
}
}

View file

@ -0,0 +1,183 @@
<?php
namespace ForkBB\Models\Pages\Profile;
use ForkBB\Core\Validator;
use ForkBB\Core\Exceptions\MailException;
use ForkBB\Models\Pages\Profile;
use ForkBB\Models\User\Model as User;
class Email extends Profile
{
/**
* Изменяет почтовый адрес пользователя по ссылке активации
*
* @param array $args
* @param string $method
*
* @return Page
*/
public function setEmail(array $args, $method)
{
if ($this->user->id !== (int) $args['id']
|| ! \hash_equals($args['hash'], $this->c->Secury->hash($args['id'] . $args['email'] . $args['key']))
|| empty($this->user->activate_string)
|| ! \hash_equals($this->user->activate_string, $args['key'])
) {
return $this->c->Message->message('Bad request', false);
}
$this->c->Lang->load('profile');
$this->user->email = $args['email'];
$this->user->email_confirmed = 1;
$this->user->activate_string = '';
$this->c->users->update($this->user);
return $this->c->Redirect->url($this->user->link)->message('Email changed redirect');
}
/**
* Подготавливает данные для шаблона смены почтового адреса
*
* @param array $args
* @param string $method
*
* @return Page
*/
public function email(array $args, $method)
{
if (false === $this->initProfile($args['id']) || ! $this->rules->editEmail) {
return $this->c->Message->message('Bad request');
}
if ('POST' === $method) {
$v = $this->c->Validator->reset()
->addValidators([
'check_password' => [$this, 'vCheckPassword'],
])->addRules([
'token' => 'token:EditUserEmail',
'password' => 'required|string:trim|check_password',
'new_email' => 'required|string:trim,lower|email:noban,unique,flood',
])->addAliases([
'new_email' => 'New email',
'password' => 'Your passphrase',
])->addArguments([
'token' => ['id' => $this->curUser->id],
'new_email.email' => $this->curUser,
])->addMessages([
]);
if ($v->validation($_POST)) {
if ($v->new_email === $this->curUser->email) {
return $this->c->Redirect->page('EditUserProfile', ['id' => $this->curUser->id])->message('Email is old redirect');
}
if ($this->user->isAdmin || '1' != $this->c->config->o_regs_verify) {
$this->curUser->email = $v->new_email;
$this->curUser->email_confirmed = 0;
$this->c->users->update($this->curUser);
return $this->c->Redirect->page('EditUserProfile', ['id' => $this->curUser->id])->message('Email changed redirect');
} else {
$key = $this->c->Secury->randomPass(33);
$hash = $this->c->Secury->hash($this->curUser->id . $v->new_email . $key);
$link = $this->c->Router->link('SetNewEmail', ['id' => $this->curUser->id, 'email' => $v->new_email, 'key' => $key, 'hash' => $hash]);
$tplData = [
'fRootLink' => $this->c->Router->link('Index'),
'fMailer' => \ForkBB\__('Mailer', $this->c->config->o_board_title),
'username' => $this->curUser->username,
'link' => $link,
];
try {
$isSent = $this->c->Mail
->reset()
->setFolder($this->c->DIR_LANG)
->setLanguage($this->curUser->language)
->setTo($v->new_email, $this->curUser->username)
->setFrom($this->c->config->o_webmaster_email, \ForkBB\__('Mailer', $this->c->config->o_board_title))
->setTpl('activate_email.tpl', $tplData)
->send();
} catch (MailException $e) {
$isSent = false;
}
if ($isSent) {
$this->curUser->activate_string = $key;
$this->curUser->last_email_sent = \time();
$this->c->users->update($this->curUser);
return $this->c->Message->message(\ForkBB\__('Activate email sent', $this->c->config->o_admin_email), false, 200);
} else {
return $this->c->Message->message(\ForkBB\__('Error mail', $this->c->config->o_admin_email), true, 200);
}
}
} else {
$this->curUser->__email = $v->new_email;
}
$this->fIswev = $v->getErrors();
}
$this->crumbs = $this->crumbsExt(
[$this->c->Router->link('EditUserEmail', ['id' => $this->curUser->id]), \ForkBB\__('Change email')],
[$this->c->Router->link('EditUserProfile', ['id' => $this->curUser->id]), \ForkBB\__('Editing profile')]
);
$this->form = $this->form();
$this->actionBtns = $this->btns('edit');
return $this;
}
/**
* Создает массив данных для формы
*
* @return array
*/
protected function form()
{
$form = [
'action' => $this->c->Router->link('EditUserEmail', ['id' => $this->curUser->id]),
'hidden' => [
'token' => $this->c->Csrf->create('EditUserEmail', ['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' => $this->curUser->email,
'info' => ! $this->user->isAdmin && '1' == $this->c->config->o_regs_verify ? \ForkBB\__('Email instructions') : null,
],
'password' => [
'id' => 'password',
'type' => 'password',
'caption' => \ForkBB\__('Your passphrase'),
'required' => true,
],
],
],
],
'btns' => [
'submit' => [
'type' => 'submit',
'value' => \ForkBB\__('Submit'),
'accesskey' => 's',
],
],
];
return $form;
}
}

View file

@ -0,0 +1,119 @@
<?php
namespace ForkBB\Models\Pages\Profile;
use ForkBB\Core\Image;
use ForkBB\Core\Validator;
use ForkBB\Core\Exceptions\MailException;
use ForkBB\Models\Pages\Profile;
use ForkBB\Models\User\Model as User;
class Pass extends Profile
{
/**
* Подготавливает данные для шаблона смены пароля
*
* @param array $args
* @param string $method
*
* @return Page
*/
public function pass(array $args, $method)
{
if (false === $this->initProfile($args['id']) || ! $this->rules->editPass) {
return $this->c->Message->message('Bad request');
}
if ('POST' === $method) {
$v = $this->c->Validator->reset()
->addValidators([
'check_password' => [$this, 'vCheckPassword'],
])->addRules([
'token' => 'token:EditUserPass',
'password' => 'required|string:trim|check_password',
'new_pass' => 'required|string:trim,lower|password',
])->addAliases([
'new_pass' => 'New pass',
'password' => 'Your passphrase',
])->addArguments([
'token' => ['id' => $this->curUser->id],
])->addMessages([
]);
if ($v->validation($_POST)) {
// if (\password_verify($v->new_pass, $this->curUser->password)) {
// return $this->c->Redirect->page('EditUserProfile', ['id' => $this->curUser->id])->message('Email is old redirect');
// }
$this->curUser->password = \password_hash($v->new_pass, \PASSWORD_DEFAULT);
$this->c->users->update($this->curUser);
if ($this->rules->my) {
# $auth = $this->c->Auth;
# $auth->fIswev = ['s' => [\ForkBB\__('Pass updated')]];
# return $auth->login(['_username' => $this->curUser->username], 'GET');
return $this->c->Redirect->page('Login')->message('Pass updated'); // ???? нужна передача данных между скриптами не привязанная к пользователю
} else {
return $this->c->Redirect->page('EditUserProfile', ['id' => $this->curUser->id])->message('Pass updated redirect');
}
}
$this->fIswev = $v->getErrors();
}
$this->crumbs = $this->crumbsExt(
[$this->c->Router->link('EditUserPass', ['id' => $this->curUser->id]), \ForkBB\__('Change pass')],
[$this->c->Router->link('EditUserProfile', ['id' => $this->curUser->id]), \ForkBB\__('Editing profile')]
);
$this->form = $this->form();
$this->actionBtns = $this->btns('edit');
return $this;
}
/**
* Создает массив данных для формы
*
* @return array
*/
protected function form()
{
$form = [
'action' => $this->c->Router->link('EditUserPass', ['id' => $this->curUser->id]),
'hidden' => [
'token' => $this->c->Csrf->create('EditUserPass', ['id' => $this->curUser->id]),
],
'sets' => [
[
'class' => 'data-edit',
'fields' => [
'new_pass' => [
'id' => 'new_pass',
'type' => 'password',
'maxlength' => 25,
'caption' => \ForkBB\__('New pass'),
'required' => true,
'pattern' => '^.{16,}$',
'info' => \ForkBB\__('Pass format') . ' ' . \ForkBB\__('Pass info'),
],
'password' => [
'id' => 'password',
'type' => 'password',
'caption' => \ForkBB\__('Your passphrase'),
'required' => true,
],
],
],
],
'btns' => [
'submit' => [
'type' => 'submit',
'value' => \ForkBB\__('Submit'),
'accesskey' => 's',
],
],
];
return $form;
}
}

View file

@ -0,0 +1,297 @@
<?php
namespace ForkBB\Models\Pages\Profile;
use ForkBB\Models\Pages\Profile;
class View extends Profile
{
/**
* Подготавливает данные для шаблона просмотра профиля
*
* @param array $args
* @param string $method
*
* @return Page
*/
public function view(array $args, $method)
{
if (false === $this->initProfile($args['id'])) {
return $this->c->Message->message('Bad request');
}
$this->canonical = $this->curUser->link;
$this->robots = null;
$this->crumbs = $this->crumbsExt();
$this->form = $this->form();
$this->actionBtns = $this->btns('view');
return $this;
}
/**
* Создает массив данных для формы
*
* @return array
*/
protected function form()
{
$form = [
'sets' => []
];
// имя, титул и аватара
$fields = [];
$fields[] = [
'class' => 'usertitle',
'type' => 'wrap',
];
$fields['username'] = [
'id' => 'username',
'class' => 'pline',
'type' => 'str',
'caption' => \ForkBB\__('Username'),
'value' => $this->curUser->username,
];
$fields['title'] = [
'id' => 'title',
'class' => 'pline',
'type' => 'str',
'caption' => \ForkBB\__('Title'),
'value' => $this->curUser->title(),
];
$fields[] = [
'type' => 'endwrap',
];
if ($this->rules->useAvatar) {
$fields['avatar'] = [
'id' => 'avatar',
'type' => 'yield',
'caption' => \ForkBB\__('Avatar'),
'value' => 'avatar',
];
}
$form['sets'][] = [
'id' => 'header',
'class' => 'header',
# 'legend' => \ForkBB\__('Options'),
'fields' => $fields,
];
// примечание администрации
if ($this->user->isAdmMod && '' != $this->curUser->admin_note) {
$form['sets'][] = [
'id' => 'note',
'class' => 'data',
'legend' => \ForkBB\__('Admin note'),
'fields' => [
'admin_note' => [
'id' => 'admin_note',
'class' => 'pline',
'type' => 'str',
'caption' => \ForkBB\__('Admin note'),
'value' => $this->curUser->admin_note,
],
],
];
}
// личное
$fields = [];
if ('' != $this->curUser->realname) {
$fields['realname'] = [
'id' => 'realname',
'class' => 'pline',
'type' => 'str',
'caption' => \ForkBB\__('Realname'),
'value' => \ForkBB\cens($this->curUser->realname),
];
}
$genders = [
0 => \ForkBB\__('Do not display'),
1 => \ForkBB\__('Male'),
2 => \ForkBB\__('Female'),
];
if ($this->curUser->gender && isset($genders[$this->curUser->gender])) {
$fields['gender'] = [
'id' => 'gender',
'class' => 'pline',
'type' => 'str',
'value' => $genders[$this->curUser->gender],
'caption' => \ForkBB\__('Gender'),
];
}
if ('' != $this->curUser->location) {
$fields['location'] = [
'id' => 'location',
'class' => 'pline',
'type' => 'str',
'caption' => \ForkBB\__('Location'),
'value' => \ForkBB\cens($this->curUser->location),
];
}
if (! empty($fields)) {
$form['sets'][] = [
'id' => 'personal',
'class' => 'data',
'legend' => \ForkBB\__('Personal information'),
'fields' => $fields,
];
}
// контактная информация
$fields = [];
if ($this->rules->viewOEmail) {
$fields['open-email'] = [
'id' => 'open-email',
'class' => 'pline',
'type' => 2 === $this->curUser->email_setting ? 'str' : 'link',
'caption' => \ForkBB\__('Email info'),
'value' => \ForkBB\cens($this->curUser->email),
'href' => 'mailto:' . $this->curUser->email,
];
}
if ($this->rules->viewEmail) {
if (0 === $this->curUser->email_setting) {
$fields['email'] = [
'id' => 'email',
'class' => 'pline',
'type' => 'link',
'caption' => \ForkBB\__('Email info'),
'value' => \ForkBB\cens($this->curUser->email),
'href' => 'mailto:' . $this->curUser->email,
];
} elseif (1 === $this->curUser->email_setting) {
$fields['email'] = [
'id' => 'email',
'class' => 'pline',
'type' => 'link',
'caption' => \ForkBB\__('Email info'),
'value' => \ForkBB\__('Send email'),
'href' => $this->c->Router->link('', ['id' => $this->curUser->id]), // ????
];
}
}
if ($this->rules->viewWebsite && $this->curUser->url) {
$fields['url'] = [
'id' => 'website',
'class' => 'pline',
'type' => 'link',
'caption' => \ForkBB\__('Website'),
'value' => \ForkBB\cens($this->curUser->url),
'href' => \ForkBB\cens($this->curUser->url),
];
}
if (! empty($fields)) {
$form['sets'][] = [
'id' => 'contacts',
'class' => 'data',
'legend' => \ForkBB\__('Contact details'),
'fields' => $fields,
];
}
// подпись
if ($this->rules->useSignature) {
$fields = [];
if ('' != $this->curUser->signature) {
$fields['signature'] = [
'id' => 'signature',
'type' => 'yield',
'caption' => \ForkBB\__('Signature'),
'value' => 'signature',
];
}
if (! empty($fields)) {
$form['sets'][] = [
'id' => 'signature',
'class' => 'data',
'legend' => \ForkBB\__('Signature'),
'fields' => $fields,
];
}
}
// активность
$fields = [];
$fields['registered'] = [
'id' => 'registered',
'class' => 'pline',
'type' => 'str',
'value' => \ForkBB\dt($this->curUser->registered, true),
'caption' => \ForkBB\__('Registered info'),
];
if ($this->rules->viewLastVisit) {
$fields['lastvisit'] = [
'id' => 'lastvisit',
'class' => 'pline',
'type' => 'str',
'value' => \ForkBB\dt($this->curUser->last_visit, true),
'caption' => \ForkBB\__('Last visit info'),
];
}
$fields['lastpost'] = [
'id' => 'lastpost',
'class' => 'pline',
'type' => 'str',
'value' => \ForkBB\dt($this->curUser->last_post, true),
'caption' => \ForkBB\__('Last post info'),
];
if ($this->curUser->num_posts) {
if ('1' == $this->user->g_search) {
$fields['posts'] = [
'id' => 'posts',
'class' => 'pline',
'type' => 'link',
'caption' => \ForkBB\__('Posts info'),
'value' => $this->user->showPostCount ? \ForkBB\num($this->curUser->num_posts) : \ForkBB\__('Show posts'),
'href' => $this->c->Router->link('SearchAction', ['action' => 'posts', 'uid' => $this->curUser->id]),
'title' => \ForkBB\__('Show posts'),
];
$fields['topics'] = [
'id' => 'topics',
'class' => 'pline',
'type' => 'link',
'caption' => \ForkBB\__('Topics info'),
'value' => $this->user->showPostCount ? \ForkBB\num($this->curUser->num_topics) : \ForkBB\__('Show topics'),
'href' => $this->c->Router->link('SearchAction', ['action' => 'topics', 'uid' => $this->curUser->id]),
'title' => \ForkBB\__('Show topics'),
];
} elseif ($this->user->showPostCount) {
$fields['posts'] = [
'id' => 'posts',
'class' => 'pline',
'type' => 'str',
'caption' => \ForkBB\__('Posts info'),
'value' => \ForkBB\num($this->curUser->num_posts),
];
$fields['topics'] = [
'id' => 'topics',
'class' => 'pline',
'type' => 'str',
'caption' => \ForkBB\__('Topics info'),
'value' => \ForkBB\num($this->curUser->num_topics),
];
}
}
if ($this->rules->viewIP) {
$fields['ip'] = [
'id' => 'ip',
'class' => 'pline',
'type' => 'link',
'caption' => \ForkBB\__('IP'),
'value' => $this->curUser->registration_ip,
'href' => $this->c->Router->link('', ['id' => $this->curUser->id]), // ????
'title' => \ForkBB\__('IP title'),
];
}
$form['sets'][] = [
'id' => 'activity',
'class' => 'data',
'legend' => \ForkBB\__('User activity'),
'fields' => $fields,
];
return $form;
}
}

View file

@ -147,7 +147,11 @@ return [
'Ban' => \ForkBB\Models\Pages\Ban::class,
'Debug' => \ForkBB\Models\Pages\Debug::class,
'Misc' => \ForkBB\Models\Pages\Misc::class,
'Profile' => \ForkBB\Models\Pages\Profile::class,
'ProfileView' => \ForkBB\Models\Pages\Profile\View::class,
'ProfileEdit' => \ForkBB\Models\Pages\Profile\Edit::class,
'ProfileConfig' => \ForkBB\Models\Pages\Profile\Config::class,
'ProfilePass' => \ForkBB\Models\Pages\Profile\Pass::class,
'ProfileEmail' => \ForkBB\Models\Pages\Profile\Email::class,
'AdminIndex' => \ForkBB\Models\Pages\Admin\Index::class,
'AdminStatistics' => \ForkBB\Models\Pages\Admin\Statistics::class,
'AdminOptions' => \ForkBB\Models\Pages\Admin\Options::class,

View file

@ -28,11 +28,7 @@
<dt> @if ($cur['caption'])<label class="f-child1 @if ($cur['required']) f-req @endif" @if (is_string($key) && 'radio' !== $cur['type'] && 'yield' !== $cur['type']) for="id-{{ $key }}" @endif>{!! $cur['caption'] !!}</label> @endif</dt>
<dd>
@if ('text' === $cur['type'])
@if ($form['action'])
<input @if ($cur['required']) required @endif @if ($cur['disabled']) disabled @endif @if ($cur['autofocus']) autofocus @endif class="f-ctrl" id="id-{{ $key }}" name="{{ $key }}" type="text" @if ($cur['maxlength']) maxlength="{{ $cur['maxlength'] }}" @endif @if ($cur['pattern']) pattern="{{ $cur['pattern'] }}" @endif @if (isset($cur['value'])) value="{{ $cur['value'] }}" @endif>
@else
<p class="f-ctrl" id="id-{{ $key }}">{{ $cur['value'] or '' }}</p>
@endif
@elseif ('textarea' === $cur['type'])
<textarea @if ($cur['required']) required @endif @if ($cur['disabled']) disabled @endif @if ($cur['autofocus']) autofocus @endif class="f-ctrl" id="id-{{ $key }}" name="{{ $key }}">{{ $cur['value'] or '' }}</textarea>
@if ($cur['bb'])
@ -89,13 +85,9 @@
@elseif ('checkbox' === $cur['type'])
<label class="f-child2"><input @if ($cur['autofocus']) autofocus @endif @if ($cur['disabled']) disabled @endif type="checkbox" id="id-{{ $key }}" name="{{ $key }}" value="{{ $cur['value'] or '1' }}" @if ($cur['checked']) checked @endif>{!! $cur['label'] !!}</label>
@elseif ('radio' === $cur['type'])
@if ($form['action'])
@foreach ($cur['values'] as $v => $n)
@foreach ($cur['values'] as $v => $n)
<label class="f-label"><input @if ($cur['autofocus']) autofocus @endif @if ($cur['disabled']) disabled @endif type="radio" id="id-{{ $key }}-{{ $v }}" name="{{ $key }}" value="{{ $v }}" @if ($v == $cur['value']) checked @endif>{{ $n }}</label>
@endforeach
@else
<p class="f-ctrl" id="id-{{ $key }}">{{ $cur['values'][$cur['value']] or '' }}</p>
@endif
@endforeach
@elseif ('password' === $cur['type'])
<input @if ($cur['required']) required @endif @if ($cur['disabled']) disabled @endif @if ($cur['autofocus']) autofocus @endif class="f-ctrl" id="id-{{ $key }}" name="{{ $key }}" type="password" @if ($cur['maxlength']) maxlength="{{ $cur['maxlength'] }}" @endif @if ($cur['pattern']) pattern="{{ $cur['pattern'] }}" @endif @if (isset($cur['value'])) value="{{ $cur['value'] }}" @endif>
@elseif ('btn' === $cur['type'])