diff --git a/assets/less/classes/input/button.less b/assets/less/classes/input/button.less index 41149d32..ef810935 100644 --- a/assets/less/classes/input/button.less +++ b/assets/less/classes/input/button.less @@ -55,4 +55,8 @@ &--destroy { --accent-colour: #c00; } + + &--save { + --accent-colour: #080; + } } diff --git a/assets/less/classes/input/select.less b/assets/less/classes/input/select.less index fed66e24..76f1e4dd 100644 --- a/assets/less/classes/input/select.less +++ b/assets/less/classes/input/select.less @@ -9,4 +9,16 @@ &:focus { border-color: var(--accent-colour); } + + &--new { + font-size: 1.2em; + margin: 0; + padding: 5px 10px; + color: #fff; + border-radius: 2px; + background: #222; + box-shadow: inset 0 0 4px #111; + border-color: #222; + transition: border-color .2s; + } } diff --git a/assets/less/classes/input/textarea.less b/assets/less/classes/input/textarea.less index 211cb10d..40d7ce72 100644 --- a/assets/less/classes/input/textarea.less +++ b/assets/less/classes/input/textarea.less @@ -10,4 +10,15 @@ &:focus { border-color: var(--accent-colour); } + + &--new { + font-size: 1.2em; + padding: 5px 10px; + color: #fff; + border-radius: 2px; + background: #222; + box-shadow: inset 0 0 4px #111; + border-color: #222; + transition: border-color .2s; + } } diff --git a/assets/less/classes/profile/about.less b/assets/less/classes/profile/about.less index f61241dc..3e7b4408 100644 --- a/assets/less/classes/profile/about.less +++ b/assets/less/classes/profile/about.less @@ -3,5 +3,22 @@ &__content { max-height: 600px; overflow: auto; + padding: 2px 5px; + + &--edit { + padding: 5px; + } + } + + &__editor { + width: 100%; + height: 500px; + max-width: 100%; + min-width: 100%; + } + + &__select { + margin-bottom: 5px; + width: 100%; } } diff --git a/assets/less/classes/profile/accounts.less b/assets/less/classes/profile/accounts.less index 369bc90e..32fe7ea3 100644 --- a/assets/less/classes/profile/accounts.less +++ b/assets/less/classes/profile/accounts.less @@ -30,6 +30,10 @@ text-decoration: none; } + &__input { + width: 100%; + } + &__link { color: inherit; text-decoration: underline dotted; diff --git a/assets/less/classes/profile/header.less b/assets/less/classes/profile/header.less index 638bbb49..e00949ec 100644 --- a/assets/less/classes/profile/header.less +++ b/assets/less/classes/profile/header.less @@ -48,6 +48,7 @@ } &__options { + min-height: 62px; background-color: rgba(0, 0, 0, .9); padding: 0 20px; display: flex; @@ -59,6 +60,10 @@ align-items: center; } + &__action { + margin-right: 5px; + } + &__stats { display: flex; } diff --git a/public/profile.php b/public/profile.php index e560d674..21c4973c 100644 --- a/public/profile.php +++ b/public/profile.php @@ -4,13 +4,13 @@ use Misuzu\IO\File; require_once __DIR__ . '/../misuzu.php'; -$user_id = (int)($_GET['u'] ?? 0); +$userId = (int)($_GET['u'] ?? 0); $mode = (string)($_GET['m'] ?? null); switch ($mode) { case 'avatar': $avatar_filename = $app->getDefaultAvatar(); - $user_avatar = "{$user_id}.msz"; + $user_avatar = "{$userId}.msz"; $cropped_avatar = build_path( create_directory(build_path($app->getStoragePath(), 'avatars/200x200')), $user_avatar @@ -42,7 +42,7 @@ switch ($mode) { case 'background': $user_background = build_path( create_directory(build_path($app->getStoragePath(), 'backgrounds/original')), - "{$user_id}.msz" + "{$userId}.msz" ); if (!is_file($user_background)) { @@ -85,7 +85,7 @@ switch ($mode) { ON r.`role_id` = u.`display_role` WHERE `user_id` = :user_id '); - $getProfile->bindValue('user_id', $user_id); + $getProfile->bindValue('user_id', $userId); $profile = $getProfile->execute() ? $getProfile->fetch() : []; if (!$profile) { @@ -94,7 +94,19 @@ switch ($mode) { break; } + $userPerms = perms_get_user(MSZ_PERMS_USER, $app->getUserId()); + $perms = [ + 'edit_profile' => perms_check($userPerms, MSZ_PERM_USER_EDIT_PROFILE), + 'edit_avatar' => perms_check($userPerms, MSZ_PERM_USER_CHANGE_AVATAR), + 'edit_background' => perms_check($userPerms, MSZ_PERM_USER_CHANGE_BACKGROUND), + 'edit_about' => perms_check($userPerms, MSZ_PERM_USER_EDIT_ABOUT), + ]; + if ($app->hasActiveSession()) { + $canEdit = $app->getUserId() === $profile['user_id'] + || perms_check($userPerms, MSZ_PERM_USER_MANAGE_USERS); + $isEditing = $canEdit && $mode === 'edit'; + $getFriendInfo = Database::prepare(' SELECT :visitor as `visitor`, :profile as `profile`, @@ -121,12 +133,17 @@ switch ($mode) { $getFriendInfo->bindValue('profile', $profile['user_id']); $friendInfo = $getFriendInfo->execute() ? $getFriendInfo->fetch(PDO::FETCH_ASSOC) : []; - tpl_var('friend_info', $friendInfo); + tpl_vars([ + 'friend_info' => $friendInfo, + ]); } tpl_vars([ 'profile' => $profile, - 'profile_fields' => $app->hasActiveSession() ? user_profile_fields_display($profile) : [], + 'can_edit' => $canEdit ?? false, + 'is_editing' => $isEditing ?? false, + 'perms' => $perms, + 'profile_fields' => $app->hasActiveSession() ? user_profile_fields_display($profile, !$isEditing) : [], 'has_background' => is_file(build_path($app->getStoragePath(), 'backgrounds/original', "{$profile['user_id']}.msz")), ]); echo tpl_render('user.profile'); diff --git a/public/settings.php b/public/settings.php index 81eab67f..aea732ee 100644 --- a/public/settings.php +++ b/public/settings.php @@ -20,6 +20,15 @@ if (!$app->hasActiveSession()) { return; } +$settingsUserId = !empty($_REQUEST['user']) && perms_check($userPerms, MSZ_PERM_USER_MANAGE_USERS) + ? (int)$_REQUEST['user'] + : $app->getUserId(); + +if ($settingsUserId !== $app->getUserId() && !user_exists($settingsUserId)) { + echo render_error(400); + return; +} + $settingsModes = [ 'account' => 'Account', 'sessions' => 'Sessions', @@ -54,6 +63,7 @@ $avatarErrorStrings = [ ]; tpl_vars([ + 'settings_user_id' => $settingsUserId, 'settings_perms' => $perms, 'settings_mode' => $settingsMode, 'settings_modes' => $settingsModes, @@ -69,7 +79,7 @@ if (!array_key_exists($settingsMode, $settingsModes)) { $settingsErrors = []; $disableAccountOptions = !MSZ_DEBUG && $app->disableRegistration(); -$avatarFileName = "{$app->getUserId()}.msz"; +$avatarFileName = "{$settingsUserId}.msz"; $avatarProps = $app->getAvatarProps(); $backgroundProps = $app->getBackgroundProps(); @@ -81,7 +91,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!$perms['edit_profile']) { $settingsErrors[] = "You're not allowed to edit your profile."; } else { - $setUserFieldErrors = user_profile_fields_set($app->getUserId(), $_POST['profile']); + $setUserFieldErrors = user_profile_fields_set($settingsUserId, $_POST['profile']); if (count($setUserFieldErrors) > 0) { foreach ($setUserFieldErrors as $name => $error) { @@ -136,7 +146,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { `user_about_parser` = :parser WHERE `user_id` = :user '); - $setAbout->bindValue('user', $app->getUserId()); + $setAbout->bindValue('user', $settingsUserId); $setAbout->bindValue('content', strlen($aboutText) < 1 ? null : $aboutText); $setAbout->bindValue('parser', $aboutParser); $setAbout->execute(); @@ -148,7 +158,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!empty($_POST['avatar']) && is_array($_POST['avatar'])) { switch ($_POST['avatar']['mode'] ?? '') { case 'delete': - user_avatar_delete($app->getUserId()); + user_avatar_delete($settingsUserId); break; case 'upload': @@ -176,7 +186,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } $setAvatar = user_avatar_set_from_path( - $app->getUserId(), + $settingsUserId, $_FILES['avatar']['tmp_name']['file'], $avatarProps ); @@ -198,7 +208,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!empty($_POST['background']) && is_array($_POST['background'])) { switch ($_POST['background']['mode'] ?? '') { case 'delete': - user_background_delete($app->getUserId()); + user_background_delete($settingsUserId); break; case 'upload': @@ -226,7 +236,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } $setBackground = user_background_set_from_path( - $app->getUserId(), + $settingsUserId, $_FILES['background']['tmp_name']['file'], $backgroundProps ); @@ -252,9 +262,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { DELETE FROM `msz_sessions` WHERE `user_id` = :user_id ')->execute([ - 'user_id' => $app->getUserId(), + 'user_id' => $settingsUserId, ]); - audit_log('PERSONAL_SESSION_DESTROY_ALL', $app->getUserId()); + audit_log('PERSONAL_SESSION_DESTROY_ALL', $settingsUserId); header('Location: /'); return; } @@ -274,7 +284,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $findSession->bindValue('session_id', $session_id); $session = $findSession->execute() ? $findSession->fetch() : null; - if (!$session || (int)$session['user_id'] !== $app->getUserId()) { + if (!$session || (int)$session['user_id'] !== $settingsUserId) { $settingsErrors[] = 'You may only end your own sessions.'; } else { if ((int)$session['session_id'] === $app->getSessionId()) { @@ -283,7 +293,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } user_session_delete($session['session_id']); - audit_log('PERSONAL_SESSION_DESTROY', $app->getUserId(), [ + audit_log('PERSONAL_SESSION_DESTROY', $settingsUserId, [ $session['session_id'], ]); } @@ -304,7 +314,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { FROM `msz_users` WHERE `user_id` = :user_id '); - $fetchPassword->bindValue('user_id', $app->getUserId()); + $fetchPassword->bindValue('user_id', $settingsUserId); $currentPassword = $fetchPassword->execute() ? $fetchPassword->fetchColumn() : null; if (empty($currentPassword)) { @@ -339,7 +349,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } } else { $updateAccountFields['email'] = mb_strtolower($_POST['email']['new']); - audit_log('PERSONAL_EMAIL_CHANGE', $app->getUserId(), [ + audit_log('PERSONAL_EMAIL_CHANGE', $settingsUserId, [ $updateAccountFields['email'], ]); } @@ -357,7 +367,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $settingsErrors[] = "The given passwords was too weak."; } else { $updateAccountFields['password'] = user_password_hash($_POST['password']['new']); - audit_log('PERSONAL_PASSWORD_CHANGE', $app->getUserId()); + audit_log('PERSONAL_PASSWORD_CHANGE', $settingsUserId); } } } @@ -368,7 +378,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { SET ' . pdo_prepare_array_update($updateAccountFields, true) . ' WHERE `user_id` = :user_id '); - $updateAccountFields['user_id'] = $app->getUserId(); + $updateAccountFields['user_id'] = $settingsUserId; $updateUser->execute($updateAccountFields); } } @@ -376,6 +386,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } } } + + if (!empty($_POST['user']) && !empty($_SERVER['HTTP_REFERER'])) { + header('Location: /profile.php?u=' . ((int)($_POST['user'] ?? 0))); + return; + } } tpl_vars([ @@ -384,7 +399,7 @@ tpl_vars([ ]); switch ($settingsMode) { - case 'account': // TODO: FIX THIS GARBAGE HOLY HELL + case 'account': $profileFields = user_profile_fields_get(); $getAccountInfo = Database::prepare(sprintf( @@ -395,7 +410,7 @@ switch ($settingsMode) { ', pdo_prepare_array($profileFields, true, '`user_%s`') )); - $getAccountInfo->bindValue('user_id', $app->getUserId()); + $getAccountInfo->bindValue('user_id', $settingsUserId); $accountInfo = $getAccountInfo->execute() ? $getAccountInfo->fetch(PDO::FETCH_ASSOC) : []; $userHasAvatar = is_file(build_path($app->getStoragePath(), 'avatars/original', $avatarFileName)); @@ -418,7 +433,7 @@ switch ($settingsMode) { FROM `msz_sessions` WHERE `user_id` = :user_id '); - $getSessionCount->bindValue('user_id', $app->getUserId()); + $getSessionCount->bindValue('user_id', $settingsUserId); $sessionCount = $getSessionCount->execute() ? $getSessionCount->fetchColumn() : 0; $getSessions = Database::prepare(' @@ -432,7 +447,7 @@ switch ($settingsMode) { '); $getSessions->bindValue('offset', $queryOffset); $getSessions->bindValue('take', $queryTake); - $getSessions->bindValue('user_id', $app->getUserId()); + $getSessions->bindValue('user_id', $settingsUserId); $sessions = $getSessions->execute() ? $getSessions->fetchAll() : []; tpl_vars([ @@ -453,7 +468,7 @@ switch ($settingsMode) { FROM `msz_login_attempts` WHERE `user_id` = :user_id '); - $getLoginAttemptsCount->bindValue('user_id', $app->getUserId()); + $getLoginAttemptsCount->bindValue('user_id', $settingsUserId); $loginAttemptsCount = $getLoginAttemptsCount->execute() ? $getLoginAttemptsCount->fetchColumn() : 0; $getLoginAttempts = Database::prepare(' @@ -467,14 +482,14 @@ switch ($settingsMode) { '); $getLoginAttempts->bindValue('offset', $loginAttemptsOffset); $getLoginAttempts->bindValue('take', min(20, max(5, $queryTake))); - $getLoginAttempts->bindValue('user_id', $app->getUserId()); + $getLoginAttempts->bindValue('user_id', $settingsUserId); $loginAttempts = $getLoginAttempts->execute() ? $getLoginAttempts->fetchAll() : []; - $auditLogCount = audit_log_count($app->getUserId()); + $auditLogCount = audit_log_count($settingsUserId); $auditLog = audit_log_list( $auditLogOffset, min(20, max(5, $queryTake)), - $app->getUserId() + $settingsUserId ); tpl_vars([ diff --git a/src/Users/profile.php b/src/Users/profile.php index 7215950c..660c3c34 100644 --- a/src/Users/profile.php +++ b/src/Users/profile.php @@ -163,19 +163,20 @@ function user_profile_fields_set(int $userId, array $fields): array return $errors; } -function user_profile_fields_display(array $user): array +function user_profile_fields_display(array $user, bool $hideEmpty = true): array { $output = []; foreach (MSZ_USER_PROFILE_FIELDS as $name => $field) { $dbn = user_profile_field_get_database_name($name); - if (!array_key_exists($dbn, $user) || empty($user[$dbn])) { + if ($hideEmpty && (!array_key_exists($dbn, $user) || empty($user[$dbn]))) { continue; } + $value = $user[$dbn] ?? ''; $output[$name] = $field; - $output[$name]['value'] = htmlentities($user[$dbn]); + $output[$name]['value'] = htmlentities($value); foreach (['link', 'format'] as $multipath) { if (empty($output[$name][$multipath]) || !is_array($output[$name][$multipath])) { @@ -183,7 +184,7 @@ function user_profile_fields_display(array $user): array } foreach (array_reverse($output[$name][$multipath], true) as $regex => $string) { - if ($regex === '_' || !preg_match("#{$regex}#", $user[$dbn])) { + if ($regex === '_' || !preg_match("#{$regex}#", $value)) { continue; } diff --git a/src/Users/user.php b/src/Users/user.php index 7b840d11..b2ad6ada 100644 --- a/src/Users/user.php +++ b/src/Users/user.php @@ -51,6 +51,18 @@ function user_password_hash(string $password): string return password_hash($password, MSZ_USERS_PASSWORD_HASH_ALGO); } +// function of the century, only use this if it doesn't make sense to grab data otherwise +function user_exists(int $userId): bool +{ + $check = Database::prepare(' + SELECT COUNT(`user_id`) > 0 + FROM `msz_users` + WHERE `user_id` = :user_id + '); + $check->bindValue('user_id', $userId); + return $check->execute() ? (bool)$check->fetchColumn() : false; +} + function user_id_from_username(string $username): int { $getId = Database::prepare('SELECT `user_id` FROM `msz_users` WHERE LOWER(`username`) = LOWER(:username)'); diff --git a/templates/settings/account.twig b/templates/settings/account.twig index 06fca5de..0d16d368 100644 --- a/templates/settings/account.twig +++ b/templates/settings/account.twig @@ -1,10 +1,16 @@ {% extends 'settings/master.twig' %} {% if user_has_background %} - {% set site_background_url = '/profile.php?m=background&u=' ~ current_user.user_id %} + {% set site_background_url = '/profile.php?m=background&u=' ~ settings_user_id %} {% endif %} {% block settings_content %} + {#