Rewrote roles code to be OOP.
This commit is contained in:
parent
3521d06cef
commit
0bf8fde6c2
24 changed files with 652 additions and 573 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<?php
|
||||
// TODO: UNFUCK THIS FILE
|
||||
namespace Misuzu;
|
||||
|
||||
use Misuzu\Users\User;
|
||||
use Misuzu\Users\UserRole;
|
||||
use Misuzu\Users\UserRoleNotFoundException;
|
||||
|
||||
require_once '../../../misuzu.php';
|
||||
|
||||
|
@ -11,23 +12,28 @@ if(!User::hasCurrent() || !perms_check_user(MSZ_PERMS_USER, User::getCurrent()->
|
|||
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 ?? [],
|
||||
]);
|
||||
|
|
|
@ -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,
|
||||
]);
|
||||
|
|
|
@ -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 ?? [],
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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,
|
||||
]);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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'],
|
||||
]);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
7
src/HasRankInterface.php
Normal file
7
src/HasRankInterface.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
interface HasRankInterface {
|
||||
public function getRank(): int;
|
||||
public function HasAuthorityOver(self $other): bool;
|
||||
}
|
|
@ -107,7 +107,7 @@ final class SockChatHandler extends Handler {
|
|||
$out[] = [
|
||||
'Text' => $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,
|
||||
];
|
||||
|
|
|
@ -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() {
|
||||
|
|
225
src/Users/UserRole.php
Normal file
225
src/Users/UserRole.php
Normal file
|
@ -0,0 +1,225 @@
|
|||
<?php
|
||||
namespace Misuzu\Users;
|
||||
|
||||
use ArrayAccess;
|
||||
use Misuzu\Colour;
|
||||
use Misuzu\DB;
|
||||
use Misuzu\HasRankInterface;
|
||||
use Misuzu\Memoizer;
|
||||
use Misuzu\Pagination;
|
||||
|
||||
class UserRoleException extends UsersException {}
|
||||
class UserRoleNotFoundException extends UserRoleException {}
|
||||
class UserRoleCreationFailedException extends UserRoleException {}
|
||||
|
||||
class UserRole implements ArrayAccess, HasRankInterface {
|
||||
public const DEFAULT = 1;
|
||||
|
||||
// Database fields
|
||||
private $role_id = -1;
|
||||
private $role_hierarchy = 1;
|
||||
private $role_name = '';
|
||||
private $role_title = null;
|
||||
private $role_description = null;
|
||||
private $role_hidden = 0;
|
||||
private $role_can_leave = 0;
|
||||
private $role_colour = null;
|
||||
private $role_created = null;
|
||||
|
||||
public const TABLE = 'roles';
|
||||
private const QUERY_SELECT = 'SELECT %1$s FROM `' . DB::PREFIX . self::TABLE . '` AS '. self::TABLE;
|
||||
private const SELECT = '%1$s.`role_id`, %1$s.`role_hierarchy`, %1$s.`role_name`, %1$s.`role_title`, %1$s.`role_description`'
|
||||
. ', %1$s.`role_hidden`, %1$s.`role_can_leave`, %1$s.`role_colour`'
|
||||
. ', UNIX_TIMESTAMP(%1$s.`role_created`) AS `role_created`';
|
||||
|
||||
private $colour = null;
|
||||
private $users = [];
|
||||
private $userCount = -1;
|
||||
|
||||
public function getId(): int {
|
||||
return $this->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) {}
|
||||
}
|
88
src/Users/UserRoleRelation.php
Normal file
88
src/Users/UserRoleRelation.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
namespace Misuzu\Users;
|
||||
|
||||
use Misuzu\DB;
|
||||
use Misuzu\Pagination;
|
||||
|
||||
class UserRoleRelation {
|
||||
// Database fields
|
||||
private $user_id = -1;
|
||||
private $role_id = -1;
|
||||
|
||||
public const TABLE = 'user_roles';
|
||||
private const QUERY_SELECT = 'SELECT %1$s FROM `' . DB::PREFIX . self::TABLE . '` AS '. self::TABLE;
|
||||
private const SELECT = '%1$s.`user_id`, %1$s.`role_id`';
|
||||
|
||||
private $user = null;
|
||||
private $role = null;
|
||||
|
||||
public function getUserId(): int {
|
||||
return $this->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);
|
||||
}
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
<?php
|
||||
define('MSZ_ROLE_MAIN', 1);
|
||||
|
||||
function user_role_add(int $userId, int $roleId): bool {
|
||||
$addRole = \Misuzu\DB::prepare('
|
||||
INSERT INTO `msz_user_roles`
|
||||
(`user_id`, `role_id`)
|
||||
VALUES
|
||||
(:user_id, :role_id)
|
||||
');
|
||||
$addRole->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();
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -4,37 +4,37 @@
|
|||
{% from '_layout/input.twig' import input_csrf, input_text, input_checkbox %}
|
||||
|
||||
{% block manage_content %}
|
||||
<form action="?v=role{{ edit_role is defined ? '&r=' ~ edit_role.role_id : '' }}" method="post"{% if edit_role is defined %} style="{{ edit_role.role_colour|html_colour('--accent-colour') }}"{% endif %}>
|
||||
<form action="?v=role{{ role_info is not null ? '&r=' ~ role_info.id : '' }}" method="post"{% if role_info is not null %} style="--accent-colour: {{ role_info.colour }}"{% endif %}>
|
||||
{{ input_csrf() }}
|
||||
|
||||
<div class="container">
|
||||
{{ 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') }}
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Role Name</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('role[name]', '', edit_role.role_name|default(''), 'text', '', true, {'maxlength':255}) }}
|
||||
{{ input_text('role[name]', '', role_info.name|default(''), 'text', '', true, {'maxlength':255}) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Hide Rank</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_checkbox('role[secret]', '', edit_role is defined and edit_role.role_hidden) }}
|
||||
{{ input_checkbox('role[secret]', '', role_info is not null and role_info.hidden) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Hierarchy</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('role[hierarchy]', '', edit_role.role_hierarchy|default(1), 'number', '', true) }}
|
||||
{{ input_text('role[hierarchy]', '', role_info.rank|default(1), 'number', '', true) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Title</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('role[title]', '', edit_role.role_title|default(''), 'text', '', false, {'maxlength':64}) }}
|
||||
{{ input_text('role[title]', '', role_info.title|default(''), 'text', '', false, {'maxlength':64}) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
@ -46,28 +46,28 @@
|
|||
<label class="form__label">
|
||||
<div class="form__label__text">Inherit Colour</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_checkbox('role[colour][inherit]', '', role_colour is defined ? role_colour.inherit : true) }}
|
||||
{{ input_checkbox('role[colour][inherit]', '', role_info is not null ? role_info.colour.inherit : true) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Red</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('role[colour][red]', '', role_colour.red|default(0), 'number', '', false, {'min':0,'max':255}) }}
|
||||
{{ input_text('role[colour][red]', '', role_info.colour.red|default(0), 'number', '', false, {'min':0,'max':255}) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Green</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('role[colour][green]', '', role_colour.green|default(0), 'number', '', false, {'min':0,'max':255}) }}
|
||||
{{ input_text('role[colour][green]', '', role_info.colour.green|default(0), 'number', '', false, {'min':0,'max':255}) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Blue</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('role[colour][blue]', '', role_colour.blue|default(0), 'number', '', false, {'min':0,'max':255}) }}
|
||||
{{ input_text('role[colour][blue]', '', role_info.colour.blue|default(0), 'number', '', false, {'min':0,'max':255}) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
@ -79,7 +79,7 @@
|
|||
<label class="form__label">
|
||||
<div class="form__label__text">Description</div>
|
||||
<div class="form__label__input">
|
||||
<textarea class="input__textarea" name="role[description]" maxlength="1000">{{ edit_role is defined ? edit_role.role_description : '' }}</textarea>
|
||||
<textarea class="input__textarea" name="role[description]" maxlength="1000">{{ role_info.description|default('') }}</textarea>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -89,6 +89,6 @@
|
|||
{{ permissions_table(permissions, not can_manage_perms) }}
|
||||
</div>
|
||||
|
||||
<button class="input__button">{{ edit_role is defined ? 'Update role' : 'Create role' }}</button>
|
||||
<button class="input__button">{{ role_info is not null ? 'Update role' : 'Create role' }}</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
@ -32,8 +32,8 @@
|
|||
</div>
|
||||
|
||||
{% for role in manage_roles %}
|
||||
<div class="manage__role-item" style="{{ role.role_colour|html_colour('--accent-colour') }}">
|
||||
<a href="{{ url('manage-role', {'role': role.role_id}) }}" class="manage__role-item__background"></a>
|
||||
<div class="manage__role-item" style="--accent-colour: {{ role.colour }}">
|
||||
<a href="{{ url('manage-role', {'role': role.id}) }}" class="manage__role-item__background"></a>
|
||||
|
||||
<div class="manage__role-item__container">
|
||||
<div class="manage__role-item__icon">
|
||||
|
@ -44,23 +44,23 @@
|
|||
</div>
|
||||
<div class="manage__role-item__info">
|
||||
<div class="manage__role-item__name">
|
||||
{{ role.role_name }}
|
||||
{{ role.name }}
|
||||
</div>
|
||||
<div class="manage__role-item__details">
|
||||
{% if role.users > 0 %}
|
||||
{% if role.userCount > 0 %}
|
||||
<div class="manage__role-item__users">
|
||||
<i class="fas fa-users fa-fw"></i> {{ role.users|number_format }}
|
||||
<i class="fas fa-users fa-fw"></i> {{ role.userCount|number_format }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if role.role_title|default('')|length > 0 %}
|
||||
{% if role.title is not empty %}
|
||||
<div class="manage__role-item__title">
|
||||
{{ role.role_title }}
|
||||
{{ role.title }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="manage__role-item__actions">
|
||||
<a href="{{ url('user-list', {'role': role.role_id}) }}" class="manage__role-item__action" title="Members">
|
||||
<a href="{{ url('user-list', {'role': role.id}) }}" class="manage__role-item__action" title="Members">
|
||||
<i class="fas fa-users fa-fw"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -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 %}
|
||||
<div class="manage__user"{% if manage_user is defined %} style="{{ manage_user.user_colour|html_colour('--accent-colour') }}"{% endif %}>
|
||||
<div class="manage__user" style="--accent-colour: {{ user_info.userColour }}">
|
||||
{% if manage_notices|length > 0 %}
|
||||
<div class="warning">
|
||||
<div class="warning__content">
|
||||
|
@ -17,64 +17,64 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" action="{{ url('manage-user', {'user': manage_user.user_id|default(0)}) }}" class="container manage__user__container">
|
||||
{{ container_title('Editing ' ~ manage_user.username ~ ' (' ~ manage_user.user_id ~ ')') }}
|
||||
<form method="post" action="{{ url('manage-user', {'user': user_info.id}) }}" class="container manage__user__container">
|
||||
{{ container_title('Editing ' ~ user_info.username ~ ' (' ~ user_info.id ~ ')') }}
|
||||
{{ input_csrf() }}
|
||||
|
||||
<div class="manage__user__details">
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Username</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text(can_edit_user ? 'user[username]' : '', 'manage__user__input', manage_user.username, 'text', '', true, {'maxlength':16}) }}
|
||||
{{ input_text(can_edit_user ? 'user[username]' : '', 'manage__user__input', user_info.username, 'text', '', true, {'maxlength':16}) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">E-mail address</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text(can_edit_user ? 'user[email]' : '', 'manage__user__input', manage_user.email, 'text', '', true, {'maxlength':255}) }}
|
||||
{{ input_text(can_edit_user ? 'user[email]' : '', 'manage__user__input', user_info.emailAddress, 'text', '', true, {'maxlength':255}) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Title</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text(can_edit_user ? 'user[title]' : '', 'manage__user__input', manage_user.user_title, 'text', '', false, {'maxlength':64}) }}
|
||||
{{ input_text(can_edit_user ? 'user[title]' : '', 'manage__user__input', user_info.title, 'text', '', false, {'maxlength':64}) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Joined</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('', 'manage__user__input', manage_user.user_created) }}
|
||||
{{ input_text('', 'manage__user__input', user_info.createdTime|date('c')) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Last online</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('', 'manage__user__input', manage_user.user_active) }}
|
||||
{{ input_text('', 'manage__user__input', user_info.activeTime|date('c')) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Register IP</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('', 'manage__user__input', manage_user.register_ip_decoded) }}
|
||||
{{ input_text('', 'manage__user__input', user_info.registerRemoteAddress) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Last IP</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text('', 'manage__user__input', manage_user.last_ip_decoded) }}
|
||||
{{ input_text('', 'manage__user__input', user_info.lastRemoteAddress) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="form__label">
|
||||
<div class="form__label__text">Country</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_text(can_edit_user ? 'user[country]' : '', 'manage__user__input', manage_user.user_country, 'text', 'XX', true, {'maxlength':2,'minlength':2}) }}
|
||||
{{ input_text(can_edit_user ? 'user[country]' : '', 'manage__user__input', user_info.country, 'text', 'XX', true, {'maxlength':2,'minlength':2}) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
@ -99,25 +99,25 @@
|
|||
<label class="form__label">
|
||||
<div class="form__label__text">Custom Colour</div>
|
||||
<div class="form__label__input">
|
||||
{{ input_checkbox('colour[enable]', '', not user_colour.inherit, '', '', false, null, not can_edit_user) }}
|
||||
{{ input_checkbox('colour[enable]', '', not user_info.userColour.inherit, '', '', false, null, not can_edit_user) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
{{ 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)) }}
|
||||
</div>
|
||||
|
||||
{# TODO: if the hierarchy of the current user is too low to touch the role then opacity should be lowered and input disabled #}
|
||||
<div class="manage__user__details">
|
||||
<div class="manage__tags manage__tags--fixed">
|
||||
{% for role in manage_roles %}
|
||||
<label class="manage__tag" style="{{ role.role_colour|html_colour('--accent-colour') }}">
|
||||
<label class="manage__tag" style="--accent-colour: {{ role.colour }}">
|
||||
<div class="manage__tag__background"></div>
|
||||
<div class="manage__tag__content">
|
||||
{{ input_checkbox('roles[]', '', role.has_role, 'manage__tag__checkbox', role.role_id, false, null, not can_edit_user) }}
|
||||
{{ input_checkbox('roles[]', '', user_info.hasRole(role), 'manage__tag__checkbox', role.id, false, null, not can_edit_user) }}
|
||||
<div class="manage__tag__title">
|
||||
{{ role.role_name }}
|
||||
{{ role.name }}
|
||||
</div>
|
||||
{{ input_checkbox('user[display_role]', '', role.role_id == manage_user.display_role, 'manage__tag__checkbox', role.role_id, true, null, not can_edit_user) }}
|
||||
{{ input_checkbox('user[display_role]', '', role.id == user_info.displayRoleId, 'manage__tag__checkbox', role.id, true, null, not can_edit_user) }}
|
||||
</div>
|
||||
</label>
|
||||
{% endfor %}
|
||||
|
@ -132,8 +132,8 @@
|
|||
{% endif %}
|
||||
</form>
|
||||
|
||||
<form method="post" action="{{ url('manage-user', {'user': manage_user.user_id|default(0)}) }}" class="container manage__user__container">
|
||||
{{ container_title('Permissions for ' ~ manage_user.username ~ ' (' ~ manage_user.user_id ~ ')') }}
|
||||
<form method="post" action="{{ url('manage-user', {'user': user_info.id}) }}" class="container manage__user__container">
|
||||
{{ container_title('Permissions for ' ~ user_info.username ~ ' (' ~ user_info.id ~ ')') }}
|
||||
|
||||
{{ permissions_table(permissions, not can_edit_perms) }}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<div class="settings__account__title">
|
||||
New e-mail address
|
||||
</div>
|
||||
{{ input_text('email[new]', 'settings__account__input', '', 'email', current_email) }}
|
||||
{{ input_text('email[new]', 'settings__account__input', '', 'email', settings_user.emailAddress) }}
|
||||
</label>
|
||||
|
||||
<label class="settings__account__input">
|
||||
|
@ -71,22 +71,22 @@
|
|||
</div>
|
||||
|
||||
<div class="settings__role__collection">
|
||||
{% 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) %}
|
||||
|
||||
<div class="settings__role" style="{{ role.role_colour|html_colour('--accent-colour') }}">
|
||||
<div class="settings__role" style="--accent-colour: {{ role.colour }}">
|
||||
<div class="settings__role__content">
|
||||
<div class="settings__role__name">
|
||||
{{ role.role_name }}
|
||||
{{ role.name }}
|
||||
</div>
|
||||
|
||||
<div class="settings__role__description">
|
||||
{{ role.role_description }}
|
||||
{{ role.description }}
|
||||
</div>
|
||||
|
||||
<form class="settings__role__options" method="post" action="{{ url('settings-account') }}">
|
||||
{{ input_csrf() }}
|
||||
{{ input_hidden('role[id]', role.role_id) }}
|
||||
{{ input_hidden('role[id]', role.id) }}
|
||||
|
||||
<button class="settings__role__option{% if is_display_role %} settings__role__option--disabled{% endif %}"
|
||||
name="role[mode]" value="display" title="Set this as your display role"
|
||||
|
@ -94,10 +94,10 @@
|
|||
<i class="far {{ is_display_role ? 'fa-check-square' : 'fa-square' }}"></i>
|
||||
</button>
|
||||
|
||||
<button class="settings__role__option{% if not role.role_can_leave %} settings__role__option--disabled{% endif %}"
|
||||
<button class="settings__role__option{% if not role.canLeave %} settings__role__option--disabled{% endif %}"
|
||||
name="role[mode]" value="leave" title="Leave this role"
|
||||
onclick="return confirm('Are you sure you want to remove {{ role.role_name|replace({"'": "\'"}) }} from your account?')"
|
||||
{% if not role.role_can_leave %}disabled{% endif %}>
|
||||
onclick="return confirm('Are you sure you want to remove {{ role.name|replace({"'": "\'"}) }} from your account?')"
|
||||
{% if not role.canLeave %}disabled{% endif %}>
|
||||
<i class="fas fa-times-circle"></i>
|
||||
</button>
|
||||
</form>
|
||||
|
|
|
@ -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 @@
|
|||
|
||||
<div class="userlist__navigation">
|
||||
<form onchange="this.submit()" class="userlist__sorting">
|
||||
{{ 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
|
||||
) %}
|
||||
|
||||
<div class="container userlist__container" style="{{ role.role_colour|html_colour('--accent-colour') }}">
|
||||
<div class="container userlist__container" style="--accent-colour: {{ role.colour }}">
|
||||
{{ member_nav }}
|
||||
</div>
|
||||
|
||||
|
@ -62,7 +62,7 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="container userlist__container" style="{{ role.role_colour|html_colour('--accent-colour') }}">
|
||||
<div class="container userlist__container" style="--accent-colour: {{ role.colour }}">
|
||||
{{ member_nav }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Add table
Reference in a new issue