2018-04-22

This commit is contained in:
Visman 2018-04-22 16:57:23 +07:00
parent 22ac51d6b1
commit a12aa57e26
8 changed files with 212 additions and 58 deletions

View file

@ -125,6 +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');
} }
// только админ // только админ
@ -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/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');
} }
$uri = $_SERVER['REQUEST_URI']; $uri = $_SERVER['REQUEST_URI'];

View file

@ -109,6 +109,7 @@ class Validator
'absent' => [$this, 'vAbsent'], 'absent' => [$this, 'vAbsent'],
'array' => [$this, 'vArray'], 'array' => [$this, 'vArray'],
'checkbox' => [$this, 'vCheckbox'], 'checkbox' => [$this, 'vCheckbox'],
'date' => [$this, 'vDate'],
'file' => [$this, 'vFile'], 'file' => [$this, 'vFile'],
'image' => [$this, 'vImage'], 'image' => [$this, 'vImage'],
'in' => [$this, 'vIn'], 'in' => [$this, 'vIn'],
@ -786,4 +787,15 @@ class Validator
} }
return $value; 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;
}
} }

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('AdminHost', ['ip' => $ip])); $this->message = \ForkBB\__('Host info', $ip, $host, $this->c->Router->link('AdminShowUsersWithIP', ['ip' => $ip]));
$this->back = true; $this->back = true;
return $this; return $this;

View file

@ -31,6 +31,60 @@ class Users extends Admin
return $onlyKeys ? \array_keys($groups) : $groups; 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'); $this->c->Lang->load('admin_users');
$v = null; $data = [];
if ('POST' === $method) { if ('POST' === $method) {
$v = $this->c->Validator->reset()
->addRules([
'token' => 'token:AdminUsers',
'ip' => 'required',
]);
if ($v->validation($_POST)) {
$ip = \filter_var($v->ip, \FILTER_VALIDATE_IP);
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() $v = $this->c->Validator->reset()
->addValidators([ ->addValidators([
'check_message' => [$this, 'vCheckMessage'],
])->addRules([ ])->addRules([
'token' => 'token:AdminMaintenance', 'token' => 'token:AdminUsers',
'o_maintenance' => 'required|integer|in:0,1', 'username' => 'string|max:25',
'o_maintenance_message' => 'string:trim|max:65000 bytes|check_message', '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([ ])->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([ ])->addArguments([
])->addMessages([ ])->addMessages([
]); ]);
if ($v->validation($_POST)) { if ($v->validation($_POST)) {
$this->c->DB->beginTransaction(); $filters = $v->getData();
unset($filters['token']);
$this->c->config->o_maintenance = $v->o_maintenance; $filters = \base64_encode(\json_encode($filters));
$this->c->config->o_maintenance_message = $v->o_maintenance_message; $hash = $this->c->Secury->hash($filters);
$this->c->config->save(); return $this->c->Redirect->page('AdminShowUsersWithFilter', ['filters' => $filters, 'hash' => $hash]);
$this->c->DB->commit();
return $this->c->Redirect->page('AdminMaintenance')->message('Data updated redirect');
} }
$this->fIswev = $v->getErrors(); $this->fIswev = $v->getErrors();
$data = $v->getData();
}
} }
$this->nameTpl = 'admin/users'; $this->nameTpl = 'admin/users';
$this->aIndex = 'users'; $this->aIndex = 'users';
$this->titles = \ForkBB\__('Users'); $this->titles = \ForkBB\__('Users');
$this->formSearch = $this->formSearch($v); $this->formSearch = $this->formSearch($data);
if ($this->user->isAdmin) { if ($this->user->isAdmin) {
$this->formIP = $this->formIP($v); $this->formIP = $this->formIP($data);
} }
return $this; return $this;
@ -87,11 +193,11 @@ class Users extends Admin
/** /**
* Создает массив данных для формы поиска * Создает массив данных для формы поиска
* *
* @param mixed $v * @param array $data
* *
* @return array * @return array
*/ */
protected function formSearch($v) protected function formSearch(array $data)
{ {
$form = [ $form = [
'action' => $this->c->Router->link('AdminUsers'), 'action' => $this->c->Router->link('AdminUsers'),
@ -120,25 +226,25 @@ class Users extends Admin
'type' => 'text', 'type' => 'text',
'maxlength' => 25, 'maxlength' => 25,
'caption' => \ForkBB\__('Username label'), 'caption' => \ForkBB\__('Username label'),
'value' => isset($v->username) ? $v->username : null, 'value' => isset($data['username']) ? $data['username'] : null,
]; ];
$fields['email'] = [ $fields['email'] = [
'type' => 'text', 'type' => 'text',
'maxlength' => 80, 'maxlength' => 80,
'caption' => \ForkBB\__('E-mail address label'), 'caption' => \ForkBB\__('E-mail address label'),
'value' => isset($v->email) ? $v->email : null, 'value' => isset($data['email']) ? $data['email'] : null,
]; ];
$fields['title'] = [ $fields['title'] = [
'type' => 'text', 'type' => 'text',
'maxlength' => 50, 'maxlength' => 50,
'caption' => \ForkBB\__('Title label'), 'caption' => \ForkBB\__('Title label'),
'value' => isset($v->title) ? $v->title : null, 'value' => isset($data['title']) ? $data['title'] : null,
]; ];
$fields['realname'] = [ $fields['realname'] = [
'type' => 'text', 'type' => 'text',
'maxlength' => 40, 'maxlength' => 40,
'caption' => \ForkBB\__('Real name label'), 'caption' => \ForkBB\__('Real name label'),
'value' => isset($v->realname) ? $v->realname : null, 'value' => isset($data['realname']) ? $data['realname'] : null,
]; ];
$genders = [ $genders = [
0 => \ForkBB\__('Do not display'), 0 => \ForkBB\__('Do not display'),
@ -148,7 +254,7 @@ class Users extends Admin
$fields['gender'] = [ $fields['gender'] = [
# 'class' => 'block', # 'class' => 'block',
'type' => 'radio', 'type' => 'radio',
'value' => isset($v->gender) ? $v->gender : -1, 'value' => isset($data['gender']) ? $data['gender'] : -1,
'values' => $genders, 'values' => $genders,
'caption' => \ForkBB\__('Gender label'), 'caption' => \ForkBB\__('Gender label'),
]; ];
@ -157,25 +263,25 @@ class Users extends Admin
'type' => 'text', 'type' => 'text',
'maxlength' => 100, 'maxlength' => 100,
'caption' => \ForkBB\__('Website label'), 'caption' => \ForkBB\__('Website label'),
'value' => isset($v->url) ? $v->url : null, 'value' => isset($data['url']) ? $data['url'] : null,
]; ];
$fields['location'] = [ $fields['location'] = [
'type' => 'text', 'type' => 'text',
'maxlength' => 30, 'maxlength' => 30,
'caption' => \ForkBB\__('Location label'), 'caption' => \ForkBB\__('Location label'),
'value' => isset($v->location) ? $v->location : null, 'value' => isset($data['location']) ? $data['location'] : null,
]; ];
$fields['signature'] = [ $fields['signature'] = [
'type' => 'text', 'type' => 'text',
'maxlength' => 512, 'maxlength' => 512,
'caption' => \ForkBB\__('Signature label'), 'caption' => \ForkBB\__('Signature label'),
'value' => isset($v->signature) ? $v->signature : null, 'value' => isset($data['signature']) ? $data['signature'] : null,
]; ];
$fields['admin_note'] = [ $fields['admin_note'] = [
'type' => 'text', 'type' => 'text',
'maxlength' => 30, 'maxlength' => 30,
'caption' => \ForkBB\__('Admin note label'), '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'] = [ $fields['between1'] = [
'class' => 'between', 'class' => 'between',
@ -186,7 +292,7 @@ class Users extends Admin
'class' => 'bstart', 'class' => 'bstart',
'min' => 0, 'min' => 0,
'max' => 9999999999, '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'), 'caption' => \ForkBB\__('Posts label'),
]; ];
$fields['num_posts_2'] = [ $fields['num_posts_2'] = [
@ -194,7 +300,7 @@ class Users extends Admin
'class' => 'bend', 'class' => 'bend',
'min' => 0, 'min' => 0,
'max' => 9999999999, '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[] = [ $fields[] = [
'type' => 'endwrap', 'type' => 'endwrap',
@ -207,14 +313,14 @@ class Users extends Admin
'class' => 'bstart', 'class' => 'bstart',
'type' => 'text', 'type' => 'text',
'maxlength' => 100, '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'), 'caption' => \ForkBB\__('Last post label'),
]; ];
$fields['last_post_2'] = [ $fields['last_post_2'] = [
'class' => 'bend', 'class' => 'bend',
'type' => 'text', 'type' => 'text',
'maxlength' => 100, '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[] = [ $fields[] = [
'type' => 'endwrap', 'type' => 'endwrap',
@ -227,14 +333,14 @@ class Users extends Admin
'class' => 'bstart', 'class' => 'bstart',
'type' => 'text', 'type' => 'text',
'maxlength' => 100, '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'), 'caption' => \ForkBB\__('Last visit label'),
]; ];
$fields['last_visit_2'] = [ $fields['last_visit_2'] = [
'class' => 'bend', 'class' => 'bend',
'type' => 'text', 'type' => 'text',
'maxlength' => 100, '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[] = [ $fields[] = [
'type' => 'endwrap', 'type' => 'endwrap',
@ -247,14 +353,14 @@ class Users extends Admin
'class' => 'bstart', 'class' => 'bstart',
'type' => 'text', 'type' => 'text',
'maxlength' => 100, 'maxlength' => 100,
'value' => isset($v->registered_1) ? $v->registered_1 : null, 'value' => isset($data['registered_1']) ? $data['registered_1'] : null,
'caption' => \ForkBB\__('Registered label'), 'caption' => \ForkBB\__('Registered label'),
]; ];
$fields['registered_2'] = [ $fields['registered_2'] = [
'class' => 'bend', 'class' => 'bend',
'type' => 'text', 'type' => 'text',
'maxlength' => 100, 'maxlength' => 100,
'value' => isset($v->registered_2) ? $v->registered_2 : null, 'value' => isset($data['registered_2']) ? $data['registered_2'] : null,
]; ];
$fields[] = [ $fields[] = [
'type' => 'endwrap', 'type' => 'endwrap',
@ -280,7 +386,7 @@ class Users extends Admin
'last_visit' => \ForkBB\__('Order by last visit'), 'last_visit' => \ForkBB\__('Order by last visit'),
'registered' => \ForkBB\__('Order by registered'), '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'), 'caption' => \ForkBB\__('Order by label'),
]; ];
$fields['direction'] = [ $fields['direction'] = [
@ -290,7 +396,7 @@ class Users extends Admin
'ASC' => \ForkBB\__('Ascending'), 'ASC' => \ForkBB\__('Ascending'),
'DESC' => \ForkBB\__('Descending'), 'DESC' => \ForkBB\__('Descending'),
], ],
'value' => isset($v->direction) ? $v->direction : 'DESC', 'value' => isset($data['direction']) ? $data['direction'] : 'DESC',
]; ];
$fields[] = [ $fields[] = [
'type' => 'endwrap', 'type' => 'endwrap',
@ -298,7 +404,7 @@ class Users extends Admin
$fields['user_group'] = [ $fields['user_group'] = [
'type' => 'select', 'type' => 'select',
'options' => $this->groups(), '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'), 'caption' => \ForkBB\__('User group label'),
]; ];
@ -313,11 +419,11 @@ class Users extends Admin
/** /**
* Создает массив данных для формы поиска по IP * Создает массив данных для формы поиска по IP
* *
* @param mixed $v * @param array $data
* *
* @return array * @return array
*/ */
protected function formIP($v) protected function formIP(array $data)
{ {
$form = [ $form = [
'action' => $this->c->Router->link('AdminUsers'), 'action' => $this->c->Router->link('AdminUsers'),
@ -338,7 +444,7 @@ class Users extends Admin
'type' => 'text', 'type' => 'text',
'maxlength' => 49, 'maxlength' => 49,
'caption' => \ForkBB\__('IP address label'), 'caption' => \ForkBB\__('IP address label'),
'value' => isset($v->ip) ? $v->ip : null, 'value' => isset($data['ip']) ? $data['ip'] : null,
'required' => true, 'required' => true,
]; ];
$form['sets']['ip'] = [ $form['sets']['ip'] = [

View file

@ -76,9 +76,9 @@ class Userlist extends Page
} else { } else {
$filters['group_id'] = ['=', $v->group]; $filters['group_id'] = ['=', $v->group];
} }
if (null !== $v->name && '*' !== $v->name) { # if (null !== $v->name && '*' !== $v->name) {
$filters['username'] = ['LIKE', $v->name]; # $filters['username'] = ['LIKE', $v->name];
} # }
$order = $v->sort ? [$v->sort => $v->dir] : []; $order = $v->sort ? [$v->sort => $v->dir] : [];

View file

@ -26,10 +26,10 @@ class Filter extends Action
foreach ($order as $field => $dir) { foreach ($order as $field => $dir) {
if (! isset($fields[$field])) { 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) { 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}"; $orderBy[] = "u.{$field} {$dir}";
} }
@ -43,20 +43,54 @@ class Filter extends Action
foreach ($filters as $field => $rule) { foreach ($filters as $field => $rule) {
if (! isset($fields[$field])) { if (! isset($fields[$field])) {
throw new InvalidArgumentException('No sorting field found'); throw new InvalidArgumentException("The '{$field}' field is not found");
} }
switch ($rule[0]) { 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 '=':
case '!=': case '!=':
$where[] = "u.{$field}{$rule[0]}?{$fields[$field]}"; $where[] = "u.{$field}{$rule[0]}?{$fields[$field]}";
$vars[] = $rule[1]; $vars[] = $rule[1];
break; break;
case 'LIKE': case 'BETWEEN':
$where[] = "u.{$field} LIKE ?{$fields[$field]}"; // если и min, и max
$vars[] = \str_replace(['%', '*', '_'], ['\\%', '%', '\\_'], $rule[1]); 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; break;
default: default:
throw new InvalidArgumentException('The condition is not clear'); throw new InvalidArgumentException('The condition is not defined');
} }
} }

View file

@ -13,4 +13,4 @@ msgstr ""
"Language: en\n" "Language: en\n"
msgid "Host info" msgid "Host info"
msgstr "The IP address is: %1$s<br>The host name is: %2$s<br><br><a href=\"\">Show more users for this IP</a>" msgstr "The IP address is: %1$s<br>The host name is: %2$s<br><br><a href=\"%3$s\">Show more users for this IP</a>"

View file

@ -13,4 +13,4 @@ msgstr ""
"Language: ru\n" "Language: ru\n"
msgid "Host info" msgid "Host info"
msgstr "IP адрес: %1$s<br>Имя хоста: %2$s<br><br><a href=\"\">Показать еще информацию по IP</a>" msgstr "IP адрес: %1$s<br>Имя хоста: %2$s<br><br><a href=\"%3$s\">Показать еще информацию по IP</a>"