diff --git a/misuzu.php b/misuzu.php index ff6d6099..a785cc4a 100644 --- a/misuzu.php +++ b/misuzu.php @@ -80,7 +80,6 @@ require_once 'src/Forum/topic.php'; require_once 'src/Forum/validate.php'; require_once 'src/Users/avatar.php'; require_once 'src/Users/background.php'; -require_once 'src/Users/role.php'; require_once 'src/Users/user_legacy.php'; $dbConfig = parse_ini_file(MSZ_ROOT . '/config/config.ini', true, INI_SCANNER_TYPED); diff --git a/public/auth/register.php b/public/auth/register.php index b278326d..36172ef8 100644 --- a/public/auth/register.php +++ b/public/auth/register.php @@ -4,7 +4,9 @@ namespace Misuzu; use Misuzu\Net\IPAddress; use Misuzu\Net\IPAddressBlacklist; use Misuzu\Users\User; +use Misuzu\Users\UserCreationFailedException; use Misuzu\Users\UserLoginAttempt; +use Misuzu\Users\UserRole; use Misuzu\Users\UserSession; use Misuzu\Users\UserWarning; @@ -50,42 +52,38 @@ while(!$restricted && !empty($register)) { } $usernameValidation = User::validateUsername($register['username']); - if($usernameValidation !== '') { + if($usernameValidation !== '') $notices[] = User::usernameValidationErrorString($usernameValidation); - } $emailValidation = User::validateEMailAddress($register['email']); - if($emailValidation !== '') { + if($emailValidation !== '') $notices[] = $emailValidation === 'in-use' ? 'This e-mail address has already been used!' : 'The e-mail address you entered is invalid!'; - } - if($register['password_confirm'] !== $register['password']) { + if($register['password_confirm'] !== $register['password']) $notices[] = 'The given passwords don\'t match.'; - } - if(User::validatePassword($register['password']) !== '') { + if(User::validatePassword($register['password']) !== '') $notices[] = 'Your password is too weak!'; - } - if(!empty($notices)) { + if(!empty($notices)) break; - } - $createUser = User::create( - $register['username'], - $register['password'], - $register['email'], - $ipAddress - ); - - if($createUser === null) { + try { + $createUser = User::create( + $register['username'], + $register['password'], + $register['email'], + $ipAddress + ); + } catch(UserCreationFailedException $ex) { $notices[] = 'Something went wrong while creating your account, please alert an administrator or a developer about this!'; break; } - user_role_add($createUser->getId(), MSZ_ROLE_MAIN); + $createUser->addRole(UserRole::byDefault()); + url_redirect('auth-login-welcome', ['username' => $createUser->getUsername()]); return; } diff --git a/public/manage/general/emoticon.php b/public/manage/general/emoticon.php index 5a150015..33df8472 100644 --- a/public/manage/general/emoticon.php +++ b/public/manage/general/emoticon.php @@ -16,7 +16,7 @@ $emoteInfo = !$isNew ? Emoticon::byId($emoteId) : new Emoticon; if(CSRF::validateRequest() && isset($_POST['emote_order']) && isset($_POST['emote_hierarchy']) && !empty($_POST['emote_url']) && !empty($_POST['emote_strings'])) { $emoteInfo->setUrl($_POST['emote_url']) - ->setHierarchy($_POST['emote_hierarchy']) + ->setRank($_POST['emote_hierarchy']) ->setOrder($_POST['emote_order']) ->save(); diff --git a/public/manage/users/role.php b/public/manage/users/role.php index 1beea5a5..bc37926e 100644 --- a/public/manage/users/role.php +++ b/public/manage/users/role.php @@ -1,8 +1,9 @@ return; } -$roleId = $_GET['r'] ?? null; -$currentUserId = User::getCurrent()->getId(); -/*$isSuperUser = user_check_super($currentUserId); -$canEdit = $isSuperUser || user_check_authority($currentUserId, $userId);*/ -$canEditPerms = /*$canEdit && */perms_check_user(MSZ_PERMS_USER, $currentUserId, MSZ_PERM_USER_MANAGE_PERMS); +$roleId = (int)filter_input(INPUT_GET, 'r', FILTER_SANITIZE_NUMBER_INT); -if($canEditPerms) { +if($roleId > 0) + try { + $roleInfo = UserRole::byId($roleId); + } catch(UserRoleNotFoundException $ex) { + echo render_error(404); + return; + } + +$currentUser = User::getCurrent(); +$currentUserId = $currentUser->getId(); +$canEditPerms = perms_check_user(MSZ_PERMS_USER, $currentUserId, MSZ_PERM_USER_MANAGE_PERMS); + +if($canEditPerms) $permissions = manage_perms_list(perms_get_role_raw($roleId ?? 0)); -} if(!empty($_POST['role']) && is_array($_POST['role']) && CSRF::validateRequest()) { $roleHierarchy = (int)($_POST['role']['hierarchy'] ?? -1); - if(!user_check_super($currentUserId) && ($roleId === null - ? (user_get_hierarchy($currentUserId) <= $roleHierarchy) - : !user_role_check_authority($currentUserId, $roleId))) { - echo 'Your hierarchy is too low to do this.'; + if(!$currentUser->isSuper() && (isset($roleInfo) ? $roleInfo->hasAuthorityOver($currentUser) : $currentUser->getRank() <= $roleHierarchy)) { + echo 'You don\'t hold authority over this role.'; return; } @@ -63,15 +69,13 @@ if(!empty($_POST['role']) && is_array($_POST['role']) && CSRF::validateRequest() } } - $roleDescription = $_POST['role']['description'] ?? null; - $roleTitle = $_POST['role']['title'] ?? null; + $roleDescription = $_POST['role']['description'] ?? ''; + $roleTitle = $_POST['role']['title'] ?? ''; if($roleDescription !== null) { $rdLength = strlen($roleDescription); - if($rdLength < 1) { - $roleDescription = null; - } elseif($rdLength > 1000) { + if($rdLength > 1000) { echo 'description is too long'; return; } @@ -80,52 +84,22 @@ if(!empty($_POST['role']) && is_array($_POST['role']) && CSRF::validateRequest() if($roleTitle !== null) { $rtLength = strlen($roleTitle); - if($rtLength < 1) { - $roleTitle = null; - } elseif($rtLength > 64) { + if($rtLength > 64) { echo 'title is too long'; return; } } - if($roleId < 1) { - $updateRole = DB::prepare(' - INSERT INTO `msz_roles` - ( - `role_name`, `role_hierarchy`, `role_hidden`, `role_colour`, - `role_description`, `role_title` - ) - VALUES - ( - :role_name, :role_hierarchy, :role_hidden, :role_colour, - :role_description, :role_title - ) - '); - } else { - $updateRole = DB::prepare(' - UPDATE `msz_roles` - SET `role_name` = :role_name, - `role_hierarchy` = :role_hierarchy, - `role_hidden` = :role_hidden, - `role_colour` = :role_colour, - `role_description` = :role_description, - `role_title` = :role_title - WHERE `role_id` = :role_id - '); - $updateRole->bind('role_id', $roleId); - } + if(!isset($roleInfo)) + $roleInfo = new UserRole; - $updateRole->bind('role_name', $roleName); - $updateRole->bind('role_hierarchy', $roleHierarchy); - $updateRole->bind('role_hidden', $roleSecret ? 1 : 0); - $updateRole->bind('role_colour', $roleColour->getRaw()); - $updateRole->bind('role_description', $roleDescription); - $updateRole->bind('role_title', $roleTitle); - $updateRole->execute(); - - if($roleId < 1) { - $roleId = DB::lastId(); - } + $roleInfo->setName($roleName) + ->setRank($roleHierarchy) + ->setHidden($roleSecret) + ->setColour($roleColour) + ->setDescription($roleDescription) + ->setTitle($roleTitle) + ->save(); if(!empty($permissions) && !empty($_POST['perms']) && is_array($_POST['perms'])) { $perms = manage_perms_apply($permissions, $_POST['perms']); @@ -138,7 +112,7 @@ if(!empty($_POST['role']) && is_array($_POST['role']) && CSRF::validateRequest() VALUES (:role_id, NULL, :' . implode(', :', $permKeys) . ') '); - $setPermissions->bind('role_id', $roleId); + $setPermissions->bind('role_id', $roleInfo->getId()); foreach($perms as $key => $value) { $setPermissions->bind($key, $value); @@ -151,41 +125,17 @@ if(!empty($_POST['role']) && is_array($_POST['role']) && CSRF::validateRequest() WHERE `role_id` = :role_id AND `user_id` IS NULL '); - $deletePermissions->bind('role_id', $roleId); + $deletePermissions->bind('role_id', $roleInfo->getId()); $deletePermissions->execute(); } } - url_redirect('manage-role', ['role' => $roleId]); + url_redirect('manage-role', ['role' => $roleInfo->getId()]); return; } -if($roleId !== null) { - if($roleId < 1) { - echo 'no'; - return; - } - - $getEditRole = DB::prepare(' - SELECT * - FROM `msz_roles` - WHERE `role_id` = :role_id - '); - $getEditRole->bind('role_id', $roleId); - $editRole = $getEditRole->fetch(); - - if(empty($editRole)) { - echo 'invalid role'; - return; - } - - Template::set([ - 'edit_role' => $editRole, - 'role_colour' => new Colour($editRole['role_colour']), - ]); -} - Template::render('manage.users.role', [ + 'role_info' => $roleInfo ?? null, 'can_manage_perms' => $canEditPerms, 'permissions' => $permissions ?? [], ]); diff --git a/public/manage/users/roles.php b/public/manage/users/roles.php index a431e636..f805b088 100644 --- a/public/manage/users/roles.php +++ b/public/manage/users/roles.php @@ -2,6 +2,7 @@ namespace Misuzu; use Misuzu\Users\User; +use Misuzu\Users\UserRole; require_once '../../../misuzu.php'; @@ -10,34 +11,14 @@ if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_USER, User::getCurrent()-> return; } -$manageRolesCount = (int)DB::query(' - SELECT COUNT(`role_id`) - FROM `msz_roles` -')->fetchColumn(); +$pagination = new Pagination(UserRole::countAll(true), 10); -$rolesPagination = new Pagination($manageRolesCount, 10); - -if(!$rolesPagination->hasValidOffset()) { +if(!$pagination->hasValidOffset()) { echo render_error(404); return; } -$getManageRoles = DB::prepare(' - SELECT - `role_id`, `role_colour`, `role_name`, `role_title`, - ( - SELECT COUNT(`user_id`) - FROM `msz_user_roles` as ur - WHERE ur.`role_id` = r.`role_id` - ) as `users` - FROM `msz_roles` as r - LIMIT :offset, :take -'); -$getManageRoles->bind('offset', $rolesPagination->getOffset()); -$getManageRoles->bind('take', $rolesPagination->getRange()); -$manageRoles = $getManageRoles->fetchAll(); - Template::render('manage.users.roles', [ - 'manage_roles' => $manageRoles, - 'manage_roles_pagination' => $rolesPagination, + 'manage_roles' => UserRole::all(true, $pagination), + 'manage_roles_pagination' => $pagination, ]); diff --git a/public/manage/users/user.php b/public/manage/users/user.php index 66fefca7..3a914277 100644 --- a/public/manage/users/user.php +++ b/public/manage/users/user.php @@ -2,6 +2,9 @@ namespace Misuzu; use Misuzu\Users\User; +use Misuzu\Users\UserNotFoundException; +use Misuzu\Users\UserRole; +use Misuzu\Users\UserRoleNotFoundException; require_once '../../../misuzu.php'; @@ -11,32 +14,28 @@ if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_USER, User::getCurrent()-> } $notices = []; -$userId = (int)($_GET['u'] ?? 0); -$currentUserId = User::getCurrent()->getId(); +$userId = (int)filter_input(INPUT_GET, 'u', FILTER_SANITIZE_NUMBER_INT); +$currentUser = User::getCurrent(); +$currentUserId = $currentUser->getId(); -if($userId < 1) { +try { + $userInfo = User::byId($userId); +} catch(UserNotFoundException $ex) { echo render_error(404); return; } -$isSuperUser = user_check_super($currentUserId); -$canEdit = $isSuperUser || user_check_authority($currentUserId, $userId); +$canEdit = $currentUser->hasAuthorityOver($userInfo); $canEditPerms = $canEdit && perms_check_user(MSZ_PERMS_USER, $currentUserId, MSZ_PERM_USER_MANAGE_PERMS); $permissions = manage_perms_list(perms_get_user_raw($userId)); if(CSRF::validateRequest() && $canEdit) { if(!empty($_POST['roles']) && is_array($_POST['roles']) && array_test($_POST['roles'], 'ctype_digit')) { // Fetch existing roles - $existingRoles = DB::prepare(' - SELECT `role_id` - FROM `msz_user_roles` - WHERE `user_id` = :user_id - '); - $existingRoles->bind('user_id', $userId); - $existingRoles = $existingRoles->fetchAll(); + $existingRoles = $userInfo->getRoles(); - // Initialise set array with existing role ids - $setRoles = array_column($existingRoles, 'role_id'); + // Initialise set array with existing roles + $setRoles = $existingRoles; // Read user input array and throw intval on em $applyRoles = array_apply($_POST['roles'], 'intval'); @@ -48,53 +47,33 @@ if(CSRF::validateRequest() && $canEdit) { // Roles that the current users isn't allowed to touch (hierarchy) will stay. foreach($setRoles as $role) { // Also prevent the main role from being removed. - if($role === MSZ_ROLE_MAIN || (!$isSuperUser && !user_role_check_authority($currentUserId, $role))) { + if($role->isDefault() || !$currentUser->hasAuthorityOver($role)) continue; - } - - if(!in_array($role, $applyRoles)) { + if(!in_array($role->getId(), $applyRoles)) $removeRoles[] = $role; - } } // STEP 2: Purge the ones marked for removal. $setRoles = array_diff($setRoles, $removeRoles); // STEP 3: Add roles to the set array from the user input, if the user has authority over the given roles. - foreach($applyRoles as $role) { - if(!$isSuperUser && !user_role_check_authority($currentUserId, $role)) { + foreach($applyRoles as $roleId) { + try { + $role = $existingRoles[$roleId] ?? UserRole::byId($roleId); + } catch(UserRoleNotFoundException $ex) { continue; } - - if(!in_array($role, $setRoles)) { + if(!$currentUser->hasAuthorityOver($role)) + continue; + if(!in_array($role, $setRoles)) $setRoles[] = $role; - } } - if(!empty($setRoles)) { - // The implode here probably sets off alarm bells, but the array is - // guaranteed to only contain integers so it's probably fine. - $removeRanks = DB::prepare(sprintf(' - DELETE FROM `msz_user_roles` - WHERE `user_id` = :user_id - AND `role_id` NOT IN (%s) - ', implode(',', $setRoles))); - $removeRanks->bind('user_id', $userId); - $removeRanks->execute(); + foreach($removeRoles as $role) + $userInfo->removeRole($role); - $addRank = DB::prepare(' - INSERT IGNORE INTO `msz_user_roles` - (`user_id`, `role_id`) - VALUES - (:user_id, :role_id) - '); - $addRank->bind('user_id', $userId); - - foreach($setRoles as $role) { - $addRank->bind('role_id', $role); - $addRank->execute(); - } - } + foreach($setRoles as $role) + $userInfo->addRole($role); } $setUserInfo = []; @@ -107,9 +86,9 @@ if(CSRF::validateRequest() && $canEdit) { $displayRole = (int)($_POST['user']['display_role'] ?? 0); - if(user_role_has($userId, $displayRole)) { - $setUserInfo['display_role'] = $displayRole; - } + try { + $userInfo->setDisplayRole(UserRole::byId($displayRole)); + } catch(UserRoleNotFoundException $ex) {} $usernameValidation = User::validateUsername($setUserInfo['username']); $emailValidation = User::validateEMailAddress($setUserInfo['email']); @@ -124,19 +103,16 @@ if(CSRF::validateRequest() && $canEdit) { $notices[] = $emailValidation === 'in-use' ? 'This e-mail address has already been used!' : 'This e-mail address is invalid!'; - } else { + } else $setUserInfo['email'] = mb_strtolower($setUserInfo['email']); - } - if(!$countryValidation) { + if(!$countryValidation) $notices[] = 'Country code was invalid.'; - } - if(strlen($setUserInfo['user_title']) < 1) { + if(strlen($setUserInfo['user_title']) < 1) $setUserInfo['user_title'] = null; - } elseif(strlen($setUserInfo['user_title']) > 64) { + elseif(strlen($setUserInfo['user_title']) > 64) $notices[] = 'User title was invalid.'; - } } if(!empty($_POST['colour']) && is_array($_POST['colour'])) { @@ -160,13 +136,12 @@ if(CSRF::validateRequest() && $canEdit) { $passwordConfirmValue = (string)($_POST['password']['confirm'] ?? ''); if(!empty($passwordNewValue)) { - if($passwordNewValue !== $passwordConfirmValue) { + if($passwordNewValue !== $passwordConfirmValue) $notices[] = 'Confirm password does not match.'; - } elseif(!empty(User::validatePassword($passwordNewValue))) { + elseif(!empty(User::validatePassword($passwordNewValue))) $notices[] = 'New password is too weak.'; - } else { + else $setUserInfo['password'] = User::hashPassword($passwordNewValue); - } } } @@ -181,26 +156,22 @@ if(CSRF::validateRequest() && $canEdit) { )); $userUpdate->bind('set_user_id', $userId); - foreach($setUserInfo as $key => $value) { + foreach($setUserInfo as $key => $value) $userUpdate->bind($key, $value); - } - if(!$userUpdate->execute()) { + if(!$userUpdate->execute()) $notices[] = 'Something went wrong while updating the user.'; - } } if($canEditPerms && !empty($_POST['perms']) && is_array($_POST['perms'])) { $perms = manage_perms_apply($permissions, $_POST['perms']); if($perms !== null) { - if(!perms_set_user_raw($userId, $perms)) { + if(!perms_set_user_raw($userId, $perms)) $notices[] = 'Failed to update permissions.'; - } } else { - if(!perms_delete_user($userId)) { + if(!perms_delete_user($userId)) $notices[] = 'Failed to remove permissions.'; - } } // this smells, make it refresh/apply in a non-retarded way @@ -208,45 +179,10 @@ if(CSRF::validateRequest() && $canEdit) { } } -$getUser = DB::prepare(' - SELECT - u.*, - INET6_NTOA(u.`register_ip`) as `register_ip_decoded`, - INET6_NTOA(u.`last_ip`) as `last_ip_decoded`, - COALESCE(u.`user_colour`, r.`role_colour`) as `colour` - FROM `msz_users` as u - LEFT JOIN `msz_roles` as r - ON u.`display_role` = r.`role_id` - WHERE `user_id` = :user_id - ORDER BY `user_id` -'); -$getUser->bind('user_id', $userId); -$manageUser = $getUser->fetch(); - -if(empty($manageUser)) { - echo render_error(404); - return; -} - -$getRoles = DB::prepare(' - SELECT - r.`role_id`, r.`role_name`, r.`role_hierarchy`, r.`role_colour`, - ( - SELECT COUNT(`user_id`) > 0 - FROM `msz_user_roles` - WHERE `role_id` = r.`role_id` - AND `user_id` = :user_id - ) AS `has_role` - FROM `msz_roles` AS r -'); -$getRoles->bind('user_id', $manageUser['user_id']); -$roles = $getRoles->fetchAll(); - Template::render('manage.users.user', [ - 'manage_user' => $manageUser, - 'user_colour' => empty($manageUser['user_colour']) ? Colour::none() : new Colour($manageUser['user_colour']), + 'user_info' => $userInfo, 'manage_notices' => $notices, - 'manage_roles' => $roles, + 'manage_roles' => UserRole::all(true), 'can_edit_user' => $canEdit, 'can_edit_perms' => $canEdit && $canEditPerms, 'permissions' => $permissions ?? [], diff --git a/public/manage/users/warnings.php b/public/manage/users/warnings.php index a8e65cd8..c9a2c70d 100644 --- a/public/manage/users/warnings.php +++ b/public/manage/users/warnings.php @@ -17,7 +17,8 @@ if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_USER, User::getCurrent()-> } $notices = []; -$currentUserId = User::getCurrent()->getId(); +$currentUser = User::getCurrent(); +$currentUserId = $currentUser->getId(); if(!empty($_POST['lookup']) && is_string($_POST['lookup'])) { url_redirect('manage-users-warnings', ['user' => user_id_from_username($_POST['lookup'])]); @@ -67,19 +68,19 @@ if(!empty($_POST['warning']) && is_array($_POST['warning'])) { try { $warningsUserInfo = User::byId((int)($_POST['warning']['user'] ?? 0)); $warningsUser = $warningsUserInfo->getId(); + + if(!$currentUser->hasAuthorityOver($warningsUserInfo)) + $notices[] = 'You do not have authority over this user.'; } catch(UserNotFoundException $ex) { - $warningsUserInfo = null; + $notices[] = 'This user doesn\'t exist.'; } - if(!user_check_super($currentUserId) && !user_check_authority($currentUserId, $warningsUser)) { - $notices[] = 'You do not have authority over this user.'; - } - if(empty($notices) && $warningsUser > 0) { + if(empty($notices) && !empty($warningsUserInfo)) { try { $warningInfo = UserWarning::create( $warningsUserInfo, - User::getCurrent(), + $currentUser, $warningType, $warningDuration, $_POST['warning']['note'], diff --git a/public/members.php b/public/members.php index 9f8e0e73..d2c4ded2 100644 --- a/public/members.php +++ b/public/members.php @@ -2,10 +2,12 @@ namespace Misuzu; use Misuzu\Users\User; +use Misuzu\Users\UserRole; +use Misuzu\Users\UserRoleNotFoundException; require_once '../misuzu.php'; -$roleId = !empty($_GET['r']) && is_string($_GET['r']) ? (int)$_GET['r'] : MSZ_ROLE_MAIN; +$roleId = !empty($_GET['r']) && is_string($_GET['r']) ? (int)$_GET['r'] : UserRole::DEFAULT; $orderBy = !empty($_GET['ss']) && is_string($_GET['ss']) ? mb_strtolower($_GET['ss']) : ''; $orderDir = !empty($_GET['sd']) && is_string($_GET['sd']) ? mb_strtolower($_GET['sd']) : ''; @@ -79,16 +81,16 @@ if(empty($orderDir)) { $canManageUsers = perms_check_user(MSZ_PERMS_USER, User::hasCurrent() ? User::getCurrent()->getId() : 0, MSZ_PERM_USER_MANAGE_USERS); -$role = user_role_get($roleId); - -if(empty($role)) { +try { + $roleInfo = UserRole::byId($roleId); +} catch(UserRoleNotFoundException $ex) { echo render_error(404); return; } -$usersPagination = new Pagination($role['role_user_count'], 15); +$pagination = new Pagination($roleInfo->getUserCount(), 15); -$roles = user_role_all(); +$roles = UserRole::all(); $getUsers = DB::prepare(sprintf( ' @@ -148,20 +150,19 @@ $getUsers = DB::prepare(sprintf( $orderFields[$orderBy]['column'], $orderDir, \Misuzu\Users\UserRelation::TYPE_FOLLOW, - $usersPagination->getOffset(), - $usersPagination->getRange() + $pagination->getOffset(), + $pagination->getRange() )); -$getUsers->bind('role_id', $role['role_id']); +$getUsers->bind('role_id', $roleInfo->getId()); $getUsers->bind('current_user_id', User::hasCurrent() ? User::getCurrent()->getId() : 0); $users = $getUsers->fetchAll(); -if(empty($users)) { +if(empty($users)) http_response_code(404); -} Template::render('user.listing', [ 'roles' => $roles, - 'role' => $role, + 'role' => $roleInfo, 'users' => $users, 'order_fields' => $orderFields, 'order_directions' => $orderDirs, @@ -169,5 +170,5 @@ Template::render('user.listing', [ 'order_direction' => $orderDir, 'order_default' => $defaultOrder, 'can_manage_users' => $canManageUsers, - 'users_pagination' => $usersPagination, + 'users_pagination' => $pagination, ]); diff --git a/public/profile.php b/public/profile.php index 4a6eee0d..2079fa29 100644 --- a/public/profile.php +++ b/public/profile.php @@ -26,20 +26,15 @@ $currentUser = User::getCurrent(); $viewingAsGuest = $currentUser === null; $currentUserId = $viewingAsGuest ? 0 : $currentUser->getId(); $viewingOwnProfile = $currentUserId === $profileUser->getId(); -define('DUMB_SHIT', true); $isBanned = $profileUser->hasActiveWarning(); $userPerms = perms_get_user($currentUserId)[MSZ_PERMS_USER]; $canManageWarnings = perms_check($userPerms, MSZ_PERM_USER_MANAGE_WARNINGS); $canEdit = !$isBanned && UserSession::hasCurrent() - && ( - $viewingOwnProfile - || user_check_super($currentUserId) - || ( - perms_check($userPerms, MSZ_PERM_USER_MANAGE_USERS) - && user_check_authority($currentUserId, $profileUser->getId()) - ) - ); + && ($viewingOwnProfile || $currentUserId->isSuper() || ( + perms_check($userPerms, MSZ_PERM_USER_MANAGE_USERS) + && $currentUserId->hasAuthorityOver($profileUser) + )); if($isEditing) { if(!$canEdit) { diff --git a/public/settings/account.php b/public/settings/account.php index 07e070b7..f27dfa49 100644 --- a/public/settings/account.php +++ b/public/settings/account.php @@ -4,6 +4,8 @@ namespace Misuzu; use Misuzu\AuditLog; use Misuzu\Config; use Misuzu\Users\User; +use Misuzu\Users\UserRole; +use Misuzu\Users\UserRoleNotFoundException; use Misuzu\Users\UserSession; use chillerlan\QRCode\QRCode; use chillerlan\QRCode\QROptions; @@ -23,24 +25,25 @@ $twoFactorInfo = user_totp_info($currentUserId); $isVerifiedRequest = CSRF::validateRequest(); if(!$isRestricted && $isVerifiedRequest && !empty($_POST['role'])) { - $roleId = (int)($_POST['role']['id'] ?? 0); + try { + $roleInfo = UserRole::byId((int)($_POST['role']['id'] ?? 0)); + } catch(UserRoleNotFoundException $ex) {} - if($roleId > 0 && user_role_has($currentUserId, $roleId)) { + if(empty($roleInfo) || !$currentUser->hasRole($roleInfo)) + $errors[] = "You're trying to modify a role that hasn't been assigned to you."; + else { switch($_POST['role']['mode'] ?? '') { case 'display': - user_role_set_display($currentUserId, $roleId); + $currentUser->setDisplayRole($roleInfo); break; case 'leave': - if(user_role_can_leave($roleId)) { - user_role_remove($currentUserId, $roleId); - } else { + if($roleInfo->getCanLeave()) + $currentUser->removeRole($roleInfo); + else $errors[] = "You're not allow to leave this role, an administrator has to remove it for you."; - } break; } - } else { - $errors[] = "You're trying to modify a role that hasn't been assigned to you."; } } @@ -127,13 +130,9 @@ if($isVerifiedRequest && !empty($_POST['current_password'])) { } } -$userRoles = user_role_all_user($currentUserId); - Template::render('settings.account', [ 'errors' => $errors, - 'current_email' => $currentUser->getEMailAddress(), - 'user_roles' => $userRoles, - 'user_display_role' => user_role_get_display($currentUserId), + 'settings_user' => $currentUser, 'is_restricted' => $isRestricted, 'settings_2fa_enabled' => $twoFactorInfo['totp_enabled'], ]); diff --git a/src/Colour.php b/src/Colour.php index 33e9ab53..832dbab2 100644 --- a/src/Colour.php +++ b/src/Colour.php @@ -14,7 +14,7 @@ class Colour { private int $raw = 0; public function __construct(?int $raw = 0) { - $this->setRaw($raw); + $this->setRaw($raw ?? 0); } public static function none(): self { diff --git a/src/Emoticon.php b/src/Emoticon.php index 53140580..611f5290 100644 --- a/src/Emoticon.php +++ b/src/Emoticon.php @@ -36,11 +36,11 @@ final class Emoticon { return $this; } - public function getHierarchy(): int { + public function getRank(): int { return $this->emote_hierarchy ?? 0; } - public function setHierarchy(int $hierarchy): self { - $this->emote_hierarchy = $hierarchy; + public function setRank(int $rank): self { + $this->emote_hierarchy = $rank; return $this; } @@ -111,7 +111,7 @@ final class Emoticon { } $saved = $save->bind('order', $this->getOrder()) - ->bind('hierarchy', $this->getHierarchy()) + ->bind('hierarchy', $this->getRank()) ->bind('url', $this->getUrl()) ->execute(); diff --git a/src/HasRankInterface.php b/src/HasRankInterface.php new file mode 100644 index 00000000..5aedcb25 --- /dev/null +++ b/src/HasRankInterface.php @@ -0,0 +1,7 @@ + $strings, 'Image' => $emote->getUrl(), - 'Hierarchy' => $emote->getHierarchy(), + 'Hierarchy' => $emote->getRank(), ]; } @@ -275,7 +275,7 @@ final class SockChatHandler extends Handler { 'user_id' => $userInfo->getId(), 'username' => $userInfo->getUsername(), 'colour_raw' => $userInfo->getColourRaw(), - 'hierarchy' => $userInfo->getHierarchy(), + 'hierarchy' => $userInfo->getRank(), 'is_silenced' => date('c', $userInfo->isSilenced() || $userInfo->isBanned() ? ($userInfo->isActiveWarningPermanent() ? strtotime('10 years') : $userInfo->getActiveWarningExpiration()) : 0), 'perms' => $perms, ]; diff --git a/src/Users/User.php b/src/Users/User.php index 28cefea2..8e531fb8 100644 --- a/src/Users/User.php +++ b/src/Users/User.php @@ -3,6 +3,7 @@ namespace Misuzu\Users; use Misuzu\Colour; use Misuzu\DB; +use Misuzu\HasRankInterface; use Misuzu\Memoizer; use Misuzu\Pagination; use Misuzu\TOTP; @@ -10,8 +11,9 @@ use Misuzu\Net\IPAddress; class UserException extends UsersException {} // this naming definitely won't lead to confusion down the line! class UserNotFoundException extends UserException {} +class UserCreationFailedException extends UserException {} -class User { +class User implements HasRankInterface { public const NAME_MIN_LENGTH = 3; // Minimum username length public const NAME_MAX_LENGTH = 16; // Maximum username length, unless your name is Flappyzor(WorldwideOnline2018through2019through2020) public const NAME_REGEX = '[A-Za-z0-9-_]+'; // Username character constraint @@ -22,6 +24,17 @@ class User { // Password hashing algorithm public const PASSWORD_ALGO = PASSWORD_ARGON2ID; + // Order constants for ::all function + public const ORDER_ID = 'id'; + public const ORDER_NAME = 'name'; + public const ORDER_COUNTRY = 'country'; + public const ORDER_CREATED = 'registered'; + public const ORDER_ACTIVE = 'last-online'; + public const ORDER_FORUM_TOPICS = 'forum-topics'; + public const ORDER_FORUM_POSTS = 'forum-posts'; + public const ORDER_FOLLOWING = 'following'; + public const ORDER_FOLLOWERS = 'followers'; + // Database fields // TODO: update all references to use getters and setters and mark all of these as private public $user_id = -1; @@ -52,7 +65,7 @@ class User { public const TABLE = 'users'; private const QUERY_SELECT = 'SELECT %1$s FROM `' . DB::PREFIX . self::TABLE . '` AS '. self::TABLE; - private const SELECT = '%1$s.`user_id`, %1$s.`username`, %1$s.`password`, %1$s.`email`, %1$s.`user_super`, %1$s.`user_title`, ' + private const SELECT = '%1$s.`user_id`, %1$s.`username`, %1$s.`password`, %1$s.`email`, %1$s.`user_super`, %1$s.`user_title`' . ', %1$s.`user_country`, %1$s.`user_colour`, %1$s.`display_role`, %1$s.`user_totp_key`' . ', %1$s.`user_about_content`, %1$s.`user_about_parser`' . ', %1$s.`user_signature_content`, %1$s.`user_signature_parser`' @@ -119,7 +132,11 @@ class User { return get_country_name($this->getCountry()); } - public function getColour(): Colour { + public function getColour(): Colour { // Swaps role colour in if user has no personal colour + // TODO: Check inherit flag and grab role colour instead + return new Colour($this->getColourRaw()); + } + public function getUserColour(): Colour { // Only ever gets the user's actual colour return new Colour($this->getColourRaw()); } public function getColourRaw(): int { @@ -137,8 +154,23 @@ class User { return $this->user_active === null ? -1 : $this->user_active; } - public function getHierarchy(): int { - return ($userId = $this->getId()) < 1 ? 0 : user_get_hierarchy($userId); + private $userRank = null; + public function getRank(): int { + if($this->userRank === null) + $this->userRank = (int)DB::prepare( + 'SELECT MAX(`role_hierarchy`)' + . ' FROM `' . DB::PREFIX . UserRole::TABLE . '`' + . ' WHERE `role_id` IN (SELECT `role_id` FROM `' . DB::PREFIX . UserRoleRelation::TABLE . '` WHERE `user_id` = :user)' + )->bind('user', $this->getId())->fetchColumn(); + return $this->userRank; + } + public function hasAuthorityOver(HasRankInterface $other): bool { + // Don't even bother checking if we're a super user + if($this->isSuper()) + return true; + if($other instanceof self && $other->getId() === $this->getId()) + return true; + return $this->getRank() > $other->getRank(); } public function hasPassword(): bool { @@ -164,6 +196,29 @@ class User { public function getDisplayRoleId(): int { return $this->display_role < 1 ? -1 : $this->display_role; } + public function setDisplayRoleId(int $roleId): self { + $this->display_role = $roleId < 1 ? -1 : $roleId; + + // TEMPORARY!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // This DB update statement should be removed when a global update/save/whatever call exists + DB::prepare('UPDATE `' . DB::PREFIX . self::TABLE . '` SET `display_role` = :role WHERE `user_id` = :user') + ->bind('role', $this->display_role) + ->bind('user', $this->user_id) + ->execute(); + + return $this; + } + public function getDisplayRole(): UserRole { + return $this->getRoleRelations()[$this->getDisplayRoleId()]->getRole(); + } + public function setDisplayRole(UserRole $role): self { + if($this->hasRole($role)) + $this->setDisplayRoleId($role->getId()); + return $this; + } + public function isDisplayRole(UserRole $role): bool { + return $this->getDisplayRoleId() === $role->getId(); + } public function hasTOTP(): bool { return !empty($this->user_totp_key); @@ -225,6 +280,54 @@ class User { return $this->commentPermsArray; } + /********* + * ROLES * + *********/ + + private $roleRelations = null; + + public function addRole(UserRole $role, bool $display = false): void { + if(!$this->hasRole($role)) + $this->roleRelations[$role->getId()] = UserRoleRelation::create($this, $role); + + if($display && $this->isDisplayRole($role)) + $this->setDisplayRole($role); + } + + public function removeRole(UserRole $role): void { + if(!$this->hasRole($role)) + return; + UserRoleRelation::destroy($this, $role); + unset($this->roleRelations[$role->getId()]); + + if($this->isDisplayRole($role)) + $this->setDisplayRoleId(UserRole::DEFAULT); + } + + public function getRoleRelations(): array { + if($this->roleRelations === null) { + $this->roleRelations = []; + foreach(UserRoleRelation::byUser($this) as $rel) + $this->roleRelations[$rel->getRoleId()] = $rel; + } + return $this->roleRelations; + } + + public function getRoles(): array { + $roles = []; + foreach($this->getRoleRelations() as $rel) + $roles[$rel->getRoleId()] = $rel->getRole(); + return $roles; + } + + public function hasRole(UserRole $role): bool { + return array_key_exists($role->getId(), $this->getRoleRelations()); + } + + /************* + * RELATIONS * + *************/ + private $relationCache = []; private $relationFollowingCount = -1; private $relationFollowersCount = -1; @@ -283,6 +386,10 @@ class User { return $this->relationFollowingCount; } + /*************** + * FORUM STATS * + ***************/ + private $forumTopicCount = -1; private $forumPostCount = -1; @@ -327,6 +434,10 @@ class User { return UserWarning::byProfile($this, $viewer); } + /************** + * LOCAL USER * + **************/ + public function setCurrent(): void { self::$localUser = $this; } @@ -340,6 +451,10 @@ class User { return self::$localUser !== null; } + /************** + * VALIDATION * + **************/ + public static function validateUsername(string $name): string { if($name !== trim($name)) return 'trim'; @@ -418,25 +533,22 @@ class User { string $password, string $email, string $ipAddress - ): ?self { - $createUser = DB::prepare(' - INSERT INTO `msz_users` ( - `username`, `password`, `email`, `register_ip`, - `last_ip`, `user_country`, `display_role` - ) VALUES ( - :username, :password, LOWER(:email), INET6_ATON(:register_ip), - INET6_ATON(:last_ip), :user_country, 1 - ) - ') ->bind('username', $username)->bind('email', $email) - ->bind('register_ip', $ipAddress)->bind('last_ip', $ipAddress) + ): self { + $createUser = DB::prepare( + 'INSERT INTO `' . DB::PREFIX . self::TABLE . '` (`username`, `password`, `email`, `register_ip`, `last_ip`, `user_country`, `display_role`)' + . ' VALUES (:username, :password, LOWER(:email), INET6_ATON(:register_ip), INET6_ATON(:last_ip), :user_country, 1)' + ) ->bind('username', $username) + ->bind('email', $email) + ->bind('register_ip', $ipAddress) + ->bind('last_ip', $ipAddress) ->bind('password', self::hashPassword($password)) ->bind('user_country', IPAddress::country($ipAddress)) ->executeGetId(); if($createUser < 1) - return null; + throw new UserCreationFailedException; - return static::byId($createUser); + return self::byId($createUser); } private static function getMemoizer() { diff --git a/src/Users/UserRole.php b/src/Users/UserRole.php new file mode 100644 index 00000000..401e7507 --- /dev/null +++ b/src/Users/UserRole.php @@ -0,0 +1,225 @@ +role_id < 1 ? -1 : $this->role_id; + } + + public function getRank(): int { + return $this->role_hierarchy; + } + public function setRank(int $rank): self { + $this->role_hierarchy = $rank; + return $this; + } + + public function getName(): string { + return $this->role_name; + } + public function setName(string $name): self { + $this->role_name = $name; + return $this; + } + + public function getTitle(): string { + return $this->role_title ?? ''; + } + public function setTitle(string $title): self { + $this->role_title = empty($title) ? null : $title; + return $this; + } + + public function getDescription(): string { + return $this->role_description ?? ''; + } + public function setDescription(string $description): self { + $this->role_description = empty($description) ? null : $description; + return $this; + } + + public function isHidden(): bool { + return boolval($this->role_hidden); + } + public function setHidden(bool $hidden): self { + $this->role_hidden = $hidden ? 1 : 0; + return $this; + } + + public function getCanLeave(): bool { + return boolval($this->role_can_leave); + } + public function setCanLeave(bool $canLeave): bool { + $this->role_can_leave = $canLeave ? 1 : 0; + return $this; + } + + // Provided just because, avoid using these for validations sake + public function getColourRaw(): ?int { + return $this->role_colour; + } + public function setColourRaw(?int $colour): self { + $this->role_colour = $colour; + return $this; + } + + public function getColour(): Colour { + if($this->colour === null || ($this->getColourRaw() ?? 0x40000000) !== $this->colour->getRaw()) + $this->colour = new Colour($this->role_colour ?? 0x40000000); + return $this->colour; + } + public function setColour(Colour $colour): self { + $this->role_colour = $colour->getInherit() ? null : $colour->getRaw(); + $this->colour = $this->colour; + return $this; + } + + public function getCreatedTime(): int { + return $this->role_created === null ? -1 : $this->role_created; + } + + public function getUserCount(): int { + if($this->userCount < 0) + $this->userCount = UserRoleRelation::countUsers($this); + return $this->userCount; + } + + public function isDefault(): bool { + return $this->getId() === self::DEFAULT; + } + + public function hasAuthorityOver(HasRankInterface $other): bool { + if($other instanceof User && $other->isSuper()) + return false; + return $this->getRank() > $other->getRank(); + } + + public function save(): void { + $isInsert = $this->role_id < 1; + if($isInsert) { + $set = DB::prepare( + 'INSERT INTO `' . DB::PREFIX . self::TABLE . '` (`role_hierarchy`, `role_name`, `role_title`, `role_description`, `role_hidden`, `role_can_leave`, `role_colour`)' + . ' VALUES (:rank, :name, :title, :desc, :hide, :can_leave, :colour)' + ); + } else { + $set = DB::prepare( + 'UPDATE `' . DB::PREFIX . self::TABLE . '` SET' + . ' `role_hierarchy` = :rank, `role_name` = :name, `role_title` = :title,' + . ' `role_description` = :desc, `role_hidden` = :hide, `role_can_leave` = :can_leave, `role_colour` = :colour' + . ' WHERE `role_id` = :role' + )->bind('role', $this->role_id); + } + + $set->bind('rank', $this->role_hierarchy) + ->bind('name', $this->role_name) + ->bind('title', $this->role_title) + ->bind('desc', $this->role_description) + ->bind('hide', $this->role_hidden) + ->bind('can_leave', $this->role_can_leave) + ->bind('colour', $this->role_colour); + + if($isInsert) { + $this->role_id = $set->executeGetId(); + $this->role_created = time(); + } else $set->execute(); + } + + private static function countQueryBase(): string { + return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE)); + } + public static function countAll(bool $showHidden = false): int { + return (int)DB::prepare( + self::countQueryBase() + . ($showHidden ? '' : ' WHERE `role_hidden` = 0') + )->fetchColumn(); + } + + private static function memoizer() { + static $memoizer = null; + if($memoizer === null) + $memoizer = new Memoizer; + return $memoizer; + } + + private static function byQueryBase(): string { + return sprintf(self::QUERY_SELECT, sprintf(self::SELECT, self::TABLE)); + } + public static function byId(int $roleId): self { + $object = DB::prepare( + self::byQueryBase() . ' WHERE `role_id` = :role' + ) ->bind('role', $roleId) + ->fetchObject(self::class); + if(!$object) + throw new UserRoleNotFoundException; + return $object; + } + public static function byDefault(): self { + return self::byId(self::DEFAULT); + } + public static function all(bool $showHidden = false, ?Pagination $pagination = null): array { + $query = self::byQueryBase(); + + if(!$showHidden) + $query .= ' WHERE `role_hidden` = 0'; + + if($pagination !== null) + $query .= ' LIMIT :range OFFSET :offset'; + + $getObjects = DB::prepare($query); + + if($pagination !== null) + $getObjects->bind('range', $pagination->getRange()) + ->bind('offset', $pagination->getOffset()); + + return $getObjects->fetchObjects(self::class); + } + + // to satisfy the fucked behaviour array_diff has + public function __toString() { + return md5($this->getId() . '#' . $this->getName()); + } + + // Twig shim for the roles list on the members page, don't use this class as an array normally. + public function offsetExists($offset): bool { + return $offset === 'name' || $offset === 'id'; + } + public function offsetGet($offset) { + return $this->{'get' . ucfirst($offset)}(); + } + public function offsetSet($offset, $value) {} + public function offsetUnset($offset) {} +} diff --git a/src/Users/UserRoleRelation.php b/src/Users/UserRoleRelation.php new file mode 100644 index 00000000..0fc682d8 --- /dev/null +++ b/src/Users/UserRoleRelation.php @@ -0,0 +1,88 @@ +user_id < 1 ? -1 : $this->user_id; + } + public function getUser(): User { + if($this->user === null) + $this->user = User::byId($this->getUserId()); + return $this->user; + } + + public function getRoleId(): int { + return $this->role_id < 1 ? -1 : $this->role_id; + } + public function getRole(): UserRole { + if($this->role === null) + $this->role = UserRole::byId($this->getRoleId()); + return $this->role; + } + + public function delete(): void { + self::destroy($this->getUser(), $this->getRole()); + } + + public static function destroy(User $user, UserRole $role): void { + DB::prepare('DELETE FROM `' . DB::PREFIX . self::TABLE . '` WHERE `user_id` = :user AND `role_id` = :role') + ->bind('user', $user->getId()) + ->bind('role', $role->getId()) + ->execute(); + } + + public static function purge(User $user): void { + DB::prepare('DELETE FROM `' . DB::PREFIX . self::TABLE . '` WHERE `user_id` = :user') + ->bind('user', $user->getId()) + ->execute(); + } + + public static function create(User $user, UserRole $role): self { + $create = DB::prepare( + 'REPLACE INTO `' . DB::PREFIX . self::TABLE . '` (`user_id`, `role_id`)' + . ' VALUES (:user, :role)' + ) ->bind('user', $user->getId()) + ->bind('role', $role->getId()) + ->execute(); + + // data is predictable, just create a "fake" + $object = new static; + $object->user = $user; + $object->user_id = $user->getId(); + $object->role = $role; + $object->role_id = $role->getId(); + return $object; + } + + private static function countQueryBase(): string { + return sprintf(self::QUERY_SELECT, sprintf('COUNT(*)', self::TABLE)); + } + public static function countUsers(UserRole $role): int { + return (int)DB::prepare(self::countQueryBase() . ' WHERE `role_id` = :role') + ->bind('role', $role->getId()) + ->fetchColumn(); + } + + private static function byQueryBase(): string { + return sprintf(self::QUERY_SELECT, sprintf(self::SELECT, self::TABLE)); + } + public static function byUser(User $user): array { + return DB::prepare(self::byQueryBase() . ' WHERE `user_id` = :user') + ->bind('user', $user->getId()) + ->fetchObjects(self::class); + } +} diff --git a/src/Users/role.php b/src/Users/role.php deleted file mode 100644 index 544494b4..00000000 --- a/src/Users/role.php +++ /dev/null @@ -1,145 +0,0 @@ -bind('user_id', $userId); - $addRole->bind('role_id', $roleId); - return $addRole->execute(); -} - -function user_role_remove(int $userId, int $roleId): bool { - $removeRole = \Misuzu\DB::prepare(' - DELETE FROM `msz_user_roles` - WHERE `user_id` = :user_id - AND `role_id` = :role_id - '); - $removeRole->bind('user_id', $userId); - $removeRole->bind('role_id', $roleId); - return $removeRole->execute(); -} - -function user_role_can_leave(int $roleId): bool { - $canLeaveRole = \Misuzu\DB::prepare(' - SELECT `role_can_leave` != 0 - FROM `msz_roles` - WHERE `role_id` = :role_id - '); - $canLeaveRole->bind('role_id', $roleId); - return (bool)$canLeaveRole->fetchColumn(); -} - -function user_role_has(int $userId, int $roleId): bool { - $hasRole = \Misuzu\DB::prepare(' - SELECT COUNT(`role_id`) > 0 - FROM `msz_user_roles` - WHERE `user_id` = :user_id - AND `role_id` = :role_id - '); - $hasRole->bind('user_id', $userId); - $hasRole->bind('role_id', $roleId); - return (bool)$hasRole->fetchColumn(); -} - -function user_role_set_display(int $userId, int $roleId): bool { - if(!user_role_has($userId, $roleId)) - return false; - - $setDisplay = \Misuzu\DB::prepare(' - UPDATE `msz_users` - SET `display_role` = :role_id - WHERE `user_id` = :user_id - '); - $setDisplay->bind('user_id', $userId); - $setDisplay->bind('role_id', $roleId); - - return $setDisplay->execute(); -} - -function user_role_get_display(int $userId): int { - if($userId < 1) - return MSZ_ROLE_MAIN; - - $fetchRole = \Misuzu\DB::prepare(' - SELECT `display_role` - FROM `msz_users` - WHERE `user_id` = :user_id - '); - $fetchRole->bind('user_id', $userId); - return (int)$fetchRole->fetchColumn(0, MSZ_ROLE_MAIN); -} - -function user_role_all_user(int $userId): array { - $getUserRoles = \Misuzu\DB::prepare(' - SELECT - r.`role_id`, r.`role_name`, r.`role_description`, - r.`role_colour`, r.`role_can_leave`, r.`role_created` - FROM `msz_user_roles` AS ur - LEFT JOIN `msz_roles` AS r - ON r.`role_id` = ur.`role_id` - WHERE ur.`user_id` = :user_id - ORDER BY r.`role_hierarchy` DESC - '); - $getUserRoles->bind('user_id', $userId); - return $getUserRoles->fetchAll(); -} - -function user_role_all(bool $withHidden = false) { - return \Misuzu\DB::query(sprintf( - ' - SELECT - r.`role_id`, r.`role_name`, r.`role_description`, - r.`role_colour`, r.`role_can_leave`, r.`role_created`, - ( - SELECT COUNT(`user_id`) - FROM `msz_user_roles` - WHERE `role_id` = r.`role_id` - ) AS `role_user_count` - FROM `msz_roles` AS r - %s - ORDER BY `role_id` - ', - $withHidden ? '' : 'WHERE `role_hidden` = 0' - ))->fetchAll(); -} - -function user_role_get(int $roleId): array { - $getRole = \Misuzu\DB::prepare(' - SELECT - r.`role_id`, r.`role_name`, r.`role_description`, - r.`role_colour`, r.`role_can_leave`, r.`role_created`, - ( - SELECT COUNT(`user_id`) - FROM `msz_user_roles` - WHERE `role_id` = r.`role_id` - ) AS `role_user_count` - FROM `msz_roles` AS r - WHERE `role_id` = :role_id - '); - $getRole->bind('role_id', $roleId); - return $getRole->fetch(); -} - -function user_role_check_authority(int $userId, int $roleId): bool { - $checkHierarchy = \Misuzu\DB::prepare(' - SELECT ( - SELECT MAX(r.`role_hierarchy`) - FROM `msz_roles` AS r - LEFT JOIN `msz_user_roles` AS ur - ON ur.`role_id` = r.`role_id` - WHERE ur.`user_id` = :user_id - ) > ( - SELECT `role_hierarchy` - FROM `msz_roles` - WHERE `role_id` = :role_id - ) - '); - $checkHierarchy->bind('user_id', $userId); - $checkHierarchy->bind('role_id', $roleId); - return (bool)$checkHierarchy->fetchColumn(); -} diff --git a/src/Users/user_legacy.php b/src/Users/user_legacy.php index 8b6d87ea..7018a6e1 100644 --- a/src/Users/user_legacy.php +++ b/src/Users/user_legacy.php @@ -52,12 +52,6 @@ function user_id_from_username(string $username): int { return (int)$getId->fetchColumn(0, 0); } -function user_username_from_id(int $userId): string { - $getName = \Misuzu\DB::prepare('SELECT `username` FROM `msz_users` WHERE `user_id` = :user_id'); - $getName->bind('user_id', $userId); - return $getName->fetchColumn(0, ''); -} - function user_bump_last_active(int $userId, string $ipAddress = null): void { $bumpUserLast = \Misuzu\DB::prepare(' UPDATE `msz_users` @@ -70,68 +64,6 @@ function user_bump_last_active(int $userId, string $ipAddress = null): void { $bumpUserLast->execute(); } -function user_get_last_ip(int $userId): string { - $getAddress = \Misuzu\DB::prepare(' - SELECT INET6_NTOA(`last_ip`) - FROM `msz_users` - WHERE `user_id` = :user_id - '); - $getAddress->bind('user_id', $userId); - return $getAddress->fetchColumn(0, ''); -} - -function user_check_super(int $userId): bool { - static $superUsers = []; - - if(!isset($superUsers[$userId])) { - $checkSuperUser = \Misuzu\DB::prepare(" - SELECT `user_super` - FROM `msz_users` - WHERE `user_id` = :user_id - "); - $checkSuperUser->bind('user_id', $userId); - $superUsers[$userId] = (bool)$checkSuperUser->fetchColumn(0, false); - } - - return $superUsers[$userId]; -} - -function user_check_authority(int $userId, int $subjectId, bool $canManageSelf = true): bool { - if($canManageSelf && $userId === $subjectId) - return true; - - $checkHierarchy = \Misuzu\DB::prepare(' - SELECT ( - SELECT MAX(r.`role_hierarchy`) - FROM `msz_roles` AS r - LEFT JOIN `msz_user_roles` AS ur - ON ur.`role_id` = r.`role_id` - WHERE ur.`user_id` = :user_id - ) > ( - SELECT MAX(r.`role_hierarchy`) - FROM `msz_roles` AS r - LEFT JOIN `msz_user_roles` AS ur - ON ur.`role_id` = r.`role_id` - WHERE ur.`user_id` = :subject_id - ) - '); - $checkHierarchy->bind('user_id', $userId); - $checkHierarchy->bind('subject_id', $subjectId); - return (bool)$checkHierarchy->fetchColumn(0, false); -} - -function user_get_hierarchy(int $userId): int { - $getHierarchy = \Misuzu\DB::prepare(' - SELECT MAX(r.`role_hierarchy`) - FROM `msz_roles` AS r - LEFT JOIN `msz_user_roles` AS ur - ON ur.`role_id` = r.`role_id` - WHERE ur.`user_id` = :user_id - '); - $getHierarchy->bind('user_id', $userId); - return (int)$getHierarchy->fetchColumn(0, 0); -} - define('MSZ_E_USER_BIRTHDATE_OK', 0); define('MSZ_E_USER_BIRTHDATE_USER', 1); define('MSZ_E_USER_BIRTHDATE_DATE', 2); diff --git a/templates/manage/users/role.twig b/templates/manage/users/role.twig index eeb8fd54..b13616bf 100644 --- a/templates/manage/users/role.twig +++ b/templates/manage/users/role.twig @@ -4,37 +4,37 @@ {% from '_layout/input.twig' import input_csrf, input_text, input_checkbox %} {% block manage_content %} -
+ {{ input_csrf() }}
- {{ container_title(edit_role is defined ? 'Editing ' ~ edit_role.role_name ~ ' (' ~ edit_role.role_id ~ ')' : 'Creating a new role') }} + {{ container_title(role_info is not null ? 'Editing ' ~ role_info.name ~ ' (' ~ role_info.id ~ ')' : 'Creating a new role') }} @@ -46,28 +46,28 @@ @@ -79,7 +79,7 @@
@@ -89,6 +89,6 @@ {{ permissions_table(permissions, not can_manage_perms) }} - +
{% endblock %} diff --git a/templates/manage/users/roles.twig b/templates/manage/users/roles.twig index 3929e50b..06a41216 100644 --- a/templates/manage/users/roles.twig +++ b/templates/manage/users/roles.twig @@ -32,8 +32,8 @@ {% for role in manage_roles %} -
- +
+
@@ -44,23 +44,23 @@
- {{ role.role_name }} + {{ role.name }}
- {% if role.users > 0 %} + {% if role.userCount > 0 %}
- {{ role.users|number_format }} + {{ role.userCount|number_format }}
{% endif %} - {% if role.role_title|default('')|length > 0 %} + {% if role.title is not empty %}
- {{ role.role_title }} + {{ role.title }}
{% endif %}
diff --git a/templates/manage/users/user.twig b/templates/manage/users/user.twig index e0ebb92f..8b79c7cd 100644 --- a/templates/manage/users/user.twig +++ b/templates/manage/users/user.twig @@ -3,10 +3,10 @@ {% from 'manage/macros.twig' import permissions_table %} {% from '_layout/input.twig' import input_csrf, input_text, input_checkbox, input_file, input_select, input_colour %} -{% set site_link = url('user-profile', {'user': manage_user.user_id|default(0)}) %} +{% set site_link = url('user-profile', {'user': user_info.id}) %} {% block manage_content %} -
+
{% if manage_notices|length > 0 %}
@@ -17,64 +17,64 @@
{% endif %} -
- {{ container_title('Editing ' ~ manage_user.username ~ ' (' ~ manage_user.user_id ~ ')') }} + + {{ container_title('Editing ' ~ user_info.username ~ ' (' ~ user_info.id ~ ')') }} {{ input_csrf() }}
@@ -99,25 +99,25 @@ - {{ input_colour(can_edit_user ? 'colour[hex]' : '', '', '#%s'|format(user_colour.hex)) }} + {{ input_colour(can_edit_user ? 'colour[hex]' : '', '', '#%s'|format(user_info.userColour.hex)) }}
{# TODO: if the hierarchy of the current user is too low to touch the role then opacity should be lowered and input disabled #}
{% for role in manage_roles %} -
- {% for role in user_roles %} - {% set is_display_role = user_display_role == role.role_id %} + {% for role in settings_user.roles %} + {% set is_display_role = settings_user.isDisplayRole(role) %} -
+
- {{ role.role_name }} + {{ role.name }}
- {{ role.role_description }} + {{ role.description }}
{{ input_csrf() }} - {{ input_hidden('role[id]', role.role_id) }} + {{ input_hidden('role[id]', role.id) }} - diff --git a/templates/user/listing.twig b/templates/user/listing.twig index 1f6088c8..64b3b3fd 100644 --- a/templates/user/listing.twig +++ b/templates/user/listing.twig @@ -2,7 +2,7 @@ {% from 'macros.twig' import container_title %} {% from 'user/macros.twig' import user_card %} -{% set url_role = role.role_id != 1 ? role.role_id : 0 %} +{% set url_role = role.id > 1 ? role.id : 0 %} {% set url_sort = order_field == order_default ? '' : order_field %} {% set url_direction = order_fields[order_field]['default-dir'] == order_direction ? '' : order_direction %} {% set canonical_url = url('user-list', { @@ -11,7 +11,7 @@ 'direction': url_direction, 'page': users_pagination.page|default(0) > 2 ? users_pagination.page : 0, }) %} -{% set title = role.role_id == 1 ? 'Members' : 'Role » ' ~ role.role_name %} +{% set title = role.id == 1 ? 'Members' : 'Role » ' ~ role.name %} {% set manage_link = url('manage-users') %} {% macro member_nav(roles, role_id, orders, order, directions, direction, users_pagination, url_role, url_sort, url_direction) %} @@ -20,7 +20,7 @@
- {{ input_select('r', roles, role_id, 'role_name', 'role_id', false, 'userlist__select') }} + {{ input_select('r', roles, role_id, 'name', 'id', false, 'userlist__select') }} {{ input_select('ss', orders, order, 'title', null, false, 'userlist__select') }} {{ input_select('sd', directions, direction, null, null, false, 'userlist__select') }} @@ -38,13 +38,13 @@ {% block content %} {% from _self import member_nav %} {% set member_nav = member_nav( - roles, role.role_id, + roles, role.id, order_fields, order_field, order_directions, order_direction, users_pagination, url_role, url_sort, url_direction ) %} -
+
{{ member_nav }}
@@ -62,7 +62,7 @@
{% endif %} -
+
{{ member_nav }}
{% endblock %}