From a12aa57e263f2cafdaec2b08881e9db3f7cb270f Mon Sep 17 00:00:00 2001 From: Visman Date: Sun, 22 Apr 2018 16:57:23 +0700 Subject: [PATCH] 2018-04-22 --- app/Controllers/Routing.php | 2 + app/Core/Validator.php | 12 ++ app/Models/Pages/Admin/Host.php | 2 +- app/Models/Pages/Admin/Users.php | 196 ++++++++++++++++++++++++------- app/Models/Pages/Userlist.php | 6 +- app/Models/User/Filter.php | 48 ++++++-- app/lang/English/admin_host.po | 2 +- app/lang/Russian/admin_host.po | 2 +- 8 files changed, 212 insertions(+), 58 deletions(-) diff --git a/app/Controllers/Routing.php b/app/Controllers/Routing.php index 58a0d9d5..3d63906a 100644 --- a/app/Controllers/Routing.php +++ b/app/Controllers/Routing.php @@ -125,6 +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'); } // только админ @@ -149,6 +150,7 @@ 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'); } $uri = $_SERVER['REQUEST_URI']; diff --git a/app/Core/Validator.php b/app/Core/Validator.php index d1213332..835cf64e 100644 --- a/app/Core/Validator.php +++ b/app/Core/Validator.php @@ -109,6 +109,7 @@ class Validator 'absent' => [$this, 'vAbsent'], 'array' => [$this, 'vArray'], 'checkbox' => [$this, 'vCheckbox'], + 'date' => [$this, 'vDate'], 'file' => [$this, 'vFile'], 'image' => [$this, 'vImage'], 'in' => [$this, 'vIn'], @@ -786,4 +787,15 @@ class Validator } return $value; } + + public function vDate(Validator $v, $value) + { + if (null === $value) { + return null; + } elseif (! \is_string($value) || false === \strtotime($value . ' UTC')) { + $v->addError('The :alias does not contain a date'); + return \is_string($value) ? $value : null; + } + return $value; + } } diff --git a/app/Models/Pages/Admin/Host.php b/app/Models/Pages/Admin/Host.php index 15c5a6cc..e2b7c65b 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('AdminHost', ['ip' => $ip])); + $this->message = \ForkBB\__('Host info', $ip, $host, $this->c->Router->link('AdminShowUsersWithIP', ['ip' => $ip])); $this->back = true; return $this; diff --git a/app/Models/Pages/Admin/Users.php b/app/Models/Pages/Admin/Users.php index 3207fd40..f66ffa4c 100644 --- a/app/Models/Pages/Admin/Users.php +++ b/app/Models/Pages/Admin/Users.php @@ -31,6 +31,60 @@ class Users extends Admin return $onlyKeys ? \array_keys($groups) : $groups; } + /** + * Подготавливает данные для шаблона найденных по фильтру пользователей + * + * @param array $args + * @param string $method + * + * @return Page + */ + public function filter(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)) + ) { + return $this->c->Message->message('Bad request'); + } + + $this->c->Lang->load('admin_users'); + + $order = [ + $data['order_by'] => $data['direction'], + ]; + $filters = []; + + if ($data['user_group'] > -1) { + $filters['group_id'] = ['=', $data['user_group']]; + } + + unset($data['order_by'], $data['direction'], $data['user_group']); + + foreach ($data as $field => $value) { + $key = 1; + $type = '='; + + if (\preg_match('%^(.+?)_(1|2)$%', $field, $matches)) { + $type = 'BETWEEN'; + $field = $matches[1]; + $key = $matches[2]; + + if (\is_string($value)) { + $value = \strtotime($value . ' UTC'); + } + } elseif (\is_string($value)) { + $type = 'LIKE'; + } + + $filters[$field][0] = $type; + $filters[$field][$key] = $value; + } + + $ids = $this->c->users->filter($filters, $order); + + exit(var_dump($ids, $order, $filters)); + } + /** * Подготавливает данные для шаблона поиска пользователей * @@ -43,42 +97,94 @@ class Users extends Admin { $this->c->Lang->load('admin_users'); - $v = null; + $data = []; + if ('POST' === $method) { $v = $this->c->Validator->reset() - ->addValidators([ - 'check_message' => [$this, 'vCheckMessage'], - ])->addRules([ - 'token' => 'token:AdminMaintenance', - 'o_maintenance' => 'required|integer|in:0,1', - 'o_maintenance_message' => 'string:trim|max:65000 bytes|check_message', - ])->addAliases([ - ])->addArguments([ - ])->addMessages([ + ->addRules([ + 'token' => 'token:AdminUsers', + 'ip' => 'required', ]); if ($v->validation($_POST)) { - $this->c->DB->beginTransaction(); + $ip = \filter_var($v->ip, \FILTER_VALIDATE_IP); - $this->c->config->o_maintenance = $v->o_maintenance; - $this->c->config->o_maintenance_message = $v->o_maintenance_message; - $this->c->config->save(); + if (false === $ip) { + $this->fIswev = ['v', \ForkBB\__('Bad IP message')]; + $data = $v->getData(); + } else { + return $this->c->Redirect->page('AdminShowUsersWithIP', ['ip' => $ip]); + } + } else { + $v = $this->c->Validator->reset() + ->addValidators([ + ])->addRules([ + 'token' => 'token:AdminUsers', + 'username' => 'string|max:25', + 'email' => 'string|max:80', + 'title' => 'string|max:50', + 'realname' => 'string|max:40', + 'gender' => 'integer|in:0,1,2', + 'url' => 'string|max:100', + 'location' => 'string|max:30', + 'signature' => 'string|max:512', + 'admin_note' => 'string|max:30', + 'num_posts_1' => 'integer|min:0|max:9999999999', + 'num_posts_2' => 'integer|min:0|max:9999999999', + 'last_post_1' => 'date', + 'last_post_2' => 'date', + 'last_visit_1' => 'date', + 'last_visit_2' => 'date', + 'registered_1' => 'date', + 'registered_2' => 'date', + 'order_by' => 'required|string|in:username,email,num_posts,last_post,last_visit,registered', + 'direction' => 'required|string|in:ASC,DESC', + 'user_group' => 'required|integer|in:' . \implode(',', $this->groups(true)), + ])->addAliases([ + 'username' => 'Username label', + 'email' => 'E-mail address label', + 'title' => 'Title label', + 'realname' => 'Real name label', + 'gender' => 'Gender label', + 'url' => 'Website label', + 'location' => 'Location label', + 'signature' => 'Signature label', + 'admin_note' => 'Admin note label', + 'num_posts_1' => 'Posts label', + 'num_posts_2' => 'Posts label', + 'last_post_1' => 'Last post label', + 'last_post_2' => 'Last post label', + 'last_visit_1' => 'Last visit label', + 'last_visit_2' => 'Last visit label', + 'registered_1' => 'Registered label', + 'registered_2' => 'Registered label', + 'order_by' => 'Order by label', +# 'direction' => , + 'user_group' => 'User group label', + ])->addArguments([ + ])->addMessages([ + ]); - $this->c->DB->commit(); + if ($v->validation($_POST)) { + $filters = $v->getData(); + 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('AdminMaintenance')->message('Data updated redirect'); + $this->fIswev = $v->getErrors(); + $data = $v->getData(); } - - $this->fIswev = $v->getErrors(); } $this->nameTpl = 'admin/users'; $this->aIndex = 'users'; $this->titles = \ForkBB\__('Users'); - $this->formSearch = $this->formSearch($v); + $this->formSearch = $this->formSearch($data); if ($this->user->isAdmin) { - $this->formIP = $this->formIP($v); + $this->formIP = $this->formIP($data); } return $this; @@ -87,11 +193,11 @@ class Users extends Admin /** * Создает массив данных для формы поиска * - * @param mixed $v + * @param array $data * * @return array */ - protected function formSearch($v) + protected function formSearch(array $data) { $form = [ 'action' => $this->c->Router->link('AdminUsers'), @@ -120,25 +226,25 @@ class Users extends Admin 'type' => 'text', 'maxlength' => 25, 'caption' => \ForkBB\__('Username label'), - 'value' => isset($v->username) ? $v->username : null, + 'value' => isset($data['username']) ? $data['username'] : null, ]; $fields['email'] = [ 'type' => 'text', 'maxlength' => 80, 'caption' => \ForkBB\__('E-mail address label'), - 'value' => isset($v->email) ? $v->email : null, + 'value' => isset($data['email']) ? $data['email'] : null, ]; $fields['title'] = [ 'type' => 'text', 'maxlength' => 50, 'caption' => \ForkBB\__('Title label'), - 'value' => isset($v->title) ? $v->title : null, + 'value' => isset($data['title']) ? $data['title'] : null, ]; $fields['realname'] = [ 'type' => 'text', 'maxlength' => 40, 'caption' => \ForkBB\__('Real name label'), - 'value' => isset($v->realname) ? $v->realname : null, + 'value' => isset($data['realname']) ? $data['realname'] : null, ]; $genders = [ 0 => \ForkBB\__('Do not display'), @@ -148,7 +254,7 @@ class Users extends Admin $fields['gender'] = [ # 'class' => 'block', 'type' => 'radio', - 'value' => isset($v->gender) ? $v->gender : -1, + 'value' => isset($data['gender']) ? $data['gender'] : -1, 'values' => $genders, 'caption' => \ForkBB\__('Gender label'), ]; @@ -157,25 +263,25 @@ class Users extends Admin 'type' => 'text', 'maxlength' => 100, 'caption' => \ForkBB\__('Website label'), - 'value' => isset($v->url) ? $v->url : null, + 'value' => isset($data['url']) ? $data['url'] : null, ]; $fields['location'] = [ 'type' => 'text', 'maxlength' => 30, 'caption' => \ForkBB\__('Location label'), - 'value' => isset($v->location) ? $v->location : null, + 'value' => isset($data['location']) ? $data['location'] : null, ]; $fields['signature'] = [ 'type' => 'text', 'maxlength' => 512, 'caption' => \ForkBB\__('Signature label'), - 'value' => isset($v->signature) ? $v->signature : null, + 'value' => isset($data['signature']) ? $data['signature'] : null, ]; $fields['admin_note'] = [ 'type' => 'text', 'maxlength' => 30, 'caption' => \ForkBB\__('Admin note label'), - 'value' => isset($v->admin_note) ? $v->admin_note : null, + 'value' => isset($data['admin_note']) ? $data['admin_note'] : null, ]; $fields['between1'] = [ 'class' => 'between', @@ -186,7 +292,7 @@ class Users extends Admin 'class' => 'bstart', 'min' => 0, 'max' => 9999999999, - 'value' => isset($v->num_posts_1) ? $v->num_posts_1 : null, + 'value' => isset($data['num_posts_1']) ? $data['num_posts_1'] : null, 'caption' => \ForkBB\__('Posts label'), ]; $fields['num_posts_2'] = [ @@ -194,7 +300,7 @@ class Users extends Admin 'class' => 'bend', 'min' => 0, 'max' => 9999999999, - 'value' => isset($v->num_posts_2) ? $v->num_posts_2 : null, + 'value' => isset($data['num_posts_2']) ? $data['num_posts_2'] : null, ]; $fields[] = [ 'type' => 'endwrap', @@ -207,14 +313,14 @@ class Users extends Admin 'class' => 'bstart', 'type' => 'text', 'maxlength' => 100, - 'value' => isset($v->last_post_1) ? $v->last_post_1 : null, + 'value' => isset($data['last_post_1']) ? $data['last_post_1'] : null, 'caption' => \ForkBB\__('Last post label'), ]; $fields['last_post_2'] = [ 'class' => 'bend', 'type' => 'text', 'maxlength' => 100, - 'value' => isset($v->last_post_2) ? $v->last_post_2 : null, + 'value' => isset($data['last_post_2']) ? $data['last_post_2'] : null, ]; $fields[] = [ 'type' => 'endwrap', @@ -227,14 +333,14 @@ class Users extends Admin 'class' => 'bstart', 'type' => 'text', 'maxlength' => 100, - 'value' => isset($v->last_visit_1) ? $v->last_visit_1 : null, + 'value' => isset($data['last_visit_1']) ? $data['last_visit_1'] : null, 'caption' => \ForkBB\__('Last visit label'), ]; $fields['last_visit_2'] = [ 'class' => 'bend', 'type' => 'text', 'maxlength' => 100, - 'value' => isset($v->last_visit_2) ? $v->last_visit_2 : null, + 'value' => isset($data['last_visit_2']) ? $data['last_visit_2'] : null, ]; $fields[] = [ 'type' => 'endwrap', @@ -247,14 +353,14 @@ class Users extends Admin 'class' => 'bstart', 'type' => 'text', 'maxlength' => 100, - 'value' => isset($v->registered_1) ? $v->registered_1 : null, + 'value' => isset($data['registered_1']) ? $data['registered_1'] : null, 'caption' => \ForkBB\__('Registered label'), ]; $fields['registered_2'] = [ 'class' => 'bend', 'type' => 'text', 'maxlength' => 100, - 'value' => isset($v->registered_2) ? $v->registered_2 : null, + 'value' => isset($data['registered_2']) ? $data['registered_2'] : null, ]; $fields[] = [ 'type' => 'endwrap', @@ -280,7 +386,7 @@ class Users extends Admin 'last_visit' => \ForkBB\__('Order by last visit'), 'registered' => \ForkBB\__('Order by registered'), ], - 'value' => isset($v->order_by) ? $v->order_by : 'registered', + 'value' => isset($data['order_by']) ? $data['order_by'] : 'registered', 'caption' => \ForkBB\__('Order by label'), ]; $fields['direction'] = [ @@ -290,7 +396,7 @@ class Users extends Admin 'ASC' => \ForkBB\__('Ascending'), 'DESC' => \ForkBB\__('Descending'), ], - 'value' => isset($v->direction) ? $v->direction : 'DESC', + 'value' => isset($data['direction']) ? $data['direction'] : 'DESC', ]; $fields[] = [ 'type' => 'endwrap', @@ -298,7 +404,7 @@ class Users extends Admin $fields['user_group'] = [ 'type' => 'select', 'options' => $this->groups(), - 'value' => isset($v->user_group) ? $v->user_group : -1, + 'value' => isset($data['user_group']) ? $data['user_group'] : -1, 'caption' => \ForkBB\__('User group label'), ]; @@ -313,11 +419,11 @@ class Users extends Admin /** * Создает массив данных для формы поиска по IP * - * @param mixed $v + * @param array $data * * @return array */ - protected function formIP($v) + protected function formIP(array $data) { $form = [ 'action' => $this->c->Router->link('AdminUsers'), @@ -338,7 +444,7 @@ class Users extends Admin 'type' => 'text', 'maxlength' => 49, 'caption' => \ForkBB\__('IP address label'), - 'value' => isset($v->ip) ? $v->ip : null, + 'value' => isset($data['ip']) ? $data['ip'] : null, 'required' => true, ]; $form['sets']['ip'] = [ diff --git a/app/Models/Pages/Userlist.php b/app/Models/Pages/Userlist.php index 6a91811d..069b3deb 100644 --- a/app/Models/Pages/Userlist.php +++ b/app/Models/Pages/Userlist.php @@ -76,9 +76,9 @@ class Userlist extends Page } else { $filters['group_id'] = ['=', $v->group]; } - if (null !== $v->name && '*' !== $v->name) { - $filters['username'] = ['LIKE', $v->name]; - } +# if (null !== $v->name && '*' !== $v->name) { +# $filters['username'] = ['LIKE', $v->name]; +# } $order = $v->sort ? [$v->sort => $v->dir] : []; diff --git a/app/Models/User/Filter.php b/app/Models/User/Filter.php index 1472329d..3d9f7929 100644 --- a/app/Models/User/Filter.php +++ b/app/Models/User/Filter.php @@ -26,10 +26,10 @@ class Filter extends Action foreach ($order as $field => $dir) { if (! isset($fields[$field])) { - throw new InvalidArgumentException('No sorting field found'); + throw new InvalidArgumentException("The '{$field}' field is not found"); } if ('ASC' !== $dir && 'DESC' !== $dir) { - throw new InvalidArgumentException('The sorting order is not clear'); + throw new InvalidArgumentException('The sort direction is not defined'); } $orderBy[] = "u.{$field} {$dir}"; } @@ -43,20 +43,54 @@ class Filter extends Action foreach ($filters as $field => $rule) { if (! isset($fields[$field])) { - throw new InvalidArgumentException('No sorting field found'); + throw new InvalidArgumentException("The '{$field}' field is not found"); } switch ($rule[0]) { + case 'LIKE': + if (false !== \strpos($rule[1], '*')) { + // кроме * есть другие символы + if ('' != \trim($rule[1], '*')) { + $where[] = "u.{$field} LIKE ?{$fields[$field]}"; + $vars[] = \str_replace(['%', '*', '_'], ['\\%', '%', '\\_'], $rule[1]); + } + break; + } + $rule[0] = '='; case '=': case '!=': $where[] = "u.{$field}{$rule[0]}?{$fields[$field]}"; $vars[] = $rule[1]; break; - case 'LIKE': - $where[] = "u.{$field} LIKE ?{$fields[$field]}"; - $vars[] = \str_replace(['%', '*', '_'], ['\\%', '%', '\\_'], $rule[1]); + case 'BETWEEN': + // если и min, и max + if (isset($rule[1], $rule[2])) { + // min меньше max + if ($rule[1] < $rule[2]) { + $where[] = "u.{$field} BETWEEN ?{$fields[$field]} AND ?{$fields[$field]}"; + $vars[] = $rule[1]; + $vars[] = $rule[2]; + // min больше max O_o + } elseif ($rule[1] > $rule[2]) { + $where[] = "u.{$field} NOT BETWEEN ?{$fields[$field]} AND ?{$fields[$field]}"; + $vars[] = $rule[1]; + $vars[] = $rule[2]; + // min равен max :) + } else { + $where[] = "u.{$field}=?{$fields[$field]}"; + $vars[] = $rule[1]; + } + // есть только min + } elseif (isset($rule[1])) { + $where[] = "u.{$field}>=?{$fields[$field]}"; + $vars[] = $rule[1]; + // есть только max + } elseif (isset($rule[2])) { + $where[] = "u.{$field}<=?{$fields[$field]}"; + $vars[] = $rule[2]; + } break; default: - throw new InvalidArgumentException('The condition is not clear'); + throw new InvalidArgumentException('The condition is not defined'); } } diff --git a/app/lang/English/admin_host.po b/app/lang/English/admin_host.po index b6f53039..1561f2fd 100644 --- a/app/lang/English/admin_host.po +++ b/app/lang/English/admin_host.po @@ -13,4 +13,4 @@ msgstr "" "Language: en\n" msgid "Host info" -msgstr "The IP address is: %1$s
The host name is: %2$s

Show more users for this IP" +msgstr "The IP address is: %1$s
The host name is: %2$s

Show more users for this IP" diff --git a/app/lang/Russian/admin_host.po b/app/lang/Russian/admin_host.po index b5b9bd16..cb010c9d 100644 --- a/app/lang/Russian/admin_host.po +++ b/app/lang/Russian/admin_host.po @@ -13,4 +13,4 @@ msgstr "" "Language: ru\n" msgid "Host info" -msgstr "IP адрес: %1$s
Имя хоста: %2$s

Показать еще информацию по IP" +msgstr "IP адрес: %1$s
Имя хоста: %2$s

Показать еще информацию по IP"