From 942b0f9afeb84c5a7a5e971e4a996caff3972858 Mon Sep 17 00:00:00 2001 From: Visman Date: Mon, 3 Sep 2018 23:48:47 +0700 Subject: [PATCH] * Admin -> Users --- app/Controllers/Routing.php | 5 +- app/Models/Pages/Admin/Host.php | 2 +- app/Models/Pages/Admin/Users.php | 132 +++++++++++++++++++++++++++---- app/Models/Post/UserStat.php | 31 ++++++++ app/config/main.dist.php | 1 + public/style/ForkBB/style.css | 54 ++++++++++--- 6 files changed, 197 insertions(+), 28 deletions(-) create mode 100644 app/Models/Post/UserStat.php diff --git a/app/Controllers/Routing.php b/app/Controllers/Routing.php index 3d63906a..6d1549b4 100644 --- a/app/Controllers/Routing.php +++ b/app/Controllers/Routing.php @@ -125,7 +125,7 @@ class Routing $r->add('GET', '/admin/statistics', 'AdminStatistics:statistics', 'AdminStatistics'); $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/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']; diff --git a/app/Models/Pages/Admin/Host.php b/app/Models/Pages/Admin/Host.php index e2b7c65b..26f0a6c6 100644 --- a/app/Models/Pages/Admin/Host.php +++ b/app/Models/Pages/Admin/Host.php @@ -28,7 +28,7 @@ class Host extends Admin $this->nameTpl = 'message'; $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; return $this; diff --git a/app/Models/Pages/Admin/Users.php b/app/Models/Pages/Admin/Users.php index 2fbc3d4f..0cc3bfd4 100644 --- a/app/Models/Pages/Admin/Users.php +++ b/app/Models/Pages/Admin/Users.php @@ -31,6 +31,47 @@ class Users extends Admin 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 пользователей * @@ -39,7 +80,7 @@ class Users extends Admin * * @return Page */ - public function ip(array $args, $method) + public function viewIP(array $args, $method) { $ip = \filter_var($args['ip'], \FILTER_VALIDATE_IP); if (false === $ip) { @@ -69,7 +110,7 @@ class Users extends Admin } $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) { return $this->c->Message->message('Bad request'); @@ -82,9 +123,9 @@ class Users extends Admin $this->nameTpl = 'admin/users_result'; $this->aIndex = 'users'; $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->pagination = $this->c->Func->paginate($pages, $page, 'AdminShowUsersWithIP', ['ip' => $ip]); + $this->pagination = $this->c->Func->paginate($pages, $page, 'AdminUsersIP', ['ip' => $ip]); return $this; } @@ -97,7 +138,7 @@ class Users extends Admin * * @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'])) || ! \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; - $pages = (int) \ceil($number / $this->c->config->o_disp_users); + $pages = (int) \ceil(($number ?: 1) / $this->c->config->o_disp_users); if ($page > $pages) { return $this->c->Message->message('Bad request'); @@ -163,9 +204,9 @@ class Users extends Admin $this->nameTpl = 'admin/users_result'; $this->aIndex = 'users'; $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->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; } @@ -197,7 +238,7 @@ class Users extends Admin $this->fIswev = ['v', \ForkBB\__('Bad IP message')]; $data = $v->getData(); } else { - return $this->c->Redirect->page('AdminShowUsersWithIP', ['ip' => $ip]); + return $this->c->Redirect->page('AdminUsersIP', ['ip' => $ip]); } } else { $v = $this->c->Validator->reset() @@ -254,7 +295,7 @@ class Users extends Admin unset($filters['token']); $filters = \base64_encode(\json_encode($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(); @@ -611,7 +652,7 @@ class Users extends Admin 'title' => \ForkBB\__('Results show posts link'), ]; $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', 'caption' => \ForkBB\__('Примечание админа'), 'value' => $user->admin_note, @@ -620,10 +661,10 @@ class Users extends Admin if ($this->user->isAdmin) { $fields["l{$number}-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'), '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}]"; $fields[$key] = [ - 'class' => ['result', 'check'], + 'class' => ['check'], 'caption' => \ForkBB\__('Select'), 'type' => $user->isGuest ? 'str' : 'checkbox', 'value' => $user->isGuest ? null : $user->id, @@ -649,4 +690,67 @@ class Users extends Admin 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; + } } diff --git a/app/Models/Post/UserStat.php b/app/Models/Post/UserStat.php new file mode 100644 index 00000000..2a7358f7 --- /dev/null +++ b/app/Models/Post/UserStat.php @@ -0,0 +1,31 @@ + $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); + } +} diff --git a/app/config/main.dist.php b/app/config/main.dist.php index 31915ca5..a72bdde8 100644 --- a/app/config/main.dist.php +++ b/app/config/main.dist.php @@ -234,6 +234,7 @@ return [ 'PostManagerView' => \ForkBB\Models\Post\View::class, 'PostManagerRebuildIndex' => \ForkBB\Models\Post\RebuildIndex::class, 'PostManagerUserInfoFromIP' => \ForkBB\Models\Post\UserInfoFromIP::class, + 'PostManagerUserStat' => \ForkBB\Models\Post\UserStat::class, 'GroupModel' => \ForkBB\Models\Group\Model::class, 'GroupManager' => \ForkBB\Models\Group\Manager::class, diff --git a/public/style/ForkBB/style.css b/public/style/ForkBB/style.css index 4ab4db87..991a603f 100644 --- a/public/style/ForkBB/style.css +++ b/public/style/ForkBB/style.css @@ -2294,6 +2294,10 @@ select { padding-bottom: 0.3125rem; } +.f-fs-stat { + display: block; +} + .f-fs-result:first-of-type, .f-fs-result .f-field-no-data { display: none; @@ -2316,22 +2320,24 @@ select { width: calc(100% - 3rem); } -.f-wrap-main-result .f-field-result > dt, -.f-wrap-main-result .f-field-result > dd, -.f-wrap-main-result .f-field-result .f-str { +.f-fs-result .f-field-result > dt, +.f-fs-result .f-field-result > dd, +.f-fs-result .f-field-result .f-str { display: inline; } -.f-wrap-main-result .f-field-result .f-child1 { +.f-fs-result .f-field-result .f-child1 { display: inline; font-weight: bold; } -.f-wrap-main-result .f-field-result .f-child1:after { +.f-fs-result .f-field-result .f-child1:after { content: ": "; } .f-fs-result .f-field-check { + border: 0; + margin: 0; width: 3rem; overflow: hidden; text-align: center; @@ -2352,22 +2358,26 @@ select { } @media screen and (min-width: 36rem) { - .f-wrap-main-result .f-field-result > dt, - .f-wrap-main-result .f-field-result > dd { + .f-fs-result .f-field-result > dt, + .f-fs-result .f-field-result > dd { display: block; } - .f-wrap-main-result .f-field-result .f-child1 { + .f-fs-result .f-field-result .f-child1 { display: block; font-weight: normal; } - .f-wrap-main-result .f-field-result .f-child1:after { + .f-fs-result .f-field-result .f-child1:after { content: ""; } } @media screen and (min-width: 50rem) { + .f-fs-stat { + display: flex; + } + .f-wrap-main-result { display: flex; } @@ -2379,7 +2389,8 @@ select { 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; margin: 0; width: 100%; @@ -2400,7 +2411,8 @@ select { 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; } @@ -2440,4 +2452,24 @@ select { 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; + } }