* Admin -> Users

This commit is contained in:
Visman 2018-09-03 23:48:47 +07:00
parent 6fc06d2faf
commit 942b0f9afe
6 changed files with 197 additions and 28 deletions

View file

@ -125,7 +125,7 @@ class Routing
$r->add('GET', '/admin/statistics', 'AdminStatistics:statistics', 'AdminStatistics'); $r->add('GET', '/admin/statistics', 'AdminStatistics:statistics', 'AdminStatistics');
$r->add(['GET', 'POST'], '/admin/users', 'AdminUsers:view', 'AdminUsers'); $r->add(['GET', 'POST'], '/admin/users', 'AdminUsers:view', 'AdminUsers');
$r->add('GET', '/admin/users/filter/{filters}/{hash}[/{page:[1-9]\d*}]', 'AdminUsers:filter', 'AdminShowUsersWithFilter'); $r->add('GET', '/admin/users/filter/{filters}/{hash}[/{page:[1-9]\d*}]', 'AdminUsers:viewFilter', 'AdminUsersFilter');
} }
// только админ // только админ
@ -150,7 +150,8 @@ class Routing
$r->add('GET', '/admin/maintenance/rebuild/{token}/{clear:[01]}/{limit:[1-9]\d*}/{start:[1-9]\d*}', 'AdminMaintenance:rebuild', 'AdminRebuildIndex' ); $r->add('GET', '/admin/maintenance/rebuild/{token}/{clear:[01]}/{limit:[1-9]\d*}/{start:[1-9]\d*}', 'AdminMaintenance:rebuild', 'AdminRebuildIndex' );
$r->add('GET', '/admin/get/host/{ip:[0-9a-fA-F:.]+}', 'AdminHost:view', 'AdminHost'); $r->add('GET', '/admin/get/host/{ip:[0-9a-fA-F:.]+}', 'AdminHost:view', 'AdminHost');
$r->add('GET', '/admin/users/ip/{ip:[0-9a-fA-F:.]+}[/{page:[1-9]\d*}]', 'AdminUsers:ip', 'AdminShowUsersWithIP'); $r->add('GET', '/admin/users/ip/{ip:[0-9a-fA-F:.]+}[/{page:[1-9]\d*}]', 'AdminUsers:viewIP', 'AdminUsersIP');
$r->add('GET', '/admin/users/user/{id:[2-9]|[1-9]\d+}[/{page:[1-9]\d*}]', 'AdminUsers:viewUserStat', 'AdminUserStat');
} }
$uri = $_SERVER['REQUEST_URI']; $uri = $_SERVER['REQUEST_URI'];

View file

@ -28,7 +28,7 @@ class Host extends Admin
$this->nameTpl = 'message'; $this->nameTpl = 'message';
$this->titles = \ForkBB\__('Info'); $this->titles = \ForkBB\__('Info');
$this->message = \ForkBB\__('Host info', $ip, $host, $this->c->Router->link('AdminShowUsersWithIP', ['ip' => $ip])); $this->message = \ForkBB\__('Host info', $ip, $host, $this->c->Router->link('AdminUsersIP', ['ip' => $ip]));
$this->back = true; $this->back = true;
return $this; return $this;

View file

@ -31,6 +31,47 @@ class Users extends Admin
return $onlyKeys ? \array_keys($groups) : $groups; return $onlyKeys ? \array_keys($groups) : $groups;
} }
/**
* Подготавливает данные для шаблона ip статистики для пользователя
*
* @param array $args
* @param string $method
*
* @return Page
*/
public function viewUserStat(array $args, $method)
{
$this->c->Lang->load('admin_users');
$stat = $this->c->posts->userStat($args['id']);
$number = \count($stat);
$page = isset($args['page']) ? (int) $args['page'] : 1;
$pages = (int) \ceil(($number ?: 1) / $this->c->config->o_disp_users);
if ($page > $pages) {
return $this->c->Message->message('Bad request');
}
$startNum = ($page - 1) * $this->c->config->o_disp_users;
$stat = \array_slice($stat, $startNum, $this->c->config->o_disp_users);
$user = $this->c->users->load((int) $args['id']);
if (0 == $number) {
$this->fIswev = ['i', \ForkBB\__('Results no posts found')];
}
$this->nameTpl = 'admin/users_result';
$this->aIndex = 'users';
$this->mainSuffix = '-one-column';
$this->aCrumbs[] = [$this->c->Router->link('AdminUserStat', ['id' => $args['id']]), $user->username];
$this->formResult = $this->formStat($stat, $startNum);
$this->pagination = $this->c->Func->paginate($pages, $page, 'AdminUserStat', ['id' => $args['id']]);
return $this;
}
/** /**
* Подготавливает данные для шаблона найденных по ip пользователей * Подготавливает данные для шаблона найденных по ip пользователей
* *
@ -39,7 +80,7 @@ class Users extends Admin
* *
* @return Page * @return Page
*/ */
public function ip(array $args, $method) public function viewIP(array $args, $method)
{ {
$ip = \filter_var($args['ip'], \FILTER_VALIDATE_IP); $ip = \filter_var($args['ip'], \FILTER_VALIDATE_IP);
if (false === $ip) { if (false === $ip) {
@ -69,7 +110,7 @@ class Users extends Admin
} }
$page = isset($args['page']) ? (int) $args['page'] : 1; $page = isset($args['page']) ? (int) $args['page'] : 1;
$pages = (int) \ceil($number / $this->c->config->o_disp_users); $pages = (int) \ceil(($number ?: 1) / $this->c->config->o_disp_users);
if ($page > $pages) { if ($page > $pages) {
return $this->c->Message->message('Bad request'); return $this->c->Message->message('Bad request');
@ -82,9 +123,9 @@ class Users extends Admin
$this->nameTpl = 'admin/users_result'; $this->nameTpl = 'admin/users_result';
$this->aIndex = 'users'; $this->aIndex = 'users';
$this->mainSuffix = '-one-column'; $this->mainSuffix = '-one-column';
$this->aCrumbs[] = [$this->c->Router->link('AdminShowUsersWithIP', ['ip' => $ip]), $ip]; $this->aCrumbs[] = [$this->c->Router->link('AdminUsersIP', ['ip' => $ip]), $ip];
$this->formResult = $this->formUsers($userList, $startNum); $this->formResult = $this->formUsers($userList, $startNum);
$this->pagination = $this->c->Func->paginate($pages, $page, 'AdminShowUsersWithIP', ['ip' => $ip]); $this->pagination = $this->c->Func->paginate($pages, $page, 'AdminUsersIP', ['ip' => $ip]);
return $this; return $this;
} }
@ -97,7 +138,7 @@ class Users extends Admin
* *
* @return Page * @return Page
*/ */
public function filter(array $args, $method) public function viewFilter(array $args, $method)
{ {
if (! \hash_equals($args['hash'], $this->c->Secury->hash($args['filters'])) if (! \hash_equals($args['hash'], $this->c->Secury->hash($args['filters']))
|| ! \is_array($data = \json_decode(\base64_decode($args['filters'], true), true)) || ! \is_array($data = \json_decode(\base64_decode($args['filters'], true), true))
@ -150,7 +191,7 @@ class Users extends Admin
} }
$page = isset($args['page']) ? (int) $args['page'] : 1; $page = isset($args['page']) ? (int) $args['page'] : 1;
$pages = (int) \ceil($number / $this->c->config->o_disp_users); $pages = (int) \ceil(($number ?: 1) / $this->c->config->o_disp_users);
if ($page > $pages) { if ($page > $pages) {
return $this->c->Message->message('Bad request'); return $this->c->Message->message('Bad request');
@ -163,9 +204,9 @@ class Users extends Admin
$this->nameTpl = 'admin/users_result'; $this->nameTpl = 'admin/users_result';
$this->aIndex = 'users'; $this->aIndex = 'users';
$this->mainSuffix = '-one-column'; $this->mainSuffix = '-one-column';
$this->aCrumbs[] = [$this->c->Router->link('AdminShowUsersWithFilter', ['filters' => $args['filters'], 'hash' => $args['hash']]), \ForkBB\__('Results head')]; $this->aCrumbs[] = [$this->c->Router->link('AdminUsersFilter', ['filters' => $args['filters'], 'hash' => $args['hash']]), \ForkBB\__('Results head')];
$this->formResult = $this->formUsers($userList, $startNum); $this->formResult = $this->formUsers($userList, $startNum);
$this->pagination = $this->c->Func->paginate($pages, $page, 'AdminShowUsersWithFilter', ['filters' => $args['filters'], 'hash' => $args['hash']]); $this->pagination = $this->c->Func->paginate($pages, $page, 'AdminUsersFilter', ['filters' => $args['filters'], 'hash' => $args['hash']]);
return $this; return $this;
} }
@ -197,7 +238,7 @@ class Users extends Admin
$this->fIswev = ['v', \ForkBB\__('Bad IP message')]; $this->fIswev = ['v', \ForkBB\__('Bad IP message')];
$data = $v->getData(); $data = $v->getData();
} else { } else {
return $this->c->Redirect->page('AdminShowUsersWithIP', ['ip' => $ip]); return $this->c->Redirect->page('AdminUsersIP', ['ip' => $ip]);
} }
} else { } else {
$v = $this->c->Validator->reset() $v = $this->c->Validator->reset()
@ -254,7 +295,7 @@ class Users extends Admin
unset($filters['token']); unset($filters['token']);
$filters = \base64_encode(\json_encode($filters)); $filters = \base64_encode(\json_encode($filters));
$hash = $this->c->Secury->hash($filters); $hash = $this->c->Secury->hash($filters);
return $this->c->Redirect->page('AdminShowUsersWithFilter', ['filters' => $filters, 'hash' => $hash]); return $this->c->Redirect->page('AdminUsersFilter', ['filters' => $filters, 'hash' => $hash]);
} }
$this->fIswev = $v->getErrors(); $this->fIswev = $v->getErrors();
@ -611,7 +652,7 @@ class Users extends Admin
'title' => \ForkBB\__('Results show posts link'), 'title' => \ForkBB\__('Results show posts link'),
]; ];
$fields["l{$number}-note"] = [ $fields["l{$number}-note"] = [
'class' => $user->isGuest ? ['result', 'note', 'no-data'] : ['result', 'note'], 'class' => '' === \trim($user->admin_note) ? ['result', 'note', 'no-data'] : ['result', 'note'],
'type' => 'str', 'type' => 'str',
'caption' => \ForkBB\__('Примечание админа'), 'caption' => \ForkBB\__('Примечание админа'),
'value' => $user->admin_note, 'value' => $user->admin_note,
@ -620,10 +661,10 @@ class Users extends Admin
if ($this->user->isAdmin) { if ($this->user->isAdmin) {
$fields["l{$number}-view-ip"] = [ $fields["l{$number}-view-ip"] = [
'class' => $user->isGuest ? ['result', 'view-ip', 'no-data'] : ['result', 'view-ip'], 'class' => $user->isGuest ? ['result', 'view-ip', 'no-data'] : ['result', 'view-ip'],
'type' => $user->isGuest ? 'str' : 'link', 'type' => $user->isGuest || ! $user->num_posts ? 'str' : 'link',
'caption' => \ForkBB\__('Results action head'), 'caption' => \ForkBB\__('Results action head'),
'value' => $user->isGuest ? null : \ForkBB\__('Results view IP link'), 'value' => $user->isGuest ? null : \ForkBB\__('Results view IP link'),
'href' => '', 'href' => $this->c->Router->link('AdminUserStat', ['id' => $user->id]),
]; ];
} }
@ -632,7 +673,7 @@ class Users extends Admin
]; ];
$key = $user->isGuest ? "guest{$number}" : "users[{$user->id}]"; $key = $user->isGuest ? "guest{$number}" : "users[{$user->id}]";
$fields[$key] = [ $fields[$key] = [
'class' => ['result', 'check'], 'class' => ['check'],
'caption' => \ForkBB\__('Select'), 'caption' => \ForkBB\__('Select'),
'type' => $user->isGuest ? 'str' : 'checkbox', 'type' => $user->isGuest ? 'str' : 'checkbox',
'value' => $user->isGuest ? null : $user->id, 'value' => $user->isGuest ? null : $user->id,
@ -649,4 +690,67 @@ class Users extends Admin
return $form; return $form;
} }
/**
* Создает массив данных для формы статистики пользователя по ip
*
* @param array $stat
* @param int $number
*
* @return array
*/
protected function formStat(array $stat, $number)
{
$form = [
'action' => null,
'hidden' => null,
'sets' => [],
'btns' => null,
];
\array_unshift($stat, ['last_used' => null, 'used_times' => null]);
$flag = false;
foreach ($stat as $ip => $data) {
$fields = [];
$fields["l{$number}-ip"] = [
'class' => ['result', 'ip'],
'type' => $flag ? 'link' : 'str',
'caption' => \ForkBB\__('Results IP address head'),
'value' => $flag ? $ip : null,
'href' => $flag ? $this->c->Router->link('AdminHost', ['ip' => $ip]) : null,
];
$fields["l{$number}-last-used"] = [
'class' => ['result', 'last-used'],
'type' => 'str',
'caption' => \ForkBB\__('Results last used head'),
'value' => $flag ? \ForkBB\dt($data['last_used']) : null,
];
$fields["l{$number}-used-times"] = [
'class' => ['result', 'used-times'],
'type' => 'str',
'caption' => \ForkBB\__('Results times found head'),
'value' => $flag ? \ForkBB\num($data['used_times']) : null,
];
$fields["l{$number}-action"] = [
'class' => ['result', 'action'],
'type' => $flag ? 'link' : 'str',
'caption' => \ForkBB\__('Results action head'),
'value' => $flag ? \ForkBB\__('Results find more link') : null,
'href' => $flag ? $this->c->Router->link('AdminUsersIP', ['ip' => $ip]) : null,
];
$form['sets']["l{$number}"] = [
'class' => ['result', 'stat'],
'legend' => $flag ? $number : null,
'fields' => $fields,
];
++$number;
$flag = true;
}
return $form;
}
} }

View file

@ -0,0 +1,31 @@
<?php
namespace ForkBB\Models\Post;
use ForkBB\Models\Action;
use ForkBB\Models\Post\Model as Post;
use PDO;
class UserStat extends Action
{
/**
* Возвращает массив данных использования ip для данного пользователя
*
* @param int $id
*
* @return array
*/
public function userStat($id)
{
$vars = [
':id' => $id,
];
$sql = 'SELECT p.poster_ip, MAX(p.posted) AS last_used, COUNT(p.id) AS used_times
FROM ::posts AS p
WHERE p.poster_id=?i:id
GROUP BY p.poster_ip
ORDER BY last_used DESC';
return $this->c->DB->query($sql, $vars)->fetchAll(PDO::FETCH_UNIQUE);
}
}

View file

@ -234,6 +234,7 @@ return [
'PostManagerView' => \ForkBB\Models\Post\View::class, 'PostManagerView' => \ForkBB\Models\Post\View::class,
'PostManagerRebuildIndex' => \ForkBB\Models\Post\RebuildIndex::class, 'PostManagerRebuildIndex' => \ForkBB\Models\Post\RebuildIndex::class,
'PostManagerUserInfoFromIP' => \ForkBB\Models\Post\UserInfoFromIP::class, 'PostManagerUserInfoFromIP' => \ForkBB\Models\Post\UserInfoFromIP::class,
'PostManagerUserStat' => \ForkBB\Models\Post\UserStat::class,
'GroupModel' => \ForkBB\Models\Group\Model::class, 'GroupModel' => \ForkBB\Models\Group\Model::class,
'GroupManager' => \ForkBB\Models\Group\Manager::class, 'GroupManager' => \ForkBB\Models\Group\Manager::class,

View file

@ -2294,6 +2294,10 @@ select {
padding-bottom: 0.3125rem; padding-bottom: 0.3125rem;
} }
.f-fs-stat {
display: block;
}
.f-fs-result:first-of-type, .f-fs-result:first-of-type,
.f-fs-result .f-field-no-data { .f-fs-result .f-field-no-data {
display: none; display: none;
@ -2316,22 +2320,24 @@ select {
width: calc(100% - 3rem); width: calc(100% - 3rem);
} }
.f-wrap-main-result .f-field-result > dt, .f-fs-result .f-field-result > dt,
.f-wrap-main-result .f-field-result > dd, .f-fs-result .f-field-result > dd,
.f-wrap-main-result .f-field-result .f-str { .f-fs-result .f-field-result .f-str {
display: inline; display: inline;
} }
.f-wrap-main-result .f-field-result .f-child1 { .f-fs-result .f-field-result .f-child1 {
display: inline; display: inline;
font-weight: bold; font-weight: bold;
} }
.f-wrap-main-result .f-field-result .f-child1:after { .f-fs-result .f-field-result .f-child1:after {
content: ": "; content: ": ";
} }
.f-fs-result .f-field-check { .f-fs-result .f-field-check {
border: 0;
margin: 0;
width: 3rem; width: 3rem;
overflow: hidden; overflow: hidden;
text-align: center; text-align: center;
@ -2352,22 +2358,26 @@ select {
} }
@media screen and (min-width: 36rem) { @media screen and (min-width: 36rem) {
.f-wrap-main-result .f-field-result > dt, .f-fs-result .f-field-result > dt,
.f-wrap-main-result .f-field-result > dd { .f-fs-result .f-field-result > dd {
display: block; display: block;
} }
.f-wrap-main-result .f-field-result .f-child1 { .f-fs-result .f-field-result .f-child1 {
display: block; display: block;
font-weight: normal; font-weight: normal;
} }
.f-wrap-main-result .f-field-result .f-child1:after { .f-fs-result .f-field-result .f-child1:after {
content: ""; content: "";
} }
} }
@media screen and (min-width: 50rem) { @media screen and (min-width: 50rem) {
.f-fs-stat {
display: flex;
}
.f-wrap-main-result { .f-wrap-main-result {
display: flex; display: flex;
} }
@ -2379,7 +2389,8 @@ select {
justify-content: center; justify-content: center;
} }
.f-fs-result .f-field-result > dt { .f-fs-result .f-field-result > dt,
.f-fs-result:first-of-type .f-field-check > dt {
display: block; display: block;
margin: 0; margin: 0;
width: 100%; width: 100%;
@ -2400,7 +2411,8 @@ select {
display: none; display: none;
} }
.f-fs-result .f-field-result .f-child1 { .f-fs-result .f-field-result .f-child1,
.f-fs-result .f-field-check .f-child1 {
font-weight: bold; font-weight: bold;
} }
@ -2440,4 +2452,24 @@ select {
border-left: 0.0625rem dotted #AA7939; border-left: 0.0625rem dotted #AA7939;
} }
.f-fs-result .f-field-ip {
width: 25%;
}
.f-fs-result .f-field-last-used {
width: 25%;
border-left: 0.0625rem dotted #AA7939;
text-align: center;
}
.f-fs-result .f-field-used-times {
width: 20%;
border-left: 0.0625rem dotted #AA7939;
text-align: center;
}
.f-fs-result .f-field-action {
width: 30%;
border-left: 0.0625rem dotted #AA7939;
}
} }