Management stuff (also pagination in places).

This commit is contained in:
flash 2018-04-23 05:00:55 +02:00
parent 6c9040dc15
commit aa83955c7b
21 changed files with 471 additions and 147 deletions

View file

@ -2,14 +2,22 @@
border: 1px solid #9475b2;
list-style: none;
display: flex;
justify-content: space-between;
padding: 1px;
&__separator {
flex-grow: 1;
flex-shrink: 1;
}
&__option {
background: #9475b2;
height: 20px;
width: 20px;
&:not(:last-child){
margin-right: 1px;
}
@media (max-width: @mio-news-mobile) {
height: 30px;
width: 30px;
@ -33,8 +41,4 @@
font-weight: bold;
}
}
&__separator {
display: none;
}
}

View file

@ -0,0 +1,46 @@
.settings__pagination {
list-style: none;
display: flex;
max-width: 400px;
margin: 2px auto;
padding: 1px;
border: 1px solid #9475b2;
&__separator {
flex-grow: 1;
flex-shrink: 1;
}
&__option {
background: #9475b2;
height: 20px;
width: 20px;
&:not(:last-child){
margin-right: 1px;
}
@media (max-width: @mio-news-mobile) {
height: 30px;
width: 30px;
font-size: 1.5em;
line-height: 1.5em;
}
}
&__link {
display: block;
width: 100%;
height: 100%;
text-align: center;
text-decoration: none;
color: #306;
&--prev,
&--next,
&--active,
&:hover {
font-weight: bold;
}
}
}

View file

@ -56,6 +56,7 @@ body {
@import "classes/settings/content";
@import "classes/settings/description";
@import "classes/settings/errors";
@import "classes/settings/pagination";
@import "classes/settings/account";
@import "classes/settings/avatar";
@import "classes/settings/login-history";

View file

@ -46,7 +46,7 @@ if (PHP_SAPI !== 'cli') {
$app->templating->addPath('mio', __DIR__ . '/views/mio');
if ($manage_mode) {
if ($app->getSession() === null || $_SERVER['HTTP_HOST'] !== 'misuzu.misaka.nl') {
if ($app->getSession() === null || $app->getSession()->user->user_id !== 1) {
http_response_code(403);
echo $app->templating->render('errors.403');
exit;

16
public/manage/forums.php Normal file
View file

@ -0,0 +1,16 @@
<?php
require_once __DIR__ . '/../../misuzu.php';
switch ($_GET['v'] ?? null) {
case 'listing':
echo 'forum listing here';
break;
case 'permissions':
echo 'permissions here, not even sure what this would do';
break;
case 'settings':
echo 'overall forum settings here';
break;
}

View file

@ -1,4 +1,24 @@
<?php
require_once __DIR__ . '/../../misuzu.php';
echo $app->templating->render('@manage.general.overview');
switch ($_GET['v'] ?? null) {
case 'overview':
echo $app->templating->render('@manage.general.overview');
break;
case 'logs':
echo 'soon';
break;
case 'emoticons':
echo 'soon as well';
break;
case 'settings':
echo 'somewhat soon i guess';
break;
case null:
header('Location: ?v=overview');
break;
}

View file

@ -1,11 +0,0 @@
<?php
use Misuzu\Application;
use Misuzu\Users\User;
require_once __DIR__ . '/../../misuzu.php';
$users_page = (int)($_GET['p'] ?? 1);
$manage_users = User::paginate(32, ['*'], 'p', $users_page);
$app->templating->vars(compact('manage_users', 'users_page'));
echo $app->templating->render('@manage.users.listing');

View file

@ -1,110 +0,0 @@
<?php
use Misuzu\Application;
use Misuzu\Colour;
use Misuzu\Users\Role;
require_once __DIR__ . '/../../misuzu.php';
$role_mode = (string)($_GET['m'] ?? 'list');
$role_id = (int)($_GET['i'] ?? 0);
while ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
echo 'csrf err';
break;
}
if (!in_array($role_mode, ['create', 'edit'], true)) {
echo 'invalid mode';
break;
}
if (!isset($_POST['role'])) {
echo 'no';
break;
}
$role_name = $_POST['role']['name'] ?? '';
$role_name_length = strlen($role_name);
if ($role_name_length < 1 || $role_name_length > 255) {
echo 'invalid name length';
break;
}
$role_secret = !empty($_POST['role']['secret']);
$role_hierarchy = (int)($_POST['role']['hierarchy'] ?? -1);
if ($role_hierarchy < 1 || $role_hierarchy > 100) {
echo 'Invalid hierarchy value.';
break;
}
$role_colour = Colour::none();
$role_colour->setInherit(!empty($_POST['role']['colour']['inherit']));
if (!$role_colour->getInherit()) {
foreach (['red', 'green', 'blue'] as $key) {
$value = (int)($_POST['role']['colour'][$key] ?? -1);
$setter = 'set' . ucfirst($key);
if ($value < 0 || $value > 0xFF) {
echo 'invalid colour value';
break 2;
}
$role_colour->{$setter}($value);
}
}
$role_description = $_POST['role']['description'] ?? '';
if (strlen($role_description) > 1000) {
echo 'description is too long';
break;
}
$edit_role = $role_id < 1 ? new Role : Role::find($role_id);
$edit_role->role_name = $role_name;
$edit_role->role_hierarchy = $role_hierarchy;
$edit_role->role_secret = $role_secret;
$edit_role->role_colour = $role_colour;
$edit_role->role_description = $role_description;
$edit_role->save();
header('Location: ?m=list');
break;
}
switch ($role_mode) {
case 'list':
$users_page = (int)($_GET['p'] ?? 1);
$manage_roles = Role::paginate(32, ['*'], 'p', $users_page);
$app->templating->vars(compact('manage_roles'));
echo $app->templating->render('@manage.users.roles');
break;
case 'edit':
if (!isset($edit_role)) {
if ($role_id < 1) {
echo 'no';
break;
}
$edit_role = Role::find($role_id);
}
if ($edit_role === null) {
echo 'invalid role';
break;
}
$app->templating->vars(compact('edit_role'));
// no break
case 'create':
echo $app->templating->render('@manage.users.roles_create');
break;
}

129
public/manage/users.php Normal file
View file

@ -0,0 +1,129 @@
<?php
use Misuzu\Application;
use Misuzu\Colour;
use Misuzu\Users\Role;
use Misuzu\Users\User;
require_once __DIR__ . '/../../misuzu.php';
$is_post_request = $_SERVER['REQUEST_METHOD'] === 'POST';
$page_id = (int)($_GET['p'] ?? 1);
switch ($_GET['v'] ?? null) {
case 'listing':
$manage_users = User::paginate(32, ['*'], 'p', $page_id);
$app->templating->vars(compact('manage_users'));
echo $app->templating->render('@manage.users.listing');
break;
case 'view':
$user_id = $_GET['u'] ?? null;
if ($user_id === null || ($user_id = (int)$user_id) < 1) {
echo 'no';
break;
}
$view_user = User::find($user_id);
if ($view_user === null) {
echo 'Could not find that user.';
break;
}
$app->templating->var('view_user', $view_user);
echo $app->templating->render('@manage.users.view');
break;
case 'roles':
$manage_roles = Role::paginate(32, ['*'], 'p', $page_id);
$app->templating->vars(compact('manage_roles'));
echo $app->templating->render('@manage.users.roles');
break;
case 'role':
$role_id = $_GET['r'] ?? null;
if ($is_post_request) {
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
echo 'csrf err';
break;
}
if (!isset($_POST['role'])) {
echo 'no';
break;
}
$role_name = $_POST['role']['name'] ?? '';
$role_name_length = strlen($role_name);
if ($role_name_length < 1 || $role_name_length > 255) {
echo 'invalid name length';
break;
}
$role_secret = !empty($_POST['role']['secret']);
$role_hierarchy = (int)($_POST['role']['hierarchy'] ?? -1);
if ($role_hierarchy < 1 || $role_hierarchy > 100) {
echo 'Invalid hierarchy value.';
break;
}
$role_colour = Colour::none();
$role_colour->setInherit(!empty($_POST['role']['colour']['inherit']));
if (!$role_colour->getInherit()) {
foreach (['red', 'green', 'blue'] as $key) {
$value = (int)($_POST['role']['colour'][$key] ?? -1);
$setter = 'set' . ucfirst($key);
if ($value < 0 || $value > 0xFF) {
echo 'invalid colour value';
break 2;
}
$role_colour->{$setter}($value);
}
}
$role_description = $_POST['role']['description'] ?? '';
if (strlen($role_description) > 1000) {
echo 'description is too long';
break;
}
$edit_role = $role_id < 1 ? new Role : Role::find($role_id);
$edit_role->role_name = $role_name;
$edit_role->role_hierarchy = $role_hierarchy;
$edit_role->role_secret = $role_secret;
$edit_role->role_colour = $role_colour;
$edit_role->role_description = $role_description;
$edit_role->save();
header("Location: ?v=role&r={$edit_role->role_id}");
break;
}
if ($role_id !== null) {
if ($role_id < 1) {
echo 'no';
break;
}
$edit_role = Role::find($role_id);
if ($edit_role === null) {
echo 'invalid role';
break;
}
$app->templating->vars(compact('edit_role'));
}
echo $app->templating->render('@manage.users.roles_create');
break;
}

View file

@ -8,6 +8,8 @@ require_once __DIR__ . '/../misuzu.php';
$settings_session = Application::getInstance()->getSession();
$page_id = (int)($_GET['p'] ?? 1);
if (Application::getInstance()->getSession() === null) {
http_response_code(403);
echo $app->templating->render('errors.403');
@ -345,11 +347,19 @@ switch ($settings_mode) {
break;
case 'sessions':
$app->templating->var('user_sessions', $settings_user->sessions->reverse());
$sessions = $settings_user->sessions()
->orderBy('session_id', 'desc')
->paginate(15, ['*'], 'p', $page_id);
$app->templating->var('user_sessions', $sessions);
break;
case 'login-history':
$app->templating->var('user_login_attempts', $settings_user->loginAttempts->reverse());
$login_attempts = $settings_user->loginAttempts()
->orderBy('attempt_id', 'desc')
->paginate(15, ['*'], 'p', $page_id);
$app->templating->var('user_login_attempts', $login_attempts);
break;
}

View file

@ -237,7 +237,7 @@ class User extends Model
$this->attributes['register_ip'] = $ipAddress->getRaw();
}
public function getLastIpAttribute(string $ipAddress): string
public function getLastIpAttribute(string $ipAddress): IPAddress
{
return IPAddress::fromRaw($ipAddress);
}

View file

@ -1 +1,7 @@
{% extends '@manage/general/master.twig' %}
{% block content %}
<div class="container">
<p>Welcome to Manage, here you can manage things.</p>
</div>
{% endblock %}

View file

@ -9,21 +9,24 @@
{
'name': 'overview',
'title': 'Overview',
'url': '/manage/index.php',
'url': '/manage/index.php?v=overview',
},
{
'name': 'logs',
'title': 'Logs',
'url': '/manage/index.php?v=logs',
},
],
[
{
'name': 'emotes',
'title': 'Emoticons',
'url': '/manage/index.php?v=emoticons',
},
{
'name': 'settings',
'title': 'Settings',
'url': '/manage/index.php?v=settings',
},
],
],
@ -36,30 +39,36 @@
{
'name': 'listing',
'title': 'Listing',
'url': '/manage/users.php?v=listing',
},
],
[
{
'name': 'roles',
'title': 'Roles',
'url': '/manage/users.php?v=roles',
},
{
'name': 'perms',
'title': 'Permissions',
'url': '/manage/users.php?v=permissions',
},
],
[
{
'name': 'report',
'title': 'Reports',
'url': '/manage/users.php?v=reports',
},
{
'name': 'restrictions',
'title': 'Restrictions',
'url': '/manage/users.php?v=restrictions',
},
{
'name': 'blacklist',
'title': 'Blacklisting',
'url': '/manage/users.php?v=blacklist',
},
],
],
@ -72,14 +81,17 @@
{
'name': 'forums',
'title': 'Listing',
'url': '/manage/forums.php?v=listing',
},
{
'name': 'perms',
'title': 'Permissions',
'url': '/manage/forums.php?v=permissions',
},
{
'name': 'settings',
'title': 'Settings',
'url': '/manage/forums.php?v=settings',
},
],
],
@ -112,7 +124,7 @@
{% for section in menu.sections %}
<div class="header__menu__section">
{% for item in section %}
<a class="header__menu__link header__menu__link--{{ menu.name }}-{{ item.name }}" href="{{ item.url|default(menu.name ~ '-' ~ item.name ~ '.php') }}">{{ item.title }}</a>
<a class="header__menu__link header__menu__link--{{ menu.name }}-{{ item.name }}" href="{{ item.url }}">{{ item.title }}</a>
{% endfor %}
</div>
{% endfor %}
@ -124,7 +136,7 @@
<div class="header__user">
<div class="header__menu">
<input type="checkbox" id="menu-user-state" class="header__menu__state">
<label for="menu-user-state" class="header__menu__toggle header__menu__toggle--profile" style="background-image:url('/profile.php?u={{ app.session.user.user_id }}&amp;m=avatar');">{{ app.session.user.username }}</label>
<label for="menu-user-state" class="header__menu__toggle header__menu__toggle--profile" style="background-image:url('/profile.php?u={{ app.session.user.user_id }}&amp;m=avatar');color:{{ app.session.user.displayColour.inherit ? 'inherit' : app.session.user.displayColour }}">{{ app.session.user.username }}</label>
<div class="header__menu__options header__menu__options--user">
<div class="header__menu__section">
<a class="header__menu__link" href="/profile.php?u={{ app.session.user.user_id }}">Profile</a>
@ -142,7 +154,7 @@
{% block content %}
<div class="container">
<p> This page has no content.</p>
<p>This page has no content.</p>
</div>
{% endblock %}

View file

@ -4,19 +4,18 @@
{% block content %}
<div class="container listing user-listing">
{% for user in manage_users %}
<label class="listing__entry user-listing__entry"{% if not user.displayColour.inherit %} style="border-color: {{ user.displayColour }}"{% endif %}>
<a href="?v=view&amp;u={{ user.user_id }}" class="listing__entry user-listing__entry"{% if not user.displayColour.inherit %} style="border-color: {{ user.displayColour }}"{% endif %}>
<div class="listing__entry__content user-listing__entry__content">
<input class="listing__entry__selector" type="checkbox">
<a href="/profile.php?u={{ user.user_id }}" class="listing__entry__column user-listing__entry__column user-listing__entry__column--username">
<div class="listing__entry__column user-listing__entry__column user-listing__entry__column--username">
{{ user.username }}
</a>
</div>
<div class="user-listing__avatar" style="background-image:url('/profile.php?u={{ user.user_id }}&amp;m=avatar');"></div>
</div>
</label>
</a>
{% endfor %}
</div>
<div class="container container--center">
{{ paginate(manage_users) }}
{{ paginate(manage_users, '?v=listing') }}
</div>
{% endblock %}

View file

@ -8,7 +8,7 @@
<div class="container listing role-listing">
{% for role in manage_roles %}
<a href="?m=edit&amp;i={{ role.role_id }}" class="listing__entry role-listing__entry"{% if not role.role_colour.inherit %} style="border-color: {{ role.role_colour }}"{% endif %}>
<a href="?v=role&amp;r={{ role.role_id }}" class="listing__entry role-listing__entry"{% if not role.role_colour.inherit %} style="border-color: {{ role.role_colour }}"{% endif %}>
<div class="listing__entry__content role-listing__entry__content">
{{ role.role_name }}
{{ role.users.count }} users
@ -18,6 +18,6 @@
</div>
<div class="container container--center">
{{ paginate(manage_roles) }}
{{ paginate(manage_roles, '?v=roles') }}
</div>
{% endblock %}

View file

@ -1,9 +1,15 @@
{% extends '@manage/users/master.twig' %}
{% block content %}
<form action="?m={{ edit_role is defined ? 'edit&i=' ~ edit_role.role_id : 'create' }}" method="post">
<form action="?v=role{{ edit_role is defined ? '&r=' ~ edit_role.role_id : '' }}" method="post">
<div class="container">
<h1 class="container__title container__title">Creating a new Role</h1>
<h1 class="container__title">
{% if edit_role is defined %}
Editing <span style="color:{{ edit_role.role_colour.inherit ? 'inherit' : edit_role.role_colour }}">{{ edit_role.role_name }}</span> ({{ edit_role.role_id }})
{% else %}
Creating a new Role
{% endif %}
</h1>
<label class="form__label">
<div class="form__label__text">Role Name</div>

View file

@ -0,0 +1,182 @@
{% extends '@manage/users/master.twig' %}
{% block content %}
<div class="container">
<h1 class="container__title">
Viewing <span style="color:{{ view_user.displayColour.inherit ? 'inherit' : view_user.displayColour }}">{{ view_user.username }}</span> ({{ view_user.user_id }})
</h1>
<label class="form__label">
<div class="form__label__text">Username</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.username }}" name="user[username]" maxlength="16">
</div>
</label>
<label class="form__label">
<div class="form__label__text">E-mail address</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.email }}" name="user[email]" maxlength="255">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Register IP</div>
<div class="form__label__input">
<input class="input input--text" readonly type="text" value="{{ view_user.register_ip.string }}">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Last IP</div>
<div class="form__label__input">
<input class="input input--text" readonly type="text" value="{{ view_user.last_ip.string }}">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Country</div>
<div class="form__label__input">
<input class="input input--text" readonly type="text" value="{{ view_user.user_country }}">
</div>
</label>
<div>
<button class="button" name="csrf" value="{{ csrf_token() }}">Update</button>
</div>
</div>
<div class="container">
<h1 class="container__title">
Avatar
</h1>
<label class="form__label">
<div class="form__label__text">New Avatar</div>
<div class="form__label__input">
<input class="input input--text" type="file" name="user[avatar]">
</div>
</label>
<div>
<button class="button" name="csrf" value="{{ csrf_token() }}">Upload</button>
<button class="button" name="csrf" value="{{ csrf_token() }}">Delete</button>
</div>
</div>
<div class="container">
<h1 class="container__title">
Password
</h1>
<label class="form__label">
<div class="form__label__text">New Password</div>
<div class="form__label__input">
<input class="input input--text" type="password" name="user[password]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Confirm Password</div>
<div class="form__label__input">
<input class="input input--text" type="password" name="user[password_confirm]">
</div>
</label>
<div>
<button class="button" name="csrf" value="{{ csrf_token() }}">Update</button>
</div>
</div>
<div class="container">
<h1 class="container__title">
Profile fields
</h1>
<label class="form__label">
<div class="form__label__text">Twitter</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_twitter }}" name="user[twitter]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">osu!</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_osu }}" name="user[osu]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Website</div>
<div class="form__label__input">
<input class="input input--text" type="url" value="{{ view_user.user_website }}" name="user[website]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Youtube</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_youtube }}" name="user[youtube]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Steam</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_steam }}" name="user[steam]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Twitch.tv</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_twitchtv }}" name="user[twitchtv]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Last.fm</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_twitchtv }}" name="user[twitchtv]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Github</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_github }}" name="user[github]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Skype</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_skype }}" name="user[skype]">
</div>
</label>
<label class="form__label">
<div class="form__label__text">Discord</div>
<div class="form__label__input">
<input class="input input--text" type="text" value="{{ view_user.user_discord }}" name="user[discord]">
</div>
</label>
<div>
<button class="button" name="csrf" value="{{ csrf_token() }}">Update</button>
</div>
</div>
<div class="container">
<h1 class="container__title">Roles</h1>
</div>
<div class="container">
<h1 class="container__title">Permissions</h1>
</div>
<div class="container">
<h1 class="container__title">Sessions</h1>
</div>
{% endblock %}

View file

@ -13,7 +13,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
{% include '@mio/_layout/meta.twig' %}
<link href="https://static.flash.moe/fonts/visitor/visitor1.css" rel="stylesheet">
<link href="https://use.fontawesome.com/releases/v5.0.8/css/all.css" rel="stylesheet">
<link href="/css/mio.css" rel="stylesheet">
</head>
<body class="mio">
@ -31,11 +30,14 @@
<div class="container__title">Hey, {{ app.session.user.username }}!</div>
<div class="container__content header__user__content">
<a href="/settings.php?m=avatar" class="avatar header__user__avatar" style="background-image:url('/profile.php?u={{ app.session.user.user_id }}&amp;m=avatar');"></a>
<div class="header__user__links__container">
<ul class="header__user__links">
<li class="header__user__option"><a class="header__user__link" href="/profile.php?u={{ app.session.user.user_id }}">Profile</a></li>
<li class="header__user__option"><a class="header__user__link" href="/settings.php">Settings</a></li>
<li class="header__user__option"><a class="header__user__link" href="{{ manage_link|default('/manage/index.php') }}">Manage</a></li>
{% if app.session.user.user_id == 1 %}
<li class="header__user__option"><a class="header__user__link" href="{{ manage_link|default('/manage/index.php') }}">Manage</a></li>
{% endif %}
<li class="header__user__option"><a class="header__user__link" href="/auth.php?m=logout&amp;s={{ csrf_token() }}">Logout</a></li>
</ul>
</div>
@ -64,6 +66,7 @@
''|date('Y') }} /
{{ link('https://github.com/flashwave/misuzu/tree/' ~ git_branch(), git_branch(), 'footer__copyright__link') }} # {{ link('https://github.com/flashwave/misuzu/commit/' ~ git_hash(true), git_hash(), 'footer__copyright__link') }}
</div>
<div class="footer__links">
{{ link('#', 'Terms of Service', 'footer__links__link') }} |
{{ link('#', 'Rules', 'footer__links__link') }} |

View file

@ -1,4 +1,5 @@
{% extends '@mio/settings/master.twig' %}
{% from '@mio/macros.twig' import paginate %}
{% block settings_content %}
<div class="settings__description">
@ -47,5 +48,7 @@
{% endif %}
</div>
{% endfor %}
{{ paginate(user_login_attempts, '?m=login-history', 'settings__') }}
</div>
{% endblock %}

View file

@ -1,4 +1,5 @@
{% extends '@mio/settings/master.twig' %}
{% from '@mio/macros.twig' import paginate %}
{% block settings_content %}
<div class="settings__description">
@ -19,6 +20,7 @@
{% endif %}
</div>
</div>
<div class="settings__sessions__column settings__sessions__column--created" onmouseenter="this.children[1].textContent = '{{ session.created_at }}';" onmouseleave="this.children[1].textContent = '{{ session.created_at.diffForHumans }}';">
<div class="settings__sessions__column__name">
Created
@ -27,6 +29,7 @@
{{ session.created_at.diffForHumans }}
</div>
</div>
<div class="settings__sessions__column settings__sessions__column--expires" onmouseenter="this.children[1].textContent = '{{ session.expires_on }}';" onmouseleave="this.children[1].textContent = '{{ session.expires_on.diffForHumans }}';">
<div class="settings__sessions__column__name">
Expires
@ -35,6 +38,7 @@
{{ session.expires_on.diffForHumans }}
</div>
</div>
{% if session.user_agent|length > 0 %}
<div class="settings__sessions__column settings__sessions__column--user_agent">
<div class="settings__sessions__column__name">
@ -45,6 +49,7 @@
</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 }}">
@ -52,5 +57,7 @@
</form>
</div>
{% endfor %}
{{ paginate(user_sessions, '?m=sessions', 'settings__') }}
</div>
{% endblock %}

View file

@ -4,6 +4,7 @@
{% set image = '/profile.php?u=' ~ profile.user_id ~ '&m=avatar' %}
{% set canonical_url = '/profile.php?u=' ~ profile.user_id %}
{% set title = 'Profile of ' ~ profile.username %}
{% set manage_link = '/manage/users.php?v=view&u=' ~ profile.user_id %}
{% set youtube_is_channel_id = profile.user_youtube|slice(0, 2) == 'UC' and profile.user_youtube|length == 24 %}