Visman 6 anni fa
parent
commit
942b0f9afe

+ 3 - 2
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'];

+ 1 - 1
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;

+ 118 - 14
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;
+    }
 }

+ 31 - 0
app/Models/Post/UserStat.php

@@ -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);
+    }
+}

+ 1 - 0
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,

+ 43 - 11
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;
+  }
 }