Improved the warning management UI.

This commit is contained in:
flash 2018-12-27 22:01:57 +01:00
parent a34a546ace
commit e1c2c97669
8 changed files with 228 additions and 76 deletions
assets/less/classes
public/manage
src/Users
templates

View file

@ -8,6 +8,10 @@
overflow: hidden;
word-wrap: break-word;
&--lazy {
margin-bottom: 2px;
}
&__title {
display: block;
overflow: hidden;

View file

@ -19,6 +19,10 @@
--accent-colour: #c33;
}
&--extendo {
margin: 4px;
}
&__background {
background-color: var(--accent-colour);
position: absolute;
@ -77,4 +81,57 @@
opacity: 1;
}
}
&__tools {
display: flex;
padding-bottom: 1px;
@media (max-width: @site-mobile-width) {
flex-direction: column;
}
}
&__options {
flex: 1 1 auto;
display: flex;
justify-content: flex-end;
align-items: center;
@media (max-width: @site-mobile-width) {
justify-content: flex-start;
}
}
&__option {
padding: 2px 5px;
color: inherit;
text-decoration: none;
}
&__user {
display: flex;
padding: 2px;
min-width: 300px;
&__avatar {
width: 20px;
height: 20px;
}
&__username {
padding: 0 5px;
min-width: 60px;
}
&__ip {
display: inline-flex;
&:before {
content: "(";
}
&:after {
content: ")";
}
}
}
}

View file

@ -111,10 +111,6 @@ switch ($_GET['v'] ?? null) {
tpl_var('permissions', $permissions = manage_perms_list(perms_get_user_raw($userId)));
}
if ($canManageWarnings) {
tpl_var('warning_types', $warnTypes = user_warning_get_types());
}
if ($isPostRequest) {
if (!csrf_verify('users_edit', $_POST['csrf'] ?? '')) {
echo 'csrf err';
@ -238,18 +234,6 @@ switch ($_GET['v'] ?? null) {
}
}
if (!empty($warnTypes) && !empty($_POST['warning']) && is_array($_POST['warning'])) {
user_warning_add(
$userId,
user_get_last_ip($userId),
user_session_current('user_id'),
ip_remote_address(),
$_POST['warning']['type'],
$_POST['warning']['note'],
$_POST['warning']['private']
);
}
header("Location: ?v=view&u={$manageUser['user_id']}");
break;
}
@ -290,13 +274,12 @@ switch ($_GET['v'] ?? null) {
$getManageRoles->bindValue('take', $rolesTake);
$manageRoles = $getManageRoles->execute() ? $getManageRoles->fetchAll() : [];
tpl_vars([
echo tpl_render('manage.users.roles', [
'manage_roles' => $manageRoles,
'manage_roles_count' => $manageRolesCount,
'manage_roles_range' => $rolesTake,
'manage_roles_offset' => $queryQffset,
]);
echo tpl_render('manage.users.roles');
break;
case 'role':
@ -485,10 +468,49 @@ switch ($_GET['v'] ?? null) {
break;
}
if (!empty($_POST['warning']) && is_array($_POST['warning'])) {
$warningType = (int)($_POST['warning']['type'] ?? 0);
if (user_warning_type_is_valid($warningType)) {
$warningsUser = max(0, (int)($_POST['warning']['user'] ?? 0));
$warningId = user_warning_add(
$warningsUser,
user_get_last_ip($warningsUser),
user_session_current('user_id'),
ip_remote_address(),
$warningType,
$_POST['warning']['note'],
$_POST['warning']['private']
);
}
} elseif (!empty($_POST['lookup']) && is_string($_POST['lookup'])) {
$userId = user_id_from_username($_POST['lookup']);
header("Location: ?v=warnings&u={$userId}");
return;
} elseif (!empty($_GET['m'])) {
$warningId = (int)($_GET['w'] ?? 0);
$modeName = $_GET['m'] ?? '';
$csrfRealm = "warning-{$modeName}[{$warningId}]";
if (csrf_verify($csrfRealm, $_GET['c'] ?? '')) {
switch ($modeName) {
case 'delete':
user_warning_remove($warningId);
break;
}
header('Location: ' . ($_SERVER['HTTP_REFERER'] ?? '?m=warnings' . (empty($_GET['u']) ? '' : '&u=' . (int)($_GET['u']))));
return;
}
}
if (empty($warningsUser)) {
$warningsUser = max(0, (int)($_GET['u'] ?? 0));
}
$warningsCount = user_warning_global_count();
$warningsTake = 50;
$warningsOffset = max(0, (int)($_GET['o'] ?? 0));
$warningsList = user_warning_global_fetch($warningsOffset, $warningsTake);
$warningsList = user_warning_global_fetch($warningsOffset, $warningsTake, $warningsUser);
echo tpl_render('manage.users.warnings', [
'warnings' => [
@ -496,6 +518,9 @@ switch ($_GET['v'] ?? null) {
'take' => $warningsTake,
'offset' => $warningsOffset,
'list' => $warningsList,
'user_id' => $warningsUser,
'username' => user_username_from_id($warningsUser),
'types' => user_warning_get_types(),
],
]);
break;

View file

@ -138,6 +138,13 @@ function user_id_from_username(string $username): int
return $getId->execute() ? (int)$getId->fetchColumn() : 0;
}
function user_username_from_id(int $userId): string
{
$getName = db_prepare('SELECT `username` FROM `msz_users` WHERE `user_id` = :user_id');
$getName->bindValue('user_id', $userId);
return $getName->execute() ? $getName->fetchColumn() : '';
}
function user_bump_last_active(int $userId, string $ipAddress = null): void
{
$bumpUserLast = db_prepare('

View file

@ -54,6 +54,10 @@ function user_warning_add(
return -1;
}
if ($userId < 1) {
return -2;
}
$addWarning = db_prepare('
INSERT INTO `msz_user_warnings`
(`user_id`, `user_ip`, `issuer_id`, `issuer_ip`, `warning_type`, `warning_note`, `warning_note_private`)
@ -144,25 +148,34 @@ function user_warning_global_count(): int
return (int)$countWarnings->fetchColumn();
}
function user_warning_global_fetch(int $offset, int $take): array
function user_warning_global_fetch(int $offset = 0, int $take = 50, ?int $userId = null): array
{
$fetchWarnings = db_prepare('
SELECT
uw.`warning_id`, uw.`warning_created`, uw.`warning_type`, uw.`warning_note`,
uw.`warning_note_private`, uw.`user_id`, uw.`issuer_id`, uw.`warning_duration`,
TIMESTAMPDIFF(SECOND, uw.`warning_created`, uw.`warning_duration`) AS `warning_duration_secs`,
INET6_NTOA(uw.`user_ip`) AS `user_ip`, INET6_NTOA(uw.`issuer_ip`) AS `issuer_ip`,
iu.`username` AS `issuer_username`, wu.`username` AS `username`
FROM `msz_user_warnings` AS uw
LEFT JOIN `msz_users` AS iu
ON iu.`user_id` = uw.`issuer_id`
LEFT JOIN `msz_users` AS wu
ON wu.`user_id` = uw.`user_id`
ORDER BY uw.`warning_id` DESC
LIMIT :offset, :take
');
$fetchWarnings = db_prepare(sprintf(
'
SELECT
uw.`warning_id`, uw.`warning_created`, uw.`warning_type`, uw.`warning_note`,
uw.`warning_note_private`, uw.`user_id`, uw.`issuer_id`, uw.`warning_duration`,
TIMESTAMPDIFF(SECOND, uw.`warning_created`, uw.`warning_duration`) AS `warning_duration_secs`,
INET6_NTOA(uw.`user_ip`) AS `user_ip`, INET6_NTOA(uw.`issuer_ip`) AS `issuer_ip`,
iu.`username` AS `issuer_username`, wu.`username` AS `username`
FROM `msz_user_warnings` AS uw
LEFT JOIN `msz_users` AS iu
ON iu.`user_id` = uw.`issuer_id`
LEFT JOIN `msz_users` AS wu
ON wu.`user_id` = uw.`user_id`
%1$s
ORDER BY uw.`warning_id` DESC
LIMIT :offset, :take
',
$userId > 0 ? 'WHERE uw.`user_id` = :user_id' : ''
));
$fetchWarnings->bindValue('offset', $offset);
$fetchWarnings->bindValue('take', $take);
if ($userId > 0) {
$fetchWarnings->bindValue('user_id', $userId);
}
$warnings = $fetchWarnings->execute() ? $fetchWarnings->fetchAll(PDO::FETCH_ASSOC) : false;
return $warnings ? $warnings : [];
}

View file

@ -139,20 +139,6 @@
</form>
{% endif %}
{% if can_manage_warns %}
<form class="container" method="post" action="">
{{ container_title('Add Warning') }}
{{ input_csrf('users_edit') }}
{{ input_select('warning[type]', warning_types) }}
{{ input_text('warning[note]', '', '', 'text', 'Public note') }}
{{ input_text('warning[until]', '', ''|date('c'), 'datetime-local') }} (empty to set null)
<button class="input__button">Add</button><br>
<textarea class="input__textarea" name="warning[private]" placeholder="Private note"></textarea>
</form>
{% endif %}
{% if can_manage_users %}
{% if has_roles|length > 0 %}
<form method="post" action="" class="container">

View file

@ -1,18 +1,60 @@
{% extends 'manage/users/master.twig' %}
{% from 'macros.twig' import pagination, container_title %}
{% from 'user/macros.twig' import user_profile_warning %}
{% from '_layout/input.twig' import input_text, input_csrf, input_select, input_hidden %}
{% block manage_content %}
<div class="container">
<form class="container container--lazy" action="" method="post">
{{ container_title('<i class="fas fa-users fa-fw"></i> Filters') }}
{{ input_text('lookup', null, warnings.username, 'text', 'Enter a username') }}
<button class="input__button">Filter</button>
</form>
{% if warnings.user_id > 0 and warnings.username|length > 0 %}{# shittiest validation in the world, but it should work #}
<form class="container container--lazy" method="post" action="">
{{ container_title('<i class="fas fa-user-shield fa-fw"></i> Warn ' ~ warnings.username) }}
{{ input_csrf('users_edit') }}
{{ input_hidden('warning[user]', warnings.user_id) }}
{{ input_select('warning[type]', warnings.types) }}
{{ input_text('warning[note]', '', '', 'text', 'Public note') }}
{{ input_text('warning[until]', '', ''|date('c'), 'datetime-local') }} (empty to set null)
<button class="input__button">Add</button><br>
<textarea class="input__textarea" name="warning[private]" placeholder="Private note"></textarea>
</form>
{% endif %}
<div class="container container--lazy">
{{ container_title('<i class="fas fa-exclamation-circle fa-fw"></i> Warnings') }}
{% set warnpag = pagination(warnings.count, warnings.take, warnings.offset, '/manage/users.php?v=warnings') %}
{{ warnpag }}
<div class="profile__warnings__container">
<div class="profile__warning">
<div class="profile__warning profile__warning--extendo">
<div class="profile__warning__background"></div>
<div class="profile__warning__tools">
<div class="profile__warning__user">
<div class="profile__warning__user__username">
User
</div>
<div class="profile__warning__user__ip">
User IP
</div>
</div>
<div class="profile__warning__user">
<div class="profile__warning__user__username">
Issuer
</div>
<div class="profile__warning__user__ip">
Issuer IP
</div>
</div>
</div>
<div class="profile__warning__content">
<div class="profile__warning__type">
Type
@ -33,30 +75,8 @@
</div>
{% for warning in warnings.list %}
{{ user_profile_warning(warning) }}
{{ user_profile_warning(warning, true, true, true, true, csrf_token('warning-delete[%d]'|format(warning.warning_id))) }}
{% endfor %}
<div class="profile__warning">
<div class="profile__warning__background"></div>
<div class="profile__warning__content">
<div class="profile__warning__type">
Type
</div>
<div class="profile__warning__created">
Created
</div>
<div class="profile__warning__duration">
Expires
</div>
<div class="profile__warning__note">
Note
</div>
</div>
</div>
</div>
{{ warnpag }}

View file

@ -222,7 +222,7 @@
</div>
{% endmacro %}
{% macro user_profile_warning(warning, show_private_note) %}
{% macro user_profile_warning(warning, show_private_note, show_user, show_issuer, show_ips, delete_csrf) %}
{% if warning.warning_type == constant('MSZ_WARN_SILENCE') %}
{% set warning_text = 'Silence' %}
{% set warning_class = 'silence' %}
@ -237,9 +237,49 @@
{% set warning_class = 'note' %}
{% endif %}
<div class="profile__warning profile__warning--{{ warning_class }}">
<div class="profile__warning profile__warning--{{ warning_class }}{% if show_user or show_issuer or delete_csrf %} profile__warning--extendo{% endif %}">
<div class="profile__warning__background"></div>
{% if show_user or show_issuer or delete_csrf %}
<div class="profile__warning__tools">
{% if show_user %}
<div class="profile__warning__user">
<div class="avatar profile__warning__user__avatar" style="background-image:url('/profile.php?m=avatar&amp;u={{ warning.user_id }}');"></div>
<div class="profile__warning__user__username">
{{ warning.username }}
</div>
{% if show_ips %}
<div class="profile__warning__user__ip">
{{ warning.user_ip }}
</div>
{% endif %}
</div>
{% endif %}
{% if show_issuer %}
<div class="profile__warning__user">
<div class="avatar profile__warning__user__avatar" style="background-image:url('/profile.php?m=avatar&amp;u={{ warning.issuer_id }}');"></div>
<div class="profile__warning__user__username">
{{ warning.issuer_username }}
</div>
{% if show_ips %}
<div class="profile__warning__user__ip">
{{ warning.issuer_ip }}
</div>
{% endif %}
</div>
{% endif %}
{% if delete_csrf %}
<div class="profile__warning__options">
<a href="/manage/users.php?v=warnings&amp;u={{ warning.user_id }}&amp;w={{ warning.warning_id }}&amp;m=delete&amp;c={{ delete_csrf }}" class="profile__warning__option"><i class="far fa-trash-alt"></i> Delete</a>
</div>
{% endif %}
</div>
{% endif %}
<div class="profile__warning__content">
<div class="profile__warning__type">
{{ warning_text }}