From d579d2c8e024009ac2f12ba14e695de664be68a3 Mon Sep 17 00:00:00 2001 From: flashwave Date: Sun, 27 May 2018 03:59:57 +0200 Subject: [PATCH] Add some basic user management logic. --- assets/less/manage/classes/pagination.less | 8 +- public/manage/users.php | 71 ++++++++++++- public/settings.php | 114 ++++++++------------- src/Users/role.php | 42 ++++++++ src/Users/user.php | 109 ++++++++++++++++++++ views/manage/users/view.twig | 63 ++++++++++-- 6 files changed, 321 insertions(+), 86 deletions(-) diff --git a/assets/less/manage/classes/pagination.less b/assets/less/manage/classes/pagination.less index 5f98f7aa..64f81151 100644 --- a/assets/less/manage/classes/pagination.less +++ b/assets/less/manage/classes/pagination.less @@ -16,8 +16,8 @@ box-shadow: 0 1px 5px 0 fade(#555, 80%); } - &--active, - &:active { + &:not(&--disabled)&--active, + &:not(&--disabled)&:active { background-color: #444; } @@ -48,4 +48,8 @@ font-size: 2em; } } + + &__option--disabled &__link { + cursor: default; + } } diff --git a/public/manage/users.php b/public/manage/users.php index bd913cc2..9198c03f 100644 --- a/public/manage/users.php +++ b/public/manage/users.php @@ -47,7 +47,6 @@ switch ($_GET['v'] ?? null) { echo 'no'; break; } - $getUser = $db->prepare(' SELECT u.*, @@ -68,7 +67,75 @@ switch ($_GET['v'] ?? null) { break; } - $templating->var('view_user', $manageUser); + $getHasRoles = $db->prepare(' + SELECT `role_id`, `role_name` + FROM `msz_roles` + WHERE `role_id` IN ( + SELECT `role_id` + FROM `msz_user_roles` + WHERE `user_id` = :user_id + ) + '); + $getHasRoles->bindValue('user_id', $manageUser['user_id']); + $hasRoles = $getHasRoles->execute() ? $getHasRoles->fetchAll() : []; + + $getAvailableRoles = $db->prepare(' + SELECT `role_id`, `role_name` + FROM `msz_roles` + WHERE `role_id` NOT IN ( + SELECT `role_id` + FROM `msz_user_roles` + WHERE `user_id` = :user_id + ) + '); + $getAvailableRoles->bindValue('user_id', $manageUser['user_id']); + $availableRoles = $getAvailableRoles->execute() ? $getAvailableRoles->fetchAll() : []; + + if ($isPostRequest) { + if (!tmp_csrf_verify($_POST['csrf'] ?? '')) { + echo 'csrf err'; + break; + } + + if (isset($_POST['avatar'])) { + switch ($_POST['avatar']['mode'] ?? '') { + case 'delete': + user_avatar_delete($manageUser['user_id']); + break; + + case 'upload': + user_avatar_set_from_path($manageUser['user_id'], $_FILES['avatar']['tmp_name']['file']); + break; + } + } + + if (isset($_POST['add_role'])) { + user_role_add($manageUser['user_id'], $_POST['add_role']['role']); + } + + if (isset($_POST['manage_roles'])) { + switch ($_POST['manage_roles']['mode'] ?? '') { + case 'display': + user_role_set_display($manageUser['user_id'], $_POST['manage_roles']['role']); + break; + + case 'remove': + if ((int)$_POST['manage_roles']['role'] !== MSZ_ROLE_MAIN) { + user_role_remove($manageUser['user_id'], $_POST['manage_roles']['role']); + } + break; + } + } + + header("Location: ?v=view&u={$manageUser['user_id']}"); + break; + } + + $templating->vars([ + 'available_roles' => $availableRoles, + 'has_roles' => $hasRoles, + 'view_user' => $manageUser, + ]); echo $templating->render('@manage.users.view'); break; diff --git a/public/settings.php b/public/settings.php index 8e49d884..a09e8171 100644 --- a/public/settings.php +++ b/public/settings.php @@ -17,6 +17,30 @@ if (!$app->hasActiveSession()) { $csrfErrorString = "Couldn't verify you, please refresh the page and retry."; +$avatarErrorStrings = [ + 'upload' => [ + 'default' => 'Something happened? (UP:%1$d)', + UPLOAD_ERR_OK => '', + UPLOAD_ERR_NO_FILE => 'Select a file before hitting upload!', + UPLOAD_ERR_PARTIAL => 'The upload was interrupted, please try again!', + UPLOAD_ERR_INI_SIZE => 'Your avatar is not allowed to be larger in file size than %2$s!', + UPLOAD_ERR_FORM_SIZE => 'Your avatar is not allowed to be larger in file size than %2$s!', + UPLOAD_ERR_NO_TMP_DIR => 'Unable to save your avatar, contact an administator!', + UPLOAD_ERR_CANT_WRITE => 'Unable to save your avatar, contact an administator!', + ], + 'set' => [ + 'default' => 'Something happened? (SET:%1$d)', + MSZ_USER_AVATAR_NO_ERRORS => '', + MSZ_USER_AVATAR_ERROR_INVALID_IMAGE => 'The file you uploaded was not an image!', + MSZ_USER_AVATAR_ERROR_PROHIBITED_TYPE => 'This type of image is not supported, keep to PNG, JPG or GIF!', + MSZ_USER_AVATAR_ERROR_DIMENSIONS_TOO_LARGE => 'Your avatar can\'t be larger than %3$dx%4$d!', + MSZ_USER_AVATAR_ERROR_DATA_TOO_LARGE => 'Your avatar is not allowed to be larger in file size than %2$s!', + MSZ_USER_AVATAR_ERROR_TMP_FAILED => 'Unable to save your avatar, contact an administator!', + MSZ_USER_AVATAR_ERROR_STORE_FAILED => 'Unable to save your avatar, contact an administator!', + MSZ_USER_AVATAR_ERROR_FILE_NOT_FOUND => 'Unable to save your avatar, contact an administator!', + ], +]; + $settingsModes = [ 'account' => 'Account', 'avatar' => 'Avatar', @@ -193,16 +217,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { break; } - $delete_this = [ - $app->getStore('avatars/original')->filename($avatarFileName), - $app->getStore('avatars/200x200')->filename($avatarFileName), - ]; - - foreach ($delete_this as $delete_avatar) { - if (File::exists($delete_avatar)) { - File::delete($delete_avatar); - } - } + user_avatar_delete($app->getUserId()); break; } @@ -212,78 +227,29 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { break; } - switch ($_FILES['avatar']['error']) { - case UPLOAD_ERR_OK: - break; - - case UPLOAD_ERR_NO_FILE: - $settingsErrors[] = 'Select a file before hitting upload!'; - break; - - case UPLOAD_ERR_PARTIAL: - $settingsErrors[] = 'The upload was interrupted, please try again!'; - break; - - case UPLOAD_ERR_INI_SIZE: - case UPLOAD_ERR_FORM_SIZE: - $settingsErrors[] = sprintf( - 'Your avatar is not allowed to be larger in filesize than %s', - byte_symbol($avatarFileSizeMax, true) - ); - break; - - case UPLOAD_ERR_NO_TMP_DIR: - case UPLOAD_ERR_CANT_WRITE: - $settingsErrors[] = 'Unable to save your avatar, contact an administator!'; - break; - - case UPLOAD_ERR_EXTENSION: - default: - $settingsErrors[] = 'Something happened?'; - break; - } - - if (count($settingsErrors) > 0) { - break; - } - - $upload_path = $_FILES['avatar']['tmp_name']; - $upload_meta = getimagesize($upload_path); - - if (!$upload_meta - || !in_array($upload_meta[2], [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG], true) - || $upload_meta[0] < 1 - || $upload_meta[1] < 1) { - $settingsErrors[] = 'Please provide a valid image.'; - break; - } - - if ($upload_meta[0] > $avatarWidthMax || $upload_meta[1] > $avatarHeightMax) { + if ($_FILES['avatar']['error'] !== UPLOAD_ERR_OK) { $settingsErrors[] = sprintf( - "Your avatar can't be larger than %dx%d, yours was %dx%d", + $avatarErrorStrings['upload'][$_FILES['avatar']['error']] + ?? $avatarErrorStrings['upload']['default'], + $_FILES['avatar']['error'], + byte_symbol($avatarFileSizeMax, true), $avatarWidthMax, - $avatarHeightMax, - $upload_meta[0], - $upload_meta[1] + $avatarHeightMax ); break; } - if (filesize($upload_path) > $avatarFileSizeMax) { + $setAvatar = user_avatar_set_from_path($app->getUserId(), $_FILES['avatar']['tmp_name']); + + if ($setAvatar !== MSZ_USER_AVATAR_NO_ERRORS) { $settingsErrors[] = sprintf( - 'Your avatar is not allowed to be larger in filesize than %s!', - byte_symbol($avatarFileSizeMax, true) + $avatarErrorStrings['set'][$setAvatar] + ?? $avatarErrorStrings['set']['default'], + $setAvatar, + byte_symbol($avatarFileSizeMax, true), + $avatarWidthMax, + $avatarHeightMax ); - break; - } - - $avatar_path = $app->getStore('avatars/original')->filename($avatarFileName); - move_uploaded_file($upload_path, $avatar_path); - - $crop_path = $app->getStore('avatars/200x200')->filename($avatarFileName); - - if (File::exists($crop_path)) { - File::delete($crop_path); } break; } diff --git a/src/Users/role.php b/src/Users/role.php index 5a657f68..e62c5e49 100644 --- a/src/Users/role.php +++ b/src/Users/role.php @@ -15,3 +15,45 @@ function user_role_add(int $userId, int $roleId): bool $addRole->bindValue('role_id', $roleId); return $addRole->execute(); } + +function user_role_remove(int $userId, int $roleId): bool +{ + $removeRole = Database::connection()->prepare(' + DELETE FROM `msz_user_roles` + WHERE `user_id` = :user_id + AND `role_id` = :role_id + '); + $removeRole->bindValue('user_id', $userId); + $removeRole->bindValue('role_id', $roleId); + return $removeRole->execute(); +} + +function user_role_has(int $userId, int $roleId): bool +{ + $hasRole = Database::connection()->prepare(' + SELECT COUNT(`role_id`) > 0 + FROM `msz_user_roles` + WHERE `user_id` = :user_id + AND `role_id` = :role_id + '); + $hasRole->bindValue('user_id', $userId); + $hasRole->bindValue('role_id', $roleId); + return $hasRole->execute() ? (bool)$hasRole->fetchColumn() : false; +} + +function user_role_set_display(int $userId, int $roleId): bool +{ + if (!user_role_has($userId, $roleId)) { + return false; + } + + $setDisplay = Database::connection()->prepare(' + UPDATE `msz_users` + SET `display_role` = :role_id + WHERE `user_id` = :user_id + '); + $setDisplay->bindValue('user_id', $userId); + $setDisplay->bindValue('role_id', $roleId); + + return $setDisplay->execute(); +} diff --git a/src/Users/user.php b/src/Users/user.php index e6eed203..64d991a5 100644 --- a/src/Users/user.php +++ b/src/Users/user.php @@ -1,5 +1,7 @@ execute() ? $chatKey : ''; } + +define('MSZ_USER_AVATAR_FORMAT', '%d.msz'); + +function user_avatar_delete(int $userId): void +{ + $app = Application::getInstance(); + $avatarFileName = sprintf(MSZ_USER_AVATAR_FORMAT, $userId); + + $deleteThis = [ + $app->getStore('avatars/original')->filename($avatarFileName), + $app->getStore('avatars/200x200')->filename($avatarFileName), + ]; + + foreach ($deleteThis as $deleteAvatar) { + if (File::exists($deleteAvatar)) { + File::delete($deleteAvatar); + } + } +} + +define('MSZ_USER_AVATAR_TYPE_PNG', IMAGETYPE_PNG); +define('MSZ_USER_AVATAR_TYPE_JPG', IMAGETYPE_JPEG); +define('MSZ_USER_AVATAR_TYPE_GIF', IMAGETYPE_GIF); +define('MSZ_USER_AVATAR_TYPES', [ + MSZ_USER_AVATAR_TYPE_PNG, + MSZ_USER_AVATAR_TYPE_JPG, + MSZ_USER_AVATAR_TYPE_GIF, +]); + +function user_avatar_is_allowed_type(int $type): bool +{ + return in_array($type, MSZ_USER_AVATAR_TYPES, true); +} + +define('MSZ_USER_AVATAR_OPTIONS', [ + 'max_width' => 4000, + 'max_height' => 4000, + 'max_size' => 1000000, +]); + +define('MSZ_USER_AVATAR_NO_ERRORS', 0); +define('MSZ_USER_AVATAR_ERROR_INVALID_IMAGE', 1); +define('MSZ_USER_AVATAR_ERROR_PROHIBITED_TYPE', 2); +define('MSZ_USER_AVATAR_ERROR_DIMENSIONS_TOO_LARGE', 3); +define('MSZ_USER_AVATAR_ERROR_DATA_TOO_LARGE', 4); +define('MSZ_USER_AVATAR_ERROR_TMP_FAILED', 5); +define('MSZ_USER_AVATAR_ERROR_STORE_FAILED', 6); +define('MSZ_USER_AVATAR_ERROR_FILE_NOT_FOUND', 7); + +function user_avatar_set_from_path(int $userId, string $path, array $options = []): int +{ + if (!file_exists($path)) { + return MSZ_USER_AVATAR_ERROR_FILE_NOT_FOUND; + } + + $options = array_merge(MSZ_USER_AVATAR_OPTIONS, $options); + + // 0 => width, 1 => height, 2 => type + $imageInfo = getimagesize($path); + + if ($imageInfo === false + || count($imageInfo) < 3 + || $imageInfo[0] < 1 + || $imageInfo[1] < 1) { + return MSZ_USER_AVATAR_ERROR_INVALID_IMAGE; + } + + if (!user_avatar_is_allowed_type($imageInfo[2])) { + return MSZ_USER_AVATAR_ERROR_PROHIBITED_TYPE; + } + + if ($imageInfo[0] > $options['max_width'] + || $imageInfo[1] > $options['max_height']) { + return MSZ_USER_AVATAR_ERROR_DIMENSIONS_TOO_LARGE; + } + + if (filesize($path) > $options['max_size']) { + return MSZ_USER_AVATAR_ERROR_DATA_TOO_LARGE; + } + + user_avatar_delete($userId); + + $fileName = sprintf(MSZ_USER_AVATAR_FORMAT, $userId); + $avatarPath = Application::getInstance()->getStore('avatars/original')->filename($fileName); + + if (!copy($path, $avatarPath)) { + return MSZ_USER_AVATAR_ERROR_STORE_FAILED; + } + + return MSZ_USER_AVATAR_NO_ERRORS; +} + +function user_avatar_set_from_data(int $userId, string $data, array $options = []): int +{ + $tmp = tempnam(sys_get_temp_dir(), 'msz'); + + if ($tmp === false || !file_exists($tmp)) { + return MSZ_USER_AVATAR_ERROR_TMP_FAILED; + } + + chmod($tmp, 644); + file_put_contents($tmp, $data); + $result = user_avatar_set_from_path($userId, $tmp, $options); + unlink($tmp); + + return $result; +} diff --git a/views/manage/users/view.twig b/views/manage/users/view.twig index 38a1693c..28e05373 100644 --- a/views/manage/users/view.twig +++ b/views/manage/users/view.twig @@ -46,23 +46,25 @@ -
+

Avatar

+ +
- - + +
-
+

@@ -168,9 +170,54 @@

-
-

Roles

-
+ {% if available_roles|length > 0 %} +
+

Add Role

+ + + +
+ +
+
+ {% endif %} + + {% if has_roles|length > 0 %} +
+

Manage Roles

+ + + + + +
+ + +
+
+ {% endif %}

Permissions