Merged account and avatar settings pages.
This commit is contained in:
parent
bd83bc15a0
commit
294380d7bc
9 changed files with 524 additions and 569 deletions
|
@ -33,7 +33,7 @@ switch ($_GET['v'] ?? null) {
|
||||||
$getManageUsers = Database::prepare('
|
$getManageUsers = Database::prepare('
|
||||||
SELECT
|
SELECT
|
||||||
u.`user_id`, u.`username`,
|
u.`user_id`, u.`username`,
|
||||||
COALESCE(u.`user_colour`, r.`role_colour`) as `colour`
|
COALESCE(u.`user_colour`, r.`role_colour`) as `user_colour`
|
||||||
FROM `msz_users` as u
|
FROM `msz_users` as u
|
||||||
LEFT JOIN `msz_roles` as r
|
LEFT JOIN `msz_roles` as r
|
||||||
ON u.`display_role` = r.`role_id`
|
ON u.`display_role` = r.`role_id`
|
||||||
|
|
|
@ -10,54 +10,23 @@ $queryOffset = (int)($_GET['o'] ?? 0);
|
||||||
$queryTake = 15;
|
$queryTake = 15;
|
||||||
|
|
||||||
$userPerms = perms_get_user(MSZ_PERMS_USER, $app->getUserId());
|
$userPerms = perms_get_user(MSZ_PERMS_USER, $app->getUserId());
|
||||||
|
$perms = [
|
||||||
$settingsModes = [
|
'edit_profile' => perms_check($userPerms, MSZ_USER_PERM_EDIT_PROFILE),
|
||||||
'account' => [
|
'edit_avatar' => perms_check($userPerms, MSZ_USER_PERM_CHANGE_AVATAR),
|
||||||
'title' => 'Account',
|
|
||||||
'allow' => perms_check($userPerms, MSZ_USER_PERM_EDIT_PROFILE),
|
|
||||||
],
|
|
||||||
'images' => [
|
|
||||||
'title' => 'Avatar',
|
|
||||||
'allow' => perms_check($userPerms, MSZ_USER_PERM_CHANGE_AVATAR),
|
|
||||||
],
|
|
||||||
'sessions' => [
|
|
||||||
'title' => 'Sessions',
|
|
||||||
'allow' => true,
|
|
||||||
],
|
|
||||||
'login-history' => [
|
|
||||||
'title' => 'Login History',
|
|
||||||
'allow' => true,
|
|
||||||
],
|
|
||||||
'log' => [
|
|
||||||
'title' => 'Account Log',
|
|
||||||
'allow' => true,
|
|
||||||
],
|
|
||||||
];
|
];
|
||||||
$settingsMode = $_GET['m'] ?? null;
|
|
||||||
|
|
||||||
if ($settingsMode === 'avatar') {
|
if (!$app->hasActiveSession()) {
|
||||||
header('Location: ?m=images');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$settingsNavigation = [];
|
|
||||||
|
|
||||||
foreach ($settingsModes as $key => $value) {
|
|
||||||
if ($value['allow']) {
|
|
||||||
$settingsNavigation[$value['title']] = $key;
|
|
||||||
|
|
||||||
if ($settingsMode === null) {
|
|
||||||
$settingsMode = $key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$app->hasActiveSession() || !$settingsModes[$settingsMode]['allow']) {
|
|
||||||
echo render_error(403);
|
echo render_error(403);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$tpl->var('settings_navigation', $settingsNavigation);
|
$settingsModes = [
|
||||||
|
'account' => 'Account',
|
||||||
|
'sessions' => 'Sessions',
|
||||||
|
'login-history' => 'Login History',
|
||||||
|
'log' => 'Account Log',
|
||||||
|
];
|
||||||
|
$settingsMode = $_GET['m'] ?? key($settingsModes);
|
||||||
|
|
||||||
$csrfErrorString = "Couldn't verify you, please refresh the page and retry.";
|
$csrfErrorString = "Couldn't verify you, please refresh the page and retry.";
|
||||||
|
|
||||||
|
@ -86,6 +55,7 @@ $avatarErrorStrings = [
|
||||||
];
|
];
|
||||||
|
|
||||||
$tpl->vars([
|
$tpl->vars([
|
||||||
|
'settings_perms' => $perms,
|
||||||
'settings_mode' => $settingsMode,
|
'settings_mode' => $settingsMode,
|
||||||
'settings_modes' => $settingsModes,
|
'settings_modes' => $settingsModes,
|
||||||
]);
|
]);
|
||||||
|
@ -106,228 +76,201 @@ $avatarHeightMax = $app->getConfig()->get('Avatar', 'max_height', 'int', 4000);
|
||||||
$avatarFileSizeMax = $app->getConfig()->get('Avatar', 'max_filesize', 'int', 1000000);
|
$avatarFileSizeMax = $app->getConfig()->get('Avatar', 'max_filesize', 'int', 1000000);
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
switch ($settingsMode) {
|
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
|
||||||
case 'account':
|
$settingsErrors[] = $csrfErrorString;
|
||||||
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
|
} else {
|
||||||
$settingsErrors[] = $csrfErrorString;
|
if (!empty($_POST['profile']) && is_array($_POST['profile'])) {
|
||||||
break;
|
$setUserFieldErrors = user_profile_fields_set($app->getUserId(), $_POST['profile']);
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_POST['profile']) && is_array($_POST['profile'])) {
|
if (count($setUserFieldErrors) > 0) {
|
||||||
$setUserFieldErrors = user_profile_fields_set($app->getUserId(), $_POST['profile']);
|
foreach ($setUserFieldErrors as $name => $error) {
|
||||||
|
switch ($error) {
|
||||||
if (count($setUserFieldErrors) > 0) {
|
case MSZ_USER_PROFILE_INVALID_FIELD:
|
||||||
foreach ($setUserFieldErrors as $name => $error) {
|
$settingsErrors[] = sprintf("Field '%s' does not exist!", $name);
|
||||||
switch ($error) {
|
|
||||||
case MSZ_USER_PROFILE_INVALID_FIELD:
|
|
||||||
$settingsErrors[] = sprintf("Field '%s' does not exist!", $name);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MSZ_USER_PROFILE_FILTER_FAILED:
|
|
||||||
$settingsErrors[] = sprintf(
|
|
||||||
'%s field was invalid!',
|
|
||||||
user_profile_field_get_display_name($name)
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MSZ_USER_PROFILE_UPDATE_FAILED:
|
|
||||||
$settingsErrors[] = 'Failed to update values, contact an administator.';
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$settingsErrors[] = 'An unexpected error occurred, contact an administator.';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$disableAccountOptions) {
|
|
||||||
if (!empty($_POST['current_password'])
|
|
||||||
|| (
|
|
||||||
(isset($_POST['password']) || isset($_POST['email']))
|
|
||||||
&& (!empty($_POST['password']['new']) || !empty($_POST['email']['new']))
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
$updateAccountFields = [];
|
|
||||||
|
|
||||||
$fetchPassword = Database::prepare('
|
|
||||||
SELECT `password`
|
|
||||||
FROM `msz_users`
|
|
||||||
WHERE `user_id` = :user_id
|
|
||||||
');
|
|
||||||
$fetchPassword->bindValue('user_id', $app->getUserId());
|
|
||||||
$currentPassword = $fetchPassword->execute() ? $fetchPassword->fetchColumn() : null;
|
|
||||||
|
|
||||||
if (empty($currentPassword)) {
|
|
||||||
$settingsErrors[] = 'Something went horribly wrong.';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!password_verify($_POST['current_password'], $currentPassword)) {
|
|
||||||
$settingsErrors[] = 'Your current password was incorrect.';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($_POST['email']['new'])) {
|
|
||||||
if (empty($_POST['email']['confirm'])
|
|
||||||
|| $_POST['email']['new'] !== $_POST['email']['confirm']) {
|
|
||||||
$settingsErrors[] = 'The given e-mail addresses did not match.';
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
$email_validate = user_validate_email($_POST['email']['new'], true);
|
case MSZ_USER_PROFILE_FILTER_FAILED:
|
||||||
|
|
||||||
if ($email_validate !== '') {
|
|
||||||
switch ($email_validate) {
|
|
||||||
case 'dns':
|
|
||||||
$settingsErrors[] = 'No valid MX record exists for this domain.';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'format':
|
|
||||||
$settingsErrors[] = 'The given e-mail address was incorrectly formatted.';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'in-use':
|
|
||||||
$settingsErrors[] = 'This e-mail address is already in use.';
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
$settingsErrors[] = 'Unknown e-mail validation error.';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$updateAccountFields['email'] = strtolower($_POST['email']['new']);
|
|
||||||
|
|
||||||
audit_log('PERSONAL_EMAIL_CHANGE', $app->getUserId(), [
|
|
||||||
$updateAccountFields['email'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($_POST['password']['new'])) {
|
|
||||||
if (empty($_POST['password']['confirm'])
|
|
||||||
|| $_POST['password']['new'] !== $_POST['password']['confirm']) {
|
|
||||||
$settingsErrors[] = "The given passwords did not match.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$password_validate = user_validate_password($_POST['password']['new']);
|
|
||||||
|
|
||||||
if ($password_validate !== '') {
|
|
||||||
$settingsErrors[] = "The given passwords was too weak.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$updateAccountFields['password'] = user_password_hash($_POST['password']['new']);
|
|
||||||
|
|
||||||
audit_log('PERSONAL_PASSWORD_CHANGE', $app->getUserId());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($updateAccountFields) > 0) {
|
|
||||||
$updateUser = Database::prepare('
|
|
||||||
UPDATE `msz_users`
|
|
||||||
SET ' . pdo_prepare_array_update($updateAccountFields, true) . '
|
|
||||||
WHERE `user_id` = :user_id
|
|
||||||
');
|
|
||||||
$updateAccountFields['user_id'] = $app->getUserId();
|
|
||||||
$updateUser->execute($updateAccountFields);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'images':
|
|
||||||
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
|
|
||||||
$settingsErrors[] = $csrfErrorString;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($_POST['avatar']) && is_array($_POST['avatar']) && !empty($_POST['avatar']['mode'])) {
|
|
||||||
switch ($_POST['avatar']['mode']) {
|
|
||||||
case 'delete':
|
|
||||||
user_avatar_delete($app->getUserId());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'upload':
|
|
||||||
if (empty($_FILES['avatar'])
|
|
||||||
|| !is_array($_FILES['avatar'])
|
|
||||||
|| empty($_FILES['avatar']['name']['file'])) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_FILES['avatar']['error']['file'] !== UPLOAD_ERR_OK) {
|
|
||||||
$settingsErrors[] = sprintf(
|
$settingsErrors[] = sprintf(
|
||||||
$avatarErrorStrings['upload'][$_FILES['avatar']['error']['file']]
|
'%s field was invalid!',
|
||||||
?? $avatarErrorStrings['upload']['default'],
|
user_profile_field_get_display_name($name)
|
||||||
$_FILES['avatar']['error']['file'],
|
|
||||||
byte_symbol($avatarFileSizeMax, true),
|
|
||||||
$avatarWidthMax,
|
|
||||||
$avatarHeightMax
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
$setAvatar = user_avatar_set_from_path(
|
case MSZ_USER_PROFILE_UPDATE_FAILED:
|
||||||
$app->getUserId(),
|
$settingsErrors[] = 'Failed to update values, contact an administator.';
|
||||||
$_FILES['avatar']['tmp_name']['file']
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$settingsErrors[] = 'An unexpected error occurred, contact an administator.';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($_POST['avatar']) && is_array($_POST['avatar'])) {
|
||||||
|
switch ($_POST['avatar']['mode'] ?? '') {
|
||||||
|
case 'delete':
|
||||||
|
user_avatar_delete($app->getUserId());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'upload':
|
||||||
|
if (empty($_FILES['avatar'])
|
||||||
|
|| !is_array($_FILES['avatar'])
|
||||||
|
|| empty($_FILES['avatar']['name']['file'])) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_FILES['avatar']['error']['file'] !== UPLOAD_ERR_OK) {
|
||||||
|
$settingsErrors[] = sprintf(
|
||||||
|
$avatarErrorStrings['upload'][$_FILES['avatar']['error']['file']]
|
||||||
|
?? $avatarErrorStrings['upload']['default'],
|
||||||
|
$_FILES['avatar']['error']['file'],
|
||||||
|
byte_symbol($avatarFileSizeMax, true),
|
||||||
|
$avatarWidthMax,
|
||||||
|
$avatarHeightMax
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($setAvatar !== MSZ_USER_AVATAR_NO_ERRORS) {
|
|
||||||
$settingsErrors[] = sprintf(
|
|
||||||
$avatarErrorStrings['set'][$setAvatar]
|
|
||||||
?? $avatarErrorStrings['set']['default'],
|
|
||||||
$setAvatar,
|
|
||||||
byte_symbol($avatarFileSizeMax, true),
|
|
||||||
$avatarWidthMax,
|
|
||||||
$avatarHeightMax
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'sessions':
|
$setAvatar = user_avatar_set_from_path(
|
||||||
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
|
$app->getUserId(),
|
||||||
$settingsErrors[] = $csrfErrorString;
|
$_FILES['avatar']['tmp_name']['file']
|
||||||
break;
|
);
|
||||||
}
|
|
||||||
|
|
||||||
|
if ($setAvatar !== MSZ_USER_AVATAR_NO_ERRORS) {
|
||||||
|
$settingsErrors[] = sprintf(
|
||||||
|
$avatarErrorStrings['set'][$setAvatar]
|
||||||
|
?? $avatarErrorStrings['set']['default'],
|
||||||
|
$setAvatar,
|
||||||
|
byte_symbol($avatarFileSizeMax, true),
|
||||||
|
$avatarWidthMax,
|
||||||
|
$avatarHeightMax
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($_POST['session']) && is_numeric($_POST['session'])) {
|
||||||
$session_id = (int)($_POST['session'] ?? 0);
|
$session_id = (int)($_POST['session'] ?? 0);
|
||||||
|
|
||||||
if ($session_id < 1) {
|
if ($session_id < 1) {
|
||||||
$settingsErrors[] = 'Invalid session.';
|
$settingsErrors[] = 'Invalid session.';
|
||||||
break;
|
} else {
|
||||||
}
|
$findSession = Database::prepare('
|
||||||
|
SELECT `session_id`, `user_id`
|
||||||
|
FROM `msz_sessions`
|
||||||
|
WHERE `session_id` = :session_id
|
||||||
|
');
|
||||||
|
$findSession->bindValue('session_id', $session_id);
|
||||||
|
$session = $findSession->execute() ? $findSession->fetch() : null;
|
||||||
|
|
||||||
$findSession = Database::prepare('
|
if (!$session || (int)$session['user_id'] !== $app->getUserId()) {
|
||||||
SELECT `session_id`, `user_id`
|
$settingsErrors[] = 'You may only end your own sessions.';
|
||||||
FROM `msz_sessions`
|
} else {
|
||||||
WHERE `session_id` = :session_id
|
if ((int)$session['session_id'] === $app->getSessionId()) {
|
||||||
|
header('Location: /auth.php?m=logout&s=' . tmp_csrf_token());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user_session_delete($session['session_id']);
|
||||||
|
audit_log('PERSONAL_SESSION_DESTROY', $app->getUserId(), [
|
||||||
|
$session['session_id'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$disableAccountOptions) {
|
||||||
|
if (!empty($_POST['current_password'])
|
||||||
|
|| (
|
||||||
|
(isset($_POST['password']) || isset($_POST['email']))
|
||||||
|
&& (!empty($_POST['password']['new']) || !empty($_POST['email']['new']))
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$updateAccountFields = [];
|
||||||
|
|
||||||
|
$fetchPassword = Database::prepare('
|
||||||
|
SELECT `password`
|
||||||
|
FROM `msz_users`
|
||||||
|
WHERE `user_id` = :user_id
|
||||||
');
|
');
|
||||||
$findSession->bindValue('session_id', $session_id);
|
$fetchPassword->bindValue('user_id', $app->getUserId());
|
||||||
$session = $findSession->execute() ? $findSession->fetch() : null;
|
$currentPassword = $fetchPassword->execute() ? $fetchPassword->fetchColumn() : null;
|
||||||
|
|
||||||
if (!$session || (int)$session['user_id'] !== $app->getUserId()) {
|
if (empty($currentPassword)) {
|
||||||
$settingsErrors[] = 'You may only end your own sessions.';
|
$settingsErrors[] = 'Something went horribly wrong.';
|
||||||
break;
|
} else {
|
||||||
|
if (!password_verify($_POST['current_password'], $currentPassword)) {
|
||||||
|
$settingsErrors[] = 'Your current password was incorrect.';
|
||||||
|
} else {
|
||||||
|
if (!empty($_POST['email']['new'])) {
|
||||||
|
if (empty($_POST['email']['confirm'])
|
||||||
|
|| $_POST['email']['new'] !== $_POST['email']['confirm']) {
|
||||||
|
$settingsErrors[] = 'The given e-mail addresses did not match.';
|
||||||
|
} else {
|
||||||
|
$email_validate = user_validate_email($_POST['email']['new'], true);
|
||||||
|
|
||||||
|
if ($email_validate !== '') {
|
||||||
|
switch ($email_validate) {
|
||||||
|
case 'dns':
|
||||||
|
$settingsErrors[] = 'No valid MX record exists for this domain.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'format':
|
||||||
|
$settingsErrors[] = 'The given e-mail address was incorrectly formatted.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'in-use':
|
||||||
|
$settingsErrors[] = 'This e-mail address is already in use.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$settingsErrors[] = 'Unknown e-mail validation error.';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$updateAccountFields['email'] = strtolower($_POST['email']['new']);
|
||||||
|
audit_log('PERSONAL_EMAIL_CHANGE', $app->getUserId(), [
|
||||||
|
$updateAccountFields['email'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($_POST['password']['new'])) {
|
||||||
|
if (empty($_POST['password']['confirm'])
|
||||||
|
|| $_POST['password']['new'] !== $_POST['password']['confirm']) {
|
||||||
|
$settingsErrors[] = "The given passwords did not match.";
|
||||||
|
} else {
|
||||||
|
$password_validate = user_validate_password($_POST['password']['new']);
|
||||||
|
|
||||||
|
if ($password_validate !== '') {
|
||||||
|
$settingsErrors[] = "The given passwords was too weak.";
|
||||||
|
} else {
|
||||||
|
$updateAccountFields['password'] = user_password_hash($_POST['password']['new']);
|
||||||
|
audit_log('PERSONAL_PASSWORD_CHANGE', $app->getUserId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($updateAccountFields) > 0) {
|
||||||
|
$updateUser = Database::prepare('
|
||||||
|
UPDATE `msz_users`
|
||||||
|
SET ' . pdo_prepare_array_update($updateAccountFields, true) . '
|
||||||
|
WHERE `user_id` = :user_id
|
||||||
|
');
|
||||||
|
$updateAccountFields['user_id'] = $app->getUserId();
|
||||||
|
$updateUser->execute($updateAccountFields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ((int)$session['session_id'] === $app->getSessionId()) {
|
|
||||||
header('Location: /auth.php?m=logout&s=' . tmp_csrf_token());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user_session_delete($session['session_id']);
|
|
||||||
audit_log('PERSONAL_SESSION_DESTROY', $app->getUserId(), [
|
|
||||||
$session['session_id'],
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$tpl->var('settings_title', $settingsModes[$settingsMode]['title']);
|
$tpl->var('settings_title', $settingsModes[$settingsMode]);
|
||||||
$tpl->var('settings_errors', $settingsErrors);
|
$tpl->var('settings_errors', $settingsErrors);
|
||||||
|
|
||||||
switch ($settingsMode) {
|
switch ($settingsMode) {
|
||||||
|
@ -348,23 +291,18 @@ switch ($settingsMode) {
|
||||||
');
|
');
|
||||||
$getMail->bindValue('user_id', $app->getUserId());
|
$getMail->bindValue('user_id', $app->getUserId());
|
||||||
$currentEmail = $getMail->execute() ? $getMail->fetchColumn() : 'Failed to fetch e-mail address.';
|
$currentEmail = $getMail->execute() ? $getMail->fetchColumn() : 'Failed to fetch e-mail address.';
|
||||||
|
|
||||||
$tpl->vars([
|
|
||||||
'settings_profile_fields' => $profileFields,
|
|
||||||
'settings_profile_values' => $userFields,
|
|
||||||
'settings_disable_account_options' => $disableAccountOptions,
|
|
||||||
'settings_email' => $currentEmail,
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'images':
|
|
||||||
$userHasAvatar = File::exists($app->getStore('avatars/original')->filename($avatarFileName));
|
$userHasAvatar = File::exists($app->getStore('avatars/original')->filename($avatarFileName));
|
||||||
|
|
||||||
$tpl->vars([
|
$tpl->vars([
|
||||||
'avatar_user_id' => $app->getUserId(),
|
'avatar_user_id' => $app->getUserId(),
|
||||||
'avatar_max_width' => $avatarWidthMax,
|
'avatar_max_width' => $avatarWidthMax,
|
||||||
'avatar_max_height' => $avatarHeightMax,
|
'avatar_max_height' => $avatarHeightMax,
|
||||||
'avatar_max_filesize' => $avatarFileSizeMax,
|
'avatar_max_filesize' => $avatarFileSizeMax,
|
||||||
'user_has_avatar' => $userHasAvatar,
|
'user_has_avatar' => $userHasAvatar,
|
||||||
|
'settings_profile_fields' => $profileFields,
|
||||||
|
'settings_profile_values' => $userFields,
|
||||||
|
'settings_disable_account_options' => $disableAccountOptions,
|
||||||
|
'settings_email' => $currentEmail,
|
||||||
]);
|
]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -286,6 +286,7 @@ class Application extends ApplicationBase
|
||||||
$this->templatingInstance->addFilter('html_colour');
|
$this->templatingInstance->addFilter('html_colour');
|
||||||
$this->templatingInstance->addFilter('url_construct');
|
$this->templatingInstance->addFilter('url_construct');
|
||||||
$this->templatingInstance->addFilter('country_name', 'get_country_name');
|
$this->templatingInstance->addFilter('country_name', 'get_country_name');
|
||||||
|
$this->templatingInstance->addFilter('flip', 'array_flip');
|
||||||
$this->templatingInstance->addFilter('first_paragraph');
|
$this->templatingInstance->addFilter('first_paragraph');
|
||||||
$this->templatingInstance->addFilter('colour_get_css');
|
$this->templatingInstance->addFilter('colour_get_css');
|
||||||
$this->templatingInstance->addFilter('colour_get_css_contrast');
|
$this->templatingInstance->addFilter('colour_get_css_contrast');
|
||||||
|
|
|
@ -1,114 +1,195 @@
|
||||||
{% extends '@mio/settings/master.twig' %}
|
{% extends '@mio/settings/master.twig' %}
|
||||||
|
|
||||||
{% block settings_content %}
|
{% block settings_content %}
|
||||||
<form method="post" action="?m=account" class="settings__account">
|
<div class="container">
|
||||||
<div class="settings__account__row">
|
<div class="container__title">Account</div>
|
||||||
<div class="settings__account__column">
|
<form action="" method="post" class="settings__account">
|
||||||
<div class="settings__account__title">Profile</div>
|
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
|
||||||
|
|
||||||
{% for name, props in settings_profile_fields %}
|
<div class="settings__account__row">
|
||||||
<label class="settings__account__input settings__account__input--{{ name }}">
|
{% if settings_perms.edit_profile %}
|
||||||
<div class="settings__account__input__name">
|
<div class="settings__account__column">
|
||||||
{{ props.name }}
|
<div class="settings__account__title">Profile</div>
|
||||||
</div>
|
|
||||||
<div class="settings__account__input__value">
|
|
||||||
<input type="{{ props.type|default('text') }}" name="profile[{{ name }}]" value="{{ settings_profile_values['user_' ~ name] }}" class="input__text settings__account__input__value__text">
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if settings_disable_account_options %}
|
{% for name, props in settings_profile_fields %}
|
||||||
<div class="settings__account__column settings__account__column--no-margin settings__account__column--disabled">
|
<label class="settings__account__input settings__account__input--{{ name }}">
|
||||||
<div class="settings__account__row">
|
<div class="settings__account__input__name">
|
||||||
<div class="settings__account__column">
|
{{ props.name }}
|
||||||
<div class="settings__account__title">E-mail and Password changing</div>
|
</div>
|
||||||
<div class="settings__account__disabled">
|
<div class="settings__account__input__value">
|
||||||
<a class="input__button" href="https://flashii.net/settings.php?m=account">visit main site</a>
|
<input type="{{ props.type|default('text') }}" name="profile[{{ name }}]" value="{{ settings_profile_values['user_' ~ name] }}" class="input__text settings__account__input__value__text">
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if settings_disable_account_options %}
|
||||||
|
<div class="settings__account__column settings__account__column--no-margin settings__account__column--disabled">
|
||||||
|
<div class="settings__account__row">
|
||||||
|
<div class="settings__account__column">
|
||||||
|
<div class="settings__account__title">E-mail and Password changing</div>
|
||||||
|
<div class="settings__account__disabled">
|
||||||
|
<a class="input__button" href="https://flashii.net/settings.php?m=account">visit main site</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% else %}
|
||||||
{% else %}
|
<div class="settings__account__column settings__account__column--no-margin">
|
||||||
<div class="settings__account__column settings__account__column--no-margin">
|
<div class="settings__account__row">
|
||||||
<div class="settings__account__row">
|
<div class="settings__account__column">
|
||||||
<div class="settings__account__column">
|
<div class="settings__account__title">E-mail</div>
|
||||||
<div class="settings__account__title">E-mail</div>
|
|
||||||
|
|
||||||
<label class="settings__account__input">
|
<label class="settings__account__input">
|
||||||
<div class="settings__account__input__name">
|
<div class="settings__account__input__name">
|
||||||
Current e-mail address
|
Current e-mail address
|
||||||
</div>
|
</div>
|
||||||
<div class="settings__account__input__value">
|
<div class="settings__account__input__value">
|
||||||
<input type="text" class="input__text input__text--readonly settings__account__input__value__text" readonly value="{{ settings_email }}">
|
<input type="text" class="input__text input__text--readonly settings__account__input__value__text" readonly value="{{ settings_email }}">
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="settings__account__input">
|
<label class="settings__account__input">
|
||||||
<div class="settings__account__input__name">
|
<div class="settings__account__input__name">
|
||||||
New e-mail Address
|
New e-mail Address
|
||||||
</div>
|
</div>
|
||||||
<div class="settings__account__input__value">
|
<div class="settings__account__input__value">
|
||||||
<input type="text" name="email[new]" class="input__text settings__account__input__value__text">
|
<input type="text" name="email[new]" class="input__text settings__account__input__value__text">
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="settings__account__input">
|
<label class="settings__account__input">
|
||||||
<div class="settings__account__input__name">
|
<div class="settings__account__input__name">
|
||||||
Confirmation
|
Confirmation
|
||||||
</div>
|
</div>
|
||||||
<div class="settings__account__input__value">
|
<div class="settings__account__input__value">
|
||||||
<input type="text" name="email[confirm]" class="input__text settings__account__input__value__text">
|
<input type="text" name="email[confirm]" class="input__text settings__account__input__value__text">
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings__account__row">
|
||||||
|
<div class="settings__account__column">
|
||||||
|
<div class="settings__account__title">Password</div>
|
||||||
|
|
||||||
|
<label class="settings__account__input">
|
||||||
|
<div class="settings__account__input__name">
|
||||||
|
New Password
|
||||||
|
</div>
|
||||||
|
<div class="settings__account__input__value">
|
||||||
|
<input type="password" name="password[new]" class="input__text settings__account__input__value__text">
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="settings__account__input">
|
||||||
|
<div class="settings__account__input__name">
|
||||||
|
Confirmation
|
||||||
|
</div>
|
||||||
|
<div class="settings__account__input__value">
|
||||||
|
<input type="password" name="password[confirm]" class="input__text settings__account__input__value__text">
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings__account__row">
|
||||||
|
<div class="settings__account__column">
|
||||||
|
<div class="settings__account__title">Confirmation</div>
|
||||||
|
|
||||||
|
<label class="settings__account__input">
|
||||||
|
<div class="settings__account__input__name">
|
||||||
|
Current Password
|
||||||
|
</div>
|
||||||
|
<div class="settings__account__input__value">
|
||||||
|
<input type="password" name="current_password" placeholder="only needed for e-mail and password updating" class="input__text settings__account__input__value__text">
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="settings__account__row">
|
{% if settings_perms.edit_profile or not settings_disable_account_options %}
|
||||||
<div class="settings__account__column">
|
<div class="settings__account__row settings__account__row--buttons">
|
||||||
<div class="settings__account__title">Password</div>
|
<button class="input__button">Update</button>
|
||||||
|
<button class="input__button" type="reset">Reset</button>
|
||||||
<label class="settings__account__input">
|
|
||||||
<div class="settings__account__input__name">
|
|
||||||
New Password
|
|
||||||
</div>
|
|
||||||
<div class="settings__account__input__value">
|
|
||||||
<input type="password" name="password[new]" class="input__text settings__account__input__value__text">
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label class="settings__account__input">
|
|
||||||
<div class="settings__account__input__name">
|
|
||||||
Confirmation
|
|
||||||
</div>
|
|
||||||
<div class="settings__account__input__value">
|
|
||||||
<input type="password" name="password[confirm]" class="input__text settings__account__input__value__text">
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings__account__row">
|
|
||||||
<div class="settings__account__column">
|
|
||||||
<div class="settings__account__title">Confirmation</div>
|
|
||||||
|
|
||||||
<label class="settings__account__input">
|
|
||||||
<div class="settings__account__input__name">
|
|
||||||
Current Password
|
|
||||||
</div>
|
|
||||||
<div class="settings__account__input__value">
|
|
||||||
<input type="password" name="current_password" placeholder="only needed for e-mail and password updating" class="input__text settings__account__input__value__text">
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if settings_perms.edit_avatar %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="container__title">Avatar</div>
|
||||||
|
<form action="" method="post" class="settings__images" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="MAX_FILE_SIZE" value="{{ avatar_max_filesize }}">
|
||||||
|
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
|
||||||
|
|
||||||
|
<div class="settings__images__sections">
|
||||||
|
<div class="settings__images__requirements">
|
||||||
|
<ul class="settings__images__requirements__list">
|
||||||
|
<li class="settings__images__requirement settings__images__requirement--header">Guidelines</li>
|
||||||
|
<li class="settings__images__requirement">Keep things sane and suitable for all ages.</li>
|
||||||
|
<li class="settings__images__requirement">Image may not exceed the <strong>{{ avatar_max_filesize|byte_symbol(true) }}</strong> filesize limit.</li>
|
||||||
|
<li class="settings__images__requirement settings__images__requirement--header">Avatar</li>
|
||||||
|
<li class="settings__images__requirement">May not be larger than <strong>{{ avatar_max_width }}x{{ avatar_max_height }}</strong>.</li>
|
||||||
|
<li class="settings__images__requirement">Will be centre cropped to be <strong>200x200</strong>.</li>
|
||||||
|
<li class="settings__images__requirement">Animated gif images are allowed.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings__avatar">
|
||||||
|
<label class="settings__avatar__label">
|
||||||
|
<div
|
||||||
|
class="avatar settings__avatar__preview"
|
||||||
|
id="avatar-preview"
|
||||||
|
style="background-image:url('/profile.php?u={{ avatar_user_id }}&m=avatar')"></div>
|
||||||
|
<input
|
||||||
|
class="settings__avatar__input"
|
||||||
|
accept="image/png,image/jpeg,image/gif"
|
||||||
|
type="file"
|
||||||
|
name="avatar[file]"
|
||||||
|
id="avatar-selection">
|
||||||
|
<div class="settings__avatar__name" id="avatar-name">
|
||||||
|
Click to select a file!
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="settings__avatar__buttons">
|
||||||
|
<button
|
||||||
|
class="settings__avatar__button"
|
||||||
|
name="avatar[mode]"
|
||||||
|
value="upload">
|
||||||
|
Upload
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="settings__avatar__button settings__avatar__button--delete{{ user_has_avatar ? '' : ' settings__avatar__button--disabled' }}"
|
||||||
|
{{ user_has_avatar ? '' : 'disabled' }}
|
||||||
|
name="avatar[mode]"
|
||||||
|
value="delete">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings__account__row settings__account__row--buttons">
|
<script>
|
||||||
<button class="input__button" name="csrf" value="{{ csrf_token() }}">Update</button>
|
function updateAvatarPreview(name, url, previewEl, nameEl) {
|
||||||
<button class="input__button" type="reset">Reset</button>
|
url = url || "/profile.php?u={{ avatar_user_id }}&m=avatar";
|
||||||
</div>
|
previewEl = previewEl || document.getElementById('avatar-preview');
|
||||||
</form>
|
nameEl = nameEl || document.getElementById('avatar-name');
|
||||||
|
previewEl.style.backgroundImage = 'url(\'{0}\')'.replace('{0}', url);
|
||||||
|
nameEl.textContent = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('avatar-selection').addEventListener('change', function (ev) {
|
||||||
|
updateAvatarPreview(ev.target.files[0].name, URL.createObjectURL(ev.target.files[0]));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
{% extends '@mio/settings/master.twig' %}
|
|
||||||
|
|
||||||
{% block settings_content %}
|
|
||||||
<form
|
|
||||||
class="settings__images"
|
|
||||||
method="post"
|
|
||||||
action="?m=images"
|
|
||||||
enctype="multipart/form-data">
|
|
||||||
<input type="hidden"
|
|
||||||
name="MAX_FILE_SIZE"
|
|
||||||
value="{{ avatar_max_filesize }}">
|
|
||||||
<input type="hidden"
|
|
||||||
name="csrf"
|
|
||||||
value="{{ csrf_token() }}">
|
|
||||||
|
|
||||||
<div class="settings__images__sections">
|
|
||||||
<div class="settings__images__requirements">
|
|
||||||
<ul class="settings__images__requirements__list">
|
|
||||||
<li class="settings__images__requirement settings__images__requirement--header">Guidelines</li>
|
|
||||||
<li class="settings__images__requirement">Keep things sane and suitable for all ages.</li>
|
|
||||||
<li class="settings__images__requirement">Image may not exceed the <strong>{{ avatar_max_filesize|byte_symbol(true) }}</strong> filesize limit.</li>
|
|
||||||
<li class="settings__images__requirement settings__images__requirement--header">Avatar</li>
|
|
||||||
<li class="settings__images__requirement">May not be larger than <strong>{{ avatar_max_width }}x{{ avatar_max_height }}</strong>.</li>
|
|
||||||
<li class="settings__images__requirement">Will be centre cropped to be <strong>200x200</strong>.</li>
|
|
||||||
<li class="settings__images__requirement">Animated gif images are allowed.</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings__avatar">
|
|
||||||
<label class="settings__avatar__label">
|
|
||||||
<div
|
|
||||||
class="avatar settings__avatar__preview"
|
|
||||||
id="avatar-preview"
|
|
||||||
style="background-image:url('/profile.php?u={{ avatar_user_id }}&m=avatar')"></div>
|
|
||||||
<input
|
|
||||||
class="settings__avatar__input"
|
|
||||||
accept="image/png,image/jpeg,image/gif"
|
|
||||||
type="file"
|
|
||||||
name="avatar[file]"
|
|
||||||
id="avatar-selection">
|
|
||||||
<div class="settings__avatar__name" id="avatar-name">
|
|
||||||
Click to select a file!
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div class="settings__avatar__buttons">
|
|
||||||
<button
|
|
||||||
class="settings__avatar__button"
|
|
||||||
name="avatar[mode]"
|
|
||||||
value="upload">
|
|
||||||
Upload
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="settings__avatar__button settings__avatar__button--delete{{ user_has_avatar ? '' : ' settings__avatar__button--disabled' }}"
|
|
||||||
{{ user_has_avatar ? '' : 'disabled' }}
|
|
||||||
name="avatar[mode]"
|
|
||||||
value="delete">
|
|
||||||
Delete
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<script>
|
|
||||||
function updateAvatarPreview(name, url, previewEl, nameEl) {
|
|
||||||
url = url || "/profile.php?u={{ avatar_user_id }}&m=avatar";
|
|
||||||
previewEl = previewEl || document.getElementById('avatar-preview');
|
|
||||||
nameEl = nameEl || document.getElementById('avatar-name');
|
|
||||||
previewEl.style.backgroundImage = 'url(\'' + url + '\')';
|
|
||||||
nameEl.textContent = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('avatar-selection').addEventListener('change', function (ev) {
|
|
||||||
updateAvatarPreview(ev.target.files[0].name, URL.createObjectURL(ev.target.files[0]));
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
|
@ -4,51 +4,55 @@
|
||||||
{% set alpagination = pagination(audit_log_count, audit_log_take, audit_log_offset, '?m=log', 'settings__') %}
|
{% set alpagination = pagination(audit_log_count, audit_log_take, audit_log_offset, '?m=log', 'settings__') %}
|
||||||
|
|
||||||
{% block settings_content %}
|
{% block settings_content %}
|
||||||
<div class="settings__description">
|
<div class="container">
|
||||||
<p>This is a log of all "important" actions that have been done using your account for your review. If you notice anything strange, please alert the staff.</p>
|
<div class="container__title">Account Log</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings__log">
|
<div class="settings__log">
|
||||||
{{ alpagination }}
|
<div class="settings__description">
|
||||||
|
<p>This is a log of all "important" actions that have been done using your account for your review. If you notice anything strange, please alert the staff.</p>
|
||||||
{% for log in audit_logs %}
|
|
||||||
<div class="settings__log__entry" id="log-{{ log.log_id }}">
|
|
||||||
<div class="settings__log__column settings__login-history__column--ip">
|
|
||||||
<div class="settings__log__column__name">
|
|
||||||
IP
|
|
||||||
</div>
|
|
||||||
<div class="settings__log__column__value">
|
|
||||||
{{ log.log_ip }}
|
|
||||||
{% if log.log_country|default('XX') != 'XX' %}
|
|
||||||
<img class="settings__log__country" src="https://static.flash.moe/flags/fff/{{ log.log_country|lower }}.png" alt="{{ log.log_country }}" title="{{ log.log_country|country_name }}">
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings__log__column settings__log__column--date" title="{{ log.log_created|date('r') }}">
|
|
||||||
<div class="settings__log__column__name">
|
|
||||||
Date
|
|
||||||
</div>
|
|
||||||
<time class="settings__log__column__value" datetime="{{ log.log_created|date('c') }}">
|
|
||||||
{{ log.log_created|time_diff }}
|
|
||||||
</time>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings__log__column settings__log__column--action">
|
|
||||||
<div class="settings__log__column__name">
|
|
||||||
Action
|
|
||||||
</div>
|
|
||||||
<div class="settings__log__column__value">
|
|
||||||
{% if log.log_action in log_strings|keys %}
|
|
||||||
{{ log_strings[log.log_action]|vsprintf(log.log_params|json_decode) }}
|
|
||||||
{% else %}
|
|
||||||
{{ log.log_action }}({{ log.log_params }})
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{{ alpagination }}
|
{{ alpagination }}
|
||||||
|
|
||||||
|
{% for log in audit_logs %}
|
||||||
|
<div class="settings__log__entry" id="log-{{ log.log_id }}">
|
||||||
|
<div class="settings__log__column settings__login-history__column--ip">
|
||||||
|
<div class="settings__log__column__name">
|
||||||
|
IP
|
||||||
|
</div>
|
||||||
|
<div class="settings__log__column__value">
|
||||||
|
{{ log.log_ip }}
|
||||||
|
{% if log.log_country|default('XX') != 'XX' %}
|
||||||
|
<img class="settings__log__country" src="https://static.flash.moe/flags/fff/{{ log.log_country|lower }}.png" alt="{{ log.log_country }}" title="{{ log.log_country|country_name }}">
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings__log__column settings__log__column--date" title="{{ log.log_created|date('r') }}">
|
||||||
|
<div class="settings__log__column__name">
|
||||||
|
Date
|
||||||
|
</div>
|
||||||
|
<time class="settings__log__column__value" datetime="{{ log.log_created|date('c') }}">
|
||||||
|
{{ log.log_created|time_diff }}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings__log__column settings__log__column--action">
|
||||||
|
<div class="settings__log__column__name">
|
||||||
|
Action
|
||||||
|
</div>
|
||||||
|
<div class="settings__log__column__value">
|
||||||
|
{% if log.log_action in log_strings|keys %}
|
||||||
|
{{ log_strings[log.log_action]|vsprintf(log.log_params|json_decode) }}
|
||||||
|
{% else %}
|
||||||
|
{{ log.log_action }}({{ log.log_params }})
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{{ alpagination }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -4,58 +4,62 @@
|
||||||
{% set lhpagination = pagination(login_attempts_count, login_attempts_take, login_attempts_offset, '?m=login-history', 'settings__') %}
|
{% set lhpagination = pagination(login_attempts_count, login_attempts_take, login_attempts_offset, '?m=login-history', 'settings__') %}
|
||||||
|
|
||||||
{% block settings_content %}
|
{% block settings_content %}
|
||||||
<div class="settings__description">
|
<div class="container">
|
||||||
<p>These are all the login attempts to your account. If any attempt that you don't recognise is marked as successful your account may be compromised, ask a staff member for advice in this case.</p>
|
<div class="container__title">Login History</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings__login-history">
|
<div class="settings__login-history">
|
||||||
{{ lhpagination }}
|
<div class="settings__description">
|
||||||
|
<p>These are all the login attempts to your account. If any attempt that you don't recognise is marked as successful your account may be compromised, ask a staff member for advice in this case.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% for attempt in user_login_attempts %}
|
{{ lhpagination }}
|
||||||
<div class="settings__login-history__entry" id="attempt-{{ attempt.attempt_id }}">
|
|
||||||
<div class="settings__login-history__column settings__login-history__column--ip">
|
|
||||||
<div class="settings__login-history__column__name">
|
|
||||||
IP
|
|
||||||
</div>
|
|
||||||
<div class="settings__login-history__column__value">
|
|
||||||
{{ attempt.attempt_ip_decoded }}
|
|
||||||
{% if attempt.attempt_country != 'XX' %}
|
|
||||||
<img class="settings__login-history__country" src="https://static.flash.moe/flags/fff/{{ attempt.attempt_country|lower }}.png" alt="{{ attempt.attempt_country }}" title="{{ attempt.attempt_country|country_name }}">
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings__login-history__column settings__login-history__column--success">
|
{% for attempt in user_login_attempts %}
|
||||||
<div class="settings__login-history__column__name">
|
<div class="settings__login-history__entry" id="attempt-{{ attempt.attempt_id }}">
|
||||||
Was Successful?
|
<div class="settings__login-history__column settings__login-history__column--ip">
|
||||||
</div>
|
|
||||||
<div class="settings__login-history__column__value settings__login-history__column__value--{{ attempt.was_successful ? 'successful' : 'failed' }}">
|
|
||||||
{{ attempt.was_successful ? 'Yes' : 'No' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings__login-history__column settings__login-history__column--created" title="{{ attempt.created_at|date('r') }}">
|
|
||||||
<div class="settings__login-history__column__name">
|
|
||||||
Attempted
|
|
||||||
</div>
|
|
||||||
<time class="settings__login-history__column__value" datetime="{{ attempt.created_at|date('c') }}">
|
|
||||||
{{ attempt.created_at|time_diff }}
|
|
||||||
</time>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if attempt.user_agent|length > 0 %}
|
|
||||||
<div class="settings__login-history__column settings__login-history__column--user_agent">
|
|
||||||
<div class="settings__login-history__column__name">
|
<div class="settings__login-history__column__name">
|
||||||
User Agent
|
IP
|
||||||
</div>
|
</div>
|
||||||
<div class="settings__login-history__column__value">
|
<div class="settings__login-history__column__value">
|
||||||
{{ attempt.user_agent }}
|
{{ attempt.attempt_ip_decoded }}
|
||||||
|
{% if attempt.attempt_country != 'XX' %}
|
||||||
|
<img class="settings__login-history__country" src="https://static.flash.moe/flags/fff/{{ attempt.attempt_country|lower }}.png" alt="{{ attempt.attempt_country }}" title="{{ attempt.attempt_country|country_name }}">
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{{ lhpagination }}
|
<div class="settings__login-history__column settings__login-history__column--success">
|
||||||
|
<div class="settings__login-history__column__name">
|
||||||
|
Was Successful?
|
||||||
|
</div>
|
||||||
|
<div class="settings__login-history__column__value settings__login-history__column__value--{{ attempt.was_successful ? 'successful' : 'failed' }}">
|
||||||
|
{{ attempt.was_successful ? 'Yes' : 'No' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings__login-history__column settings__login-history__column--created" title="{{ attempt.created_at|date('r') }}">
|
||||||
|
<div class="settings__login-history__column__name">
|
||||||
|
Attempted
|
||||||
|
</div>
|
||||||
|
<time class="settings__login-history__column__value" datetime="{{ attempt.created_at|date('c') }}">
|
||||||
|
{{ attempt.created_at|time_diff }}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if attempt.user_agent|length > 0 %}
|
||||||
|
<div class="settings__login-history__column settings__login-history__column--user_agent">
|
||||||
|
<div class="settings__login-history__column__name">
|
||||||
|
User Agent
|
||||||
|
</div>
|
||||||
|
<div class="settings__login-history__column__value">
|
||||||
|
{{ attempt.user_agent }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{{ lhpagination }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{% set title = 'Settings » ' ~ settings_title %}
|
{% set title = 'Settings » ' ~ settings_title %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{ navigation(settings_navigation, settings_mode, true, '?m=%s') }}
|
{{ navigation(settings_modes|flip, settings_mode, true, '?m=%s') }}
|
||||||
|
|
||||||
{% block settings_container %}
|
{% block settings_container %}
|
||||||
{% if settings_errors is defined and settings_errors|length > 0 %}
|
{% if settings_errors is defined and settings_errors|length > 0 %}
|
||||||
|
@ -20,14 +20,14 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="container settings settings--{{ settings_mode }}">
|
|
||||||
<div class="container__title settings__title settings__title--{{ settings_mode }}">{{ title }}</div>
|
|
||||||
{% block settings_content %}
|
{% block settings_content %}
|
||||||
<div class="container__content">
|
<div class="container">
|
||||||
This is a blank settings page.
|
<div class="container__title">{{ title }}</div>
|
||||||
|
<div class="container__content">
|
||||||
|
This is a blank settings page.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{{ navigation(mio_navigation) }}
|
{{ navigation(mio_navigation) }}
|
||||||
|
|
|
@ -4,66 +4,70 @@
|
||||||
{% set spagination = pagination(sessions_count, sessions_take, sessions_offset, '?m=sessions', 'settings__') %}
|
{% set spagination = pagination(sessions_count, sessions_take, sessions_offset, '?m=sessions', 'settings__') %}
|
||||||
|
|
||||||
{% block settings_content %}
|
{% block settings_content %}
|
||||||
<div class="settings__description">
|
<div class="container">
|
||||||
<p>These are the active logins to your account, clicking the Kill button will force a logout on that session. Your current login is highlighted with a darker purple so you don't accidentally force yourself to logout.</p>
|
<div class="container__title">Login History</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings__sessions">
|
<div class="settings__sessions">
|
||||||
{{ spagination }}
|
<div class="settings__description">
|
||||||
|
<p>These are the active logins to your account, clicking the Kill button will force a logout on that session. Your current login is highlighted with a darker purple so you don't accidentally force yourself to logout.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% for session in user_sessions %}
|
{{ spagination }}
|
||||||
<div class="settings__sessions__entry{% if session.session_id == active_session_id %} settings__sessions__entry--current{% endif %}" id="session-{{ session.session_id }}">
|
|
||||||
<div class="settings__sessions__column settings__sessions__column--ip">
|
|
||||||
<div class="settings__sessions__column__name">
|
|
||||||
IP
|
|
||||||
</div>
|
|
||||||
<div class="settings__sessions__column__value">
|
|
||||||
{{ session.session_ip_decoded }}
|
|
||||||
{% if session.session_country != 'XX' %}
|
|
||||||
<img class="settings__sessions__country" src="https://static.flash.moe/flags/fff/{{ session.session_country|lower }}.png" alt="{{ session.session_country }}" title="{{ session.session_country|country_name }}">
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings__sessions__column settings__sessions__column--created" title="{{ session.created_at|date('r') }}">
|
{% for session in user_sessions %}
|
||||||
<div class="settings__sessions__column__name">
|
<div class="settings__sessions__entry{% if session.session_id == active_session_id %} settings__sessions__entry--current{% endif %}" id="session-{{ session.session_id }}">
|
||||||
Created
|
<div class="settings__sessions__column settings__sessions__column--ip">
|
||||||
</div>
|
|
||||||
<time class="settings__sessions__column__value" datetime="{{ session.created_at|date('c') }}">
|
|
||||||
{{ session.created_at|time_diff }}
|
|
||||||
</time>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings__sessions__column settings__sessions__column--expires" title="{{ session.expires_on|date('r') }}">
|
|
||||||
<div class="settings__sessions__column__name">
|
|
||||||
Expires
|
|
||||||
</div>
|
|
||||||
<time class="settings__sessions__column__value" datetime="{{ session.expires_on|date('c') }}">
|
|
||||||
{{ session.expires_on|time_diff }}
|
|
||||||
</time>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if session.user_agent|length > 0 %}
|
|
||||||
<div class="settings__sessions__column settings__sessions__column--user_agent">
|
|
||||||
<div class="settings__sessions__column__name">
|
<div class="settings__sessions__column__name">
|
||||||
User Agent
|
IP
|
||||||
</div>
|
</div>
|
||||||
<div class="settings__sessions__column__value">
|
<div class="settings__sessions__column__value">
|
||||||
{{ session.user_agent }}
|
{{ session.session_ip_decoded }}
|
||||||
|
{% if session.session_country != 'XX' %}
|
||||||
|
<img class="settings__sessions__country" src="https://static.flash.moe/flags/fff/{{ session.session_country|lower }}.png" alt="{{ session.session_country }}" title="{{ session.session_country|country_name }}">
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form class="settings__sessions__column settings__sessions__column--options" method="post" action="?m=sessions">
|
<div class="settings__sessions__column settings__sessions__column--created" title="{{ session.created_at|date('r') }}">
|
||||||
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
|
<div class="settings__sessions__column__name">
|
||||||
<input type="hidden" name="session" value="{{ session.session_id }}">
|
Created
|
||||||
<button class="input__button settings__sessions__button">
|
</div>
|
||||||
{{ session.session_id == active_session_id ? 'Logout' : 'Kill' }}
|
<time class="settings__sessions__column__value" datetime="{{ session.created_at|date('c') }}">
|
||||||
</button>
|
{{ session.created_at|time_diff }}
|
||||||
</form>
|
</time>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
{{ spagination }}
|
<div class="settings__sessions__column settings__sessions__column--expires" title="{{ session.expires_on|date('r') }}">
|
||||||
|
<div class="settings__sessions__column__name">
|
||||||
|
Expires
|
||||||
|
</div>
|
||||||
|
<time class="settings__sessions__column__value" datetime="{{ session.expires_on|date('c') }}">
|
||||||
|
{{ session.expires_on|time_diff }}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if session.user_agent|length > 0 %}
|
||||||
|
<div class="settings__sessions__column settings__sessions__column--user_agent">
|
||||||
|
<div class="settings__sessions__column__name">
|
||||||
|
User Agent
|
||||||
|
</div>
|
||||||
|
<div class="settings__sessions__column__value">
|
||||||
|
{{ session.user_agent }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form class="settings__sessions__column settings__sessions__column--options" method="post" action="?m=sessions">
|
||||||
|
<input type="hidden" name="csrf" value="{{ csrf_token() }}">
|
||||||
|
<input type="hidden" name="session" value="{{ session.session_id }}">
|
||||||
|
<button class="input__button settings__sessions__button">
|
||||||
|
{{ session.session_id == active_session_id ? 'Logout' : 'Kill' }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{{ spagination }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Add table
Reference in a new issue