various fixes
This commit is contained in:
parent
028de7b576
commit
4daf30d75a
13 changed files with 411 additions and 393 deletions
|
@ -8,7 +8,6 @@ namespace Sakura\Console\Command;
|
|||
|
||||
use CLIFramework\Command;
|
||||
use Sakura\DB;
|
||||
use Sakura\Forum\Post;
|
||||
|
||||
/**
|
||||
* Purges users that have been inactive for 30 days or more.
|
||||
|
|
|
@ -61,7 +61,7 @@ class AuthController extends Controller
|
|||
public function logout(): void
|
||||
{
|
||||
if (!session_check()) {
|
||||
throw new HttpMethodNotAllowedException;
|
||||
throw new HttpMethodNotAllowedException;
|
||||
}
|
||||
|
||||
// Destroy the active session
|
||||
|
|
|
@ -24,24 +24,27 @@ class UserController extends Controller
|
|||
/**
|
||||
* Display the profile of a user.
|
||||
* @param int $id
|
||||
* @throws HttpRouteNotFoundException
|
||||
* @return string
|
||||
*/
|
||||
public function profile(int $id = 0): string
|
||||
{
|
||||
$profile = User::construct($id);
|
||||
|
||||
// If the user id is zero check if there was a namechange
|
||||
if ($profile->id === 0) {
|
||||
// Fetch from username_history
|
||||
$check = DB::table('username_history')
|
||||
->where('username_old_clean', clean_string($id, true))
|
||||
->orderBy('change_id', 'desc')
|
||||
->first();
|
||||
|
||||
// Redirect if so
|
||||
if ($check) {
|
||||
return redirect(route('user.profile', $check->user_id));
|
||||
}
|
||||
if ($profile->id === 0
|
||||
|| !$profile->activated
|
||||
|| (
|
||||
$profile->restricted
|
||||
&& (
|
||||
$profile->id !== CurrentSession::$user->id
|
||||
|| !(
|
||||
CurrentSession::$user->perms->isMod
|
||||
|| CurrentSession::$user->perms->isAdmin
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
throw new HttpRouteNotFoundException;
|
||||
}
|
||||
|
||||
return view('user/profile', compact('profile'));
|
||||
|
@ -54,12 +57,22 @@ class UserController extends Controller
|
|||
*/
|
||||
public function resolve(string $name): string
|
||||
{
|
||||
$clean_name = clean_string($name, true);
|
||||
|
||||
$id = DB::table('users')
|
||||
->where('username_clean', clean_string($name, true))
|
||||
->where('username_clean', $clean_name)
|
||||
->value('user_id');
|
||||
|
||||
if (!$id) {
|
||||
throw new HttpRouteNotFoundException;
|
||||
// Fetch from username_history
|
||||
$id = DB::table('username_history')
|
||||
->where('username_old_clean', $clean_name)
|
||||
->orderBy('change_id', 'desc')
|
||||
->value('user_id');
|
||||
|
||||
if (!$id) {
|
||||
throw new HttpRouteNotFoundException;
|
||||
}
|
||||
}
|
||||
|
||||
return redirect(route('user.profile', $id));
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace Sakura\News;
|
|||
|
||||
use Carbon\Carbon;
|
||||
use Sakura\DB;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* News category object.
|
||||
|
@ -90,7 +91,7 @@ class Category
|
|||
$posts->limit($limit);
|
||||
}
|
||||
|
||||
$this->postsCache = array_map(function ($post) {
|
||||
$this->postsCache = array_map(function (stdClass $post) {
|
||||
return new Post($post->post_id);
|
||||
}, $posts->get(['post_id']));
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
@media (max-width: 1064px) {
|
||||
&--left,
|
||||
&--right {
|
||||
width: 100%;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
&--left {
|
||||
|
|
78
resources/views/yuuno/auth/header_auth.twig
Normal file
78
resources/views/yuuno/auth/header_auth.twig
Normal file
|
@ -0,0 +1,78 @@
|
|||
{% if user.id == 0 %}
|
||||
<div class="header-login-container">
|
||||
<form class="header-login" method="post" action="javascript:void(0);" onsubmit="yuunoAttemptLogin(this)">
|
||||
<div class="header-login__sub header-login__sub--form">
|
||||
<input type="text" name="username" class="input__text header-login__text" placeholder="Username">
|
||||
<input type="password" name="password" class="input__text header-login__text" placeholder="Password">
|
||||
<input type="hidden" name="session" value="{{ session_id() }}">
|
||||
<button class="input__button header-login__button">
|
||||
<i class="fa fa-sign-in"></i> Login
|
||||
</button>
|
||||
</div>
|
||||
<div class="header-login__sub header-login__sub--buttons">
|
||||
<a class="input__button header-login__button header-login__button--small" href="{{ route('auth.register') }}">
|
||||
<i class="fa fa-magic"></i> I don't have an account yet!
|
||||
</a>
|
||||
<a class="input__button header-login__button header-login__button--small" href="{{ route('auth.resetpassword') }}">
|
||||
<i class="fa fa-exclamation-triangle"></i> I don't have access to my account!
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function yuunoAttemptLogin(form) {
|
||||
var ajax = new Sakura.AJAX;
|
||||
|
||||
ajax.SetUrl("{{ route('auth.login') }}");
|
||||
ajax.SetFormData(new FormData(form));
|
||||
|
||||
ajax.AddCallback(200, function () {
|
||||
var result = ajax.JSON();
|
||||
|
||||
if (result.error) {
|
||||
var diag = new Sakura.Dialogue;
|
||||
diag.Title = "Login Error";
|
||||
diag.Text = result.error;
|
||||
diag.Display();
|
||||
} else if (result.go) {
|
||||
window.location.assign(result.go);
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
ajax.Start(Sakura.HTTPMethod.POST);
|
||||
}
|
||||
</script>
|
||||
{% else %}
|
||||
<script>
|
||||
function yuunoLogout() {
|
||||
var confirm = new Sakura.Dialogue;
|
||||
confirm.SetType(Sakura.DialogueType.ConfirmNegative);
|
||||
confirm.Title = "Logout";
|
||||
confirm.Text = "Are you sure?";
|
||||
|
||||
confirm.AddCallback(Sakura.DialogueButton.Yes, function () {
|
||||
var ajax = new Sakura.AJAX;
|
||||
ajax.SetUrl("{{ route('auth.logout') }}?session=" + Sakura.Config.SessionId);
|
||||
|
||||
ajax.AddCallback(200, function () {
|
||||
window.location.reload();
|
||||
});
|
||||
ajax.AddCallback(403, function () {
|
||||
confirm.Close();
|
||||
|
||||
var error = new Sakura.Dialogue;
|
||||
error.Title = "Logout Error";
|
||||
error.Text = "Logout failed.";
|
||||
error.Display();
|
||||
});
|
||||
|
||||
ajax.Start(Sakura.HTTPMethod.DELETE);
|
||||
});
|
||||
|
||||
confirm.Display();
|
||||
}
|
||||
</script>
|
||||
{% endif %}
|
0
resources/views/yuuno/elements/comments_javascript.twig
Normal file
0
resources/views/yuuno/elements/comments_javascript.twig
Normal file
|
@ -1,12 +1,3 @@
|
|||
{% macro profile_image_changer(url, query) %}
|
||||
<div class="uploader">
|
||||
<label class="uploader__button uploader__button--change fa fa-edit">
|
||||
<input type="file" class="uploader__change" onchange="handleImageChange(this.files[0], '{{ url }}', '{{ query }}')">
|
||||
</label>
|
||||
<button class="uploader__button uploader__button--delete fa fa-trash" onclick="handleImageDelete('{{ url }}', '{{ query }}')"></button>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro news_post(id, text, created, user, title, comments) %}
|
||||
{% if title is defined and title %}
|
||||
<a href="{{ route('news.post', id) }}" class="news__head" id="p{{ id }}">{{ title }}</a>
|
||||
|
@ -55,4 +46,4 @@
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
|
|
@ -63,84 +63,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{% if user.id == 0 %}
|
||||
<div class="header-login-container">
|
||||
<form class="header-login" method="post" action="javascript:void(0);" onsubmit="yuunoAttemptLogin(this)">
|
||||
<div class="header-login__sub header-login__sub--form">
|
||||
<input type="text" name="username" class="input__text header-login__text" placeholder="Username">
|
||||
<input type="password" name="password" class="input__text header-login__text" placeholder="Password">
|
||||
<input type="hidden" name="session" value="{{ session_id() }}">
|
||||
<button class="input__button header-login__button">
|
||||
<i class="fa fa-sign-in"></i> Login
|
||||
</button>
|
||||
</div>
|
||||
<div class="header-login__sub header-login__sub--buttons">
|
||||
<a class="input__button header-login__button header-login__button--small" href="{{ route('auth.register') }}">
|
||||
<i class="fa fa-magic"></i> I don't have an account yet!
|
||||
</a>
|
||||
<a class="input__button header-login__button header-login__button--small" href="{{ route('auth.resetpassword') }}">
|
||||
<i class="fa fa-exclamation-triangle"></i> I don't have access to my account!
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function yuunoAttemptLogin(form) {
|
||||
var ajax = new Sakura.AJAX;
|
||||
|
||||
ajax.SetUrl("{{ route('auth.login') }}");
|
||||
ajax.SetFormData(new FormData(form));
|
||||
|
||||
ajax.AddCallback(200, function () {
|
||||
var result = ajax.JSON();
|
||||
|
||||
if (result.error) {
|
||||
var diag = new Sakura.Dialogue;
|
||||
diag.Title = "Login Error";
|
||||
diag.Text = result.error;
|
||||
diag.Display();
|
||||
} else if (result.go) {
|
||||
window.location.assign(result.go);
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
ajax.Start(Sakura.HTTPMethod.POST);
|
||||
}
|
||||
</script>
|
||||
{% else %}
|
||||
<script>
|
||||
function yuunoLogout() {
|
||||
var confirm = new Sakura.Dialogue;
|
||||
confirm.SetType(Sakura.DialogueType.ConfirmNegative);
|
||||
confirm.Title = "Logout";
|
||||
confirm.Text = "Are you sure?";
|
||||
|
||||
confirm.AddCallback(Sakura.DialogueButton.Yes, function () {
|
||||
var ajax = new Sakura.AJAX;
|
||||
ajax.SetUrl("{{ route('auth.logout') }}?session=" + Sakura.Config.SessionId);
|
||||
|
||||
ajax.AddCallback(200, function () {
|
||||
window.location.reload();
|
||||
});
|
||||
ajax.AddCallback(403, function () {
|
||||
confirm.Close();
|
||||
|
||||
var error = new Sakura.Dialogue;
|
||||
error.Title = "Logout Error";
|
||||
error.Text = "Logout failed.";
|
||||
error.Display();
|
||||
});
|
||||
|
||||
ajax.Start(Sakura.HTTPMethod.DELETE);
|
||||
});
|
||||
|
||||
confirm.Display();
|
||||
}
|
||||
</script>
|
||||
{% endif %}
|
||||
{% include 'auth/header_auth.twig' %}
|
||||
|
||||
<div id="contentwrapper" class="container__content">
|
||||
<div id="notifications" class="alerts"></div>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{% extends 'master.twig' %}
|
||||
{% from 'macros.twig' import profile_image_changer as pic %}
|
||||
|
||||
{% set profileHidden = profile.id == 0 or not profile.activated and (user.id != profile.id and not (user.perms.isMod or user.perms.isAdmin)) %}
|
||||
{% set noUserpage = profile.userPage|length < 1 %}
|
||||
{% set title = profileHidden ? 'User not found!' : 'Profile of ' ~ profile.username %}
|
||||
{% from 'user/profile_macros.twig' import profile_image_changer %}
|
||||
|
||||
{% set title = 'Profile of ' ~ profile.username %}
|
||||
{% set youtubeIsChannelId = profile.youtube|slice(0, 2) == 'UC' and profile.youtube|length == 24 %}
|
||||
{% set possessiveUsername = profile.username ~ "'" ~ (profile.username[:-1] == 's' ? '' : 's') %}
|
||||
|
||||
{% if user.perms.viewUserLinks or user.perms.viewUserDetails %}
|
||||
{% set fields = {
|
||||
|
@ -37,7 +37,7 @@
|
|||
"title": "YouTube",
|
||||
"value": profile.youtube,
|
||||
"link": "https://youtube.com/" ~ (youtubeIsChannelId ? 'channel/' : '') ~ "%s",
|
||||
"disp": youtubeIsChannelId ? profile.username ~ "'s channel" : "%s",
|
||||
"disp": youtubeIsChannelId ? possessiveUsername ~ " channel" : "%s",
|
||||
},
|
||||
"steam": {
|
||||
"title": "Steam",
|
||||
|
@ -70,222 +70,54 @@
|
|||
} %}
|
||||
{% endif %}
|
||||
|
||||
{% set sections = {
|
||||
"userpage": {
|
||||
"icon": "fa-file-text-o",
|
||||
"title": possessiveUsername ~ " user page",
|
||||
"display": profile.userPage|length > 0
|
||||
},
|
||||
"friends": {
|
||||
"icon": "fa-list",
|
||||
"title": possessiveUsername ~ " topics",
|
||||
"display": true
|
||||
},
|
||||
"groups": {
|
||||
"icon": "fa-users",
|
||||
"title": "Groups " ~ profile.username ~ " is part of",
|
||||
"display": false
|
||||
},
|
||||
"topics": {
|
||||
"icon": "fa-reply",
|
||||
"title": possessiveUsername ~ " posts",
|
||||
"display": true
|
||||
},
|
||||
"posts": {
|
||||
"icon": "fa-star",
|
||||
"title": possessiveUsername ~ " friends",
|
||||
"display": true
|
||||
},
|
||||
"comments": {
|
||||
"icon": "fa-comments-o",
|
||||
"title": possessiveUsername ~ " profile comments",
|
||||
"display": true
|
||||
},
|
||||
} %}
|
||||
|
||||
{% block js %}
|
||||
{% if not profileHidden %}
|
||||
<script type="text/javascript">
|
||||
window.addEventListener('load', function () {
|
||||
{% if profile.lastfm %}
|
||||
var np = new Sakura.AJAX();
|
||||
np.SetUrl("{{ route('user.nowplaying', profile.id) }}");
|
||||
np.AddCallback(200, function () {
|
||||
var data = np.JSON(),
|
||||
artist = Sakura.DOM.ID('np-artist'),
|
||||
track = Sakura.DOM.ID('np-track'),
|
||||
state = Sakura.DOM.ID('np-state'),
|
||||
by = Sakura.DOM.ID('np-by');
|
||||
|
||||
artist.href = data.artist_url;
|
||||
artist.textContent = data.artist;
|
||||
track.href = data.track_url;
|
||||
track.textContent = data.track;
|
||||
state.className = 'fa ' + (data.listening ? 'fa-play-circle' : 'fa-history');
|
||||
by.className = data.track === '' || data.artist === '' ? 'hidden' : '';
|
||||
});
|
||||
setInterval(function () { np.Start(Sakura.HTTPMethod.GET); }, 20000);
|
||||
np.Start(Sakura.HTTPMethod.GET);
|
||||
{% endif %}
|
||||
|
||||
// Check if location.hash is set
|
||||
if (location.hash) {
|
||||
var open = location.hash.slice(2);
|
||||
|
||||
// Check if the element exists
|
||||
if (document.getElementById('profile-mode-' + open)) {
|
||||
profileMode(open);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var profileUserpage = document.getElementById('profile-mode-userpage');
|
||||
|
||||
// Check if the user page has contents
|
||||
if (profileUserpage.children[0].innerHTML.trim().length) {
|
||||
profileMode('userpage');
|
||||
} else {
|
||||
profileMode('comments');
|
||||
}
|
||||
});
|
||||
|
||||
// Switch to a different mode
|
||||
function profileMode(id) {
|
||||
// Get other active modes and fetch the new element
|
||||
var current = document.getElementsByClassName('profile-mode-current'),
|
||||
newMode = document.getElementById('profile-mode-' + id);
|
||||
|
||||
// Check if the new mode exists
|
||||
if (typeof newMode == 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if there's any active
|
||||
if (current) {
|
||||
// Hide them all
|
||||
for (i in current) {
|
||||
current[i].className = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
// Set the new to active
|
||||
newMode.className = 'profile-mode-current';
|
||||
}
|
||||
|
||||
function handleImageChange(file, url, query) {
|
||||
var ajax = new Sakura.AJAX,
|
||||
formData = new FormData;
|
||||
|
||||
formData.append('session', Sakura.Config.SessionId);
|
||||
formData.append('file', file, file.name);
|
||||
|
||||
ajax.SetFormData(formData);
|
||||
ajax.SetUrl(url);
|
||||
|
||||
ajax.AddCallback(200, function () {
|
||||
var result = ajax.JSON();
|
||||
|
||||
if (result.error) {
|
||||
var diag = new Sakura.Dialogue;
|
||||
diag.Title = "Error";
|
||||
diag.Text = result.error;
|
||||
diag.Display();
|
||||
} else {
|
||||
refreshImage(url, query);
|
||||
}
|
||||
});
|
||||
|
||||
ajax.Start(Sakura.HTTPMethod.POST);
|
||||
}
|
||||
|
||||
function handleImageDelete(url, query) {
|
||||
var confirm = new Sakura.Dialogue;
|
||||
|
||||
confirm.SetType(Sakura.DialogueType.ConfirmNegative);
|
||||
confirm.Title = "Deleting image";
|
||||
confirm.Text = "Are you sure?";
|
||||
|
||||
confirm.AddCallback(Sakura.DialogueButton.Yes, function () {
|
||||
var client = new Sakura.AJAX;
|
||||
client.SetUrl(url + "?session=" + Sakura.Config.SessionId);
|
||||
client.AddCallback(200, function () {
|
||||
refreshImage(url, query);
|
||||
});
|
||||
client.Start(Sakura.HTTPMethod.DELETE);
|
||||
this.Close();
|
||||
});
|
||||
|
||||
confirm.Display();
|
||||
}
|
||||
|
||||
function refreshImage(url, query) {
|
||||
var elements = document.querySelectorAll(query);
|
||||
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var element = elements[i],
|
||||
url = url + "?" + Date.now();
|
||||
|
||||
if (element.tagName === "IMG") {
|
||||
element.src = url;
|
||||
} else {
|
||||
element.style.backgroundImage = "url('" + url + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endif %}
|
||||
{% include 'user/profile_javascript.twig' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if profileHidden %}
|
||||
<div class="content content--alt">
|
||||
<div style="padding: 20px;">
|
||||
<h1>The requested user does not exist!</h1>
|
||||
There are a few possible reasons for this:
|
||||
<ul style="padding-left: 40px; list-style: square">
|
||||
<li>They changed their username.</li>
|
||||
<li>They may have been restricted.</li>
|
||||
<li>You made a typo.</li>
|
||||
<li>They never existed.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if (user.id == profile.id and not user.restricted and user.activated and user.perms.changeBackground) or user.perms.manageProfileImages %}
|
||||
{{ pic(route('user.background', profile.id), '.container') }}
|
||||
{% endif %}
|
||||
<div class="content profile">
|
||||
<div class="profile__container">
|
||||
<div class="profile__header" style="background-image: url({{ route('user.header', profile.id) }});">
|
||||
<div class="profile__info">
|
||||
{% if (user.id == profile.id and not user.restricted and user.activated and user.perms.changeHeader) or user.perms.manageProfileImages %}
|
||||
{{ pic(route('user.header', profile.id), '.profile__header') }}
|
||||
{% endif %}
|
||||
<div class="avatar avatar--border profile__avatar" style="background-image: url({{ route('user.avatar', profile.id) }}); box-shadow: 0 0 5px #{% if profile.isOnline %}484{% else %}844{% endif %};">
|
||||
{% if (user.id == profile.id and not user.restricted and user.activated and user.perms.changeAvatar) or user.perms.manageProfileImages %}
|
||||
{{ pic(route('user.avatar', profile.id), '.avatar') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="profile__username">
|
||||
<h1 style="color: {{ profile.colour }}; text-shadow: 0 0 7px {% if profile.colour != 'inherit' %}{{ profile.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;" {% if profile.getUsernameHistory %} title="Known as {{ profile.getUsernameHistory[0].username_old }} before {{ profile.getUsernameHistory[0].change_time|date(config('general.date_format')) }}." {% endif %}>{{ profile.username }}</h1>
|
||||
{% if profile.isPremium %}<img src="/images/tenshi.png" alt="Tenshi" style="vertical-align: middle;"> {% endif %}<img src="/images/flags/{{ profile.country|lower }}.png" alt="{{ profile.country }}" style="vertical-align: middle;" title="{{ profile.country(true) }}"> <span style="font-size: .8em;">{{ profile.title }}</span>
|
||||
</div>
|
||||
<div class="profile__dates">
|
||||
{% spaceless %}
|
||||
<div class="profile__date">
|
||||
<b>Joined</b> <time class="time-ago" datetime="{{ profile.registered|date('r') }}">{{ profile.registered|date(config('general.date_format')) }}</time>
|
||||
</div>
|
||||
<div class="profile__date">
|
||||
{% if profile.lastOnline < 1 %}
|
||||
<b>{{ profile.username }} hasn't logged in yet.</b>
|
||||
{% else %}
|
||||
<b>Last online</b> <time class="time-ago" datetime="{{ profile.lastOnline|date('r') }}">{{ profile.lastOnline|date(config('general.date_format')) }}</time>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if profile.birthday != '0000-00-00' and profile.birthday|split('-')[0] > 0 %}
|
||||
<div class="profile__date">
|
||||
<b>Age</b> <span title="{{ profile.birthday }}">{{ profile.birthday(true) }} years old</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile__interactions">
|
||||
<div class="profile__navigation">
|
||||
{% if not noUserpage %}
|
||||
<a class="profile__interactions-item fa fa-file-text-o" title="View {{ profile.username }}'s user page" href="#_userpage" onclick="profileMode('userpage');"></a>
|
||||
{% endif %}
|
||||
<a class="profile__interactions-item fa fa-list" title="View {{ profile.username }}'s topics" href="#_topics" onclick="profileMode('topics');"></a>
|
||||
<a class="profile__interactions-item fa fa-reply" title="View {{ profile.username }}'s posts" href="#_posts" onclick="profileMode('posts');"></a>
|
||||
<a class="profile__interactions-item fa fa-star" title="View {{ profile.username }}'s friends" href="#_friends" onclick="profileMode('friends');"></a>
|
||||
<a class="profile__interactions-item fa fa-comments-o" title="View {{ profile.username }}'s profile comments" href="#_comments" onclick="profileMode('comments');"></a>
|
||||
</div>
|
||||
{% if user.isActive %}
|
||||
<div class="profile__actions">
|
||||
{% if user.id == profile.id %}
|
||||
<a class="profile__interactions-item fa fa-pencil-square-o" title="Edit your profile" href="{{ route('settings.account.profile') }}"></a>
|
||||
{% else %}
|
||||
{% if user.isFriends(profile.id) != 0 %}<a class="profile__interactions-item fa fa-{% if user.isFriends(profile.id) == 2 %}heart{% else %}star{% endif %}" title="You are friends"></a>{% endif %}
|
||||
<a class="profile__interactions-item fa fa-user-{% if user.isFriends(profile.id) == 0 %}plus{% else %}times{% endif %}" title="{% if user.isFriends(profile.id) == 0 %}Add {{ profile.username }} as a friend{% else %}Remove friend{% endif %}" href="javascript:void(0);" onclick="Sakura.Friend.{% if user.isFriends(profile.id) == 0 %}Add({{ profile.id }}){% else %}Remove({{ profile.id }}){% endif %}"></a>
|
||||
<a class="profile__interactions-item fa fa-exclamation-circle" title="Report {{ profile.username }}" href="{{ route('user.report', profile.id) }}"></a>
|
||||
{% endif %}
|
||||
{% if user.perms.canRestrict %}
|
||||
<a class="profile__interactions-item fa fa-trash" title="Restrict {{ profile.username }}" href="?restrict={{ session_id() }}"></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="profile__content">
|
||||
<div class="profile__mode">
|
||||
{% if profile.lastfm %}
|
||||
{% if (user.id == profile.id and not user.restricted and user.activated and user.perms.changeBackground) or user.perms.manageProfileImages %}
|
||||
{{ profile_image_changer(route('user.background', profile.id), '.container') }}
|
||||
{% endif %}
|
||||
|
||||
<div class="content profile">
|
||||
<div class="profile__container">
|
||||
{% include 'user/profile_header.twig' %}
|
||||
<div class="profile__content">
|
||||
<div class="profile__mode">
|
||||
{% if profile.lastfm %}
|
||||
<div class="profile__now-playing">
|
||||
<div class="np-icon">
|
||||
<span class="fa fa-music"></span>
|
||||
|
@ -297,80 +129,66 @@
|
|||
<a href="#" id="np-artist" class="profile__now-playing-link"></a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<noscript><h1 style="text-align: center;">Please enable Javascript!</h1></noscript>
|
||||
<div id="profile-mode-userpage" class="hidden">
|
||||
{% include 'profile/userpage.twig' %}
|
||||
{% endif %}
|
||||
<noscript><h1 style="text-align: center;">Please enable Javascript!</h1></noscript>
|
||||
{% for name, data in sections %}
|
||||
<div id="profile-mode-{{ name }}" class="hidden">
|
||||
{% include 'profile/' ~ name ~ '.twig' %}
|
||||
</div>
|
||||
<div id="profile-mode-friends" class="hidden">
|
||||
{% include 'profile/friends.twig' %}
|
||||
</div>
|
||||
<div id="profile-mode-groups" class="hidden">
|
||||
{% include 'profile/groups.twig' %}
|
||||
</div>
|
||||
<div id="profile-mode-topics" class="hidden">
|
||||
{% include 'profile/topics.twig' %}
|
||||
</div>
|
||||
<div id="profile-mode-posts" class="hidden">
|
||||
{% include 'profile/posts.twig' %}
|
||||
</div>
|
||||
<div id="profile-mode-comments" class="hidden">
|
||||
{% include 'profile/comments.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile__data">
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="profile__data">
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td style="text-align: left; font-weight: bold;">Topics</td>
|
||||
<td style="text-align: right;">{{ profile.forumStats.topics }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left; font-weight: bold;">Posts</td>
|
||||
<td style="text-align: right;">{{ profile.forumStats.posts }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left; font-weight: bold;">Friends</td>
|
||||
<td style="text-align: right;">{{ profile.friends(2)|length }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
{% if user.perms.viewUserLinks or user.perms.viewUserDetails %}
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td style="text-align: left; font-weight: bold;">Topics</td>
|
||||
<td style="text-align: right;">{{ profile.forumStats.topics }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left; font-weight: bold;">Posts</td>
|
||||
<td style="text-align: right;">{{ profile.forumStats.posts }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left; font-weight: bold;">Friends</td>
|
||||
<td style="text-align: right;">{{ profile.friends(2)|length }}</td>
|
||||
</tr>
|
||||
{% for id, data in fields %}
|
||||
{% if data.value != null %}
|
||||
<tr>
|
||||
<td style="text-align: left; font-weight: bold;">
|
||||
{{ data.title }}
|
||||
</td>
|
||||
<td style="text-align: right;">
|
||||
{% if data.link is defined %}
|
||||
<a href="{{ data.link|format(data.value) }}">{{ (data.disp is defined ? data.disp : '%s')|format(data.value) }}</a>
|
||||
{% else %}
|
||||
{{ (data.disp is defined ? data.disp : '%s')|format(data.value) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
<hr>
|
||||
{% if user.perms.viewUserLinks or user.perms.viewUserDetails %}
|
||||
<table style="width: 100%;">
|
||||
{% for id, data in fields %}
|
||||
{% if data.value != null %}
|
||||
<tr>
|
||||
<td style="text-align: left; font-weight: bold;">
|
||||
{{ data.title }}
|
||||
</td>
|
||||
<td style="text-align: right;">
|
||||
{% if data.link is defined %}
|
||||
<a href="{{ data.link|format(data.value) }}">{{ (data.disp is defined ? data.disp : '%s')|format(data.value) }}</a>
|
||||
{% else %}
|
||||
{{ (data.disp is defined ? data.disp : '%s')|format(data.value) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% elseif user.id == 0 %}
|
||||
<div><b>Log in to view the full profile!</b></div>
|
||||
{% else %}
|
||||
<div><b>You aren't allowed to view profile details!</b></div>
|
||||
{% endif %}
|
||||
<b>Account Standing</b>
|
||||
{% if not profile.activated %}
|
||||
<h2 style="color: #888; text-shadow: 0 0 7px #888; margin-top: 0;">Deactivated</h2>
|
||||
{% elseif profile.restricted %}
|
||||
<h2 style="color: #222; text-shadow: 0 0 7px #800; margin-top: 0;">Restricted</h2>
|
||||
{% elseif false %}
|
||||
<h2 style="color: #A00; text-shadow: 0 0 7px #A00; margin-top: 0;">Bad</h2>
|
||||
{% else %}
|
||||
<h2 style="color: #080; text-shadow: 0 0 7px #080; margin-top: 0;">Good</h2>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% elseif user.id == 0 %}
|
||||
<div><b>Log in to view the full profile!</b></div>
|
||||
{% else %}
|
||||
<div><b>You aren't allowed to view profile details!</b></div>
|
||||
{% endif %}
|
||||
<b>Account Standing</b>
|
||||
{% if not profile.activated %}
|
||||
<h2 style="color: #888; text-shadow: 0 0 7px #888; margin-top: 0;">Inactive</h2>
|
||||
{% elseif profile.restricted %}
|
||||
<h2 style="color: #222; text-shadow: 0 0 7px #800; margin-top: 0;">Restricted</h2>
|
||||
{% elseif false %}
|
||||
<h2 style="color: #A00; text-shadow: 0 0 7px #A00; margin-top: 0;">Bad</h2>
|
||||
{% else %}
|
||||
<h2 style="color: #080; text-shadow: 0 0 7px #080; margin-top: 0;">Good</h2>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
60
resources/views/yuuno/user/profile_header.twig
Normal file
60
resources/views/yuuno/user/profile_header.twig
Normal file
|
@ -0,0 +1,60 @@
|
|||
{% from 'user/profile_macros.twig' import profile_image_changer %}
|
||||
|
||||
<div class="profile__header" style="background-image: url({{ route('user.header', profile.id) }});">
|
||||
<div class="profile__info">
|
||||
{% if (user.id == profile.id and not user.restricted and user.activated and user.perms.changeHeader) or user.perms.manageProfileImages %}
|
||||
{{ profile_image_changer(route('user.header', profile.id), '.profile__header') }}
|
||||
{% endif %}
|
||||
<div class="avatar avatar--border profile__avatar" style="background-image: url({{ route('user.avatar', profile.id) }}); box-shadow: 0 0 5px #{% if profile.isOnline %}484{% else %}844{% endif %};">
|
||||
{% if (user.id == profile.id and not user.restricted and user.activated and user.perms.changeAvatar) or user.perms.manageProfileImages %}
|
||||
{{ profile_image_changer(route('user.avatar', profile.id), '.avatar') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="profile__username">
|
||||
<h1 style="color: {{ profile.colour }}; text-shadow: 0 0 7px {% if profile.colour != 'inherit' %}{{ profile.colour }}{% else %}#222{% endif %}; padding: 0 0 2px;" {% if profile.getUsernameHistory %} title="Known as {{ profile.getUsernameHistory[0].username_old }} before {{ profile.getUsernameHistory[0].change_time|date(config('general.date_format')) }}." {% endif %}>{{ profile.username }}</h1>
|
||||
{% if profile.isPremium %}<img src="/images/tenshi.png" alt="Tenshi" style="vertical-align: middle;"> {% endif %}<img src="/images/flags/{{ profile.country|lower }}.png" alt="{{ profile.country }}" style="vertical-align: middle;" title="{{ profile.country(true) }}"> <span style="font-size: .8em;">{{ profile.title }}</span>
|
||||
</div>
|
||||
<div class="profile__dates">
|
||||
{% spaceless %}
|
||||
<div class="profile__date">
|
||||
<b>Joined</b> <time class="time-ago" datetime="{{ profile.registered|date('r') }}">{{ profile.registered|date(config('general.date_format')) }}</time>
|
||||
</div>
|
||||
<div class="profile__date">
|
||||
{% if profile.lastOnline < 1 %}
|
||||
<b>{{ profile.username }} hasn't logged in yet.</b>
|
||||
{% else %}
|
||||
<b>Last online</b> <time class="time-ago" datetime="{{ profile.lastOnline|date('r') }}">{{ profile.lastOnline|date(config('general.date_format')) }}</time>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if profile.birthday != '0000-00-00' and profile.birthday|split('-')[0] > 0 %}
|
||||
<div class="profile__date">
|
||||
<b>Age</b> <span title="{{ profile.birthday }}">{{ profile.birthday(true) }} years old</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endspaceless %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile__interactions">
|
||||
<div class="profile__navigation">
|
||||
{% for name, data in sections %}
|
||||
{% if data.display %}
|
||||
<a class="profile__interactions-item fa {{ data.icon }}" title="{{ data.title }}" href="#{{ name }}"></a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if user.isActive %}
|
||||
<div class="profile__actions">
|
||||
{% if user.id == profile.id %}
|
||||
<a class="profile__interactions-item fa fa-pencil-square-o" title="Edit your profile" href="{{ route('settings.account.profile') }}"></a>
|
||||
{% else %}
|
||||
{% if user.isFriends(profile.id) != 0 %}<a class="profile__interactions-item fa fa-{% if user.isFriends(profile.id) == 2 %}heart{% else %}star{% endif %}" title="You are friends"></a>{% endif %}
|
||||
<a class="profile__interactions-item fa fa-user-{% if user.isFriends(profile.id) == 0 %}plus{% else %}times{% endif %}" title="{% if user.isFriends(profile.id) == 0 %}Add {{ profile.username }} as a friend{% else %}Remove friend{% endif %}" href="javascript:void(0);" onclick="Sakura.Friend.{% if user.isFriends(profile.id) == 0 %}Add({{ profile.id }}){% else %}Remove({{ profile.id }}){% endif %}"></a>
|
||||
<a class="profile__interactions-item fa fa-exclamation-circle" title="Report {{ profile.username }}" href="{{ route('user.report', profile.id) }}"></a>
|
||||
{% endif %}
|
||||
{% if user.perms.canRestrict %}
|
||||
<a class="profile__interactions-item fa fa-trash" title="Restrict {{ profile.username }}" href="?restrict={{ session_id() }}"></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
127
resources/views/yuuno/user/profile_javascript.twig
Normal file
127
resources/views/yuuno/user/profile_javascript.twig
Normal file
|
@ -0,0 +1,127 @@
|
|||
<script type="text/javascript">
|
||||
window.addEventListener('load', function () {
|
||||
{% if profile.lastfm %}
|
||||
var np = new Sakura.AJAX();
|
||||
np.SetUrl("{{ route('user.nowplaying', profile.id) }}");
|
||||
np.AddCallback(200, function () {
|
||||
var data = np.JSON(),
|
||||
artist = Sakura.DOM.ID('np-artist'),
|
||||
track = Sakura.DOM.ID('np-track'),
|
||||
state = Sakura.DOM.ID('np-state'),
|
||||
by = Sakura.DOM.ID('np-by');
|
||||
|
||||
artist.href = data.artist_url;
|
||||
artist.textContent = data.artist;
|
||||
track.href = data.track_url;
|
||||
track.textContent = data.track;
|
||||
state.className = 'fa ' + (data.listening ? 'fa-play-circle' : 'fa-history');
|
||||
by.className = data.track === '' || data.artist === '' ? 'hidden' : '';
|
||||
});
|
||||
setInterval(function () { np.Start(Sakura.HTTPMethod.GET); }, 20000);
|
||||
np.Start(Sakura.HTTPMethod.GET);
|
||||
{% endif %}
|
||||
|
||||
// Check if location.hash is set
|
||||
if (location.hash) {
|
||||
var open = location.hash.slice(1);
|
||||
|
||||
// Check if the element exists
|
||||
if (document.getElementById('profile-mode-' + open)) {
|
||||
profileMode(open);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var profileUserpage = document.getElementById('profile-mode-userpage');
|
||||
|
||||
location.hash = profileUserpage.children[0].innerHTML.trim().length ? 'userpage' : 'comments';
|
||||
});
|
||||
|
||||
window.addEventListener("hashchange", function () {
|
||||
profileMode(location.hash.slice(1));
|
||||
});
|
||||
|
||||
// Switch to a different mode
|
||||
function profileMode(id) {
|
||||
// Get other active modes and fetch the new element
|
||||
var current = document.getElementsByClassName('profile-mode-current'),
|
||||
newMode = document.getElementById('profile-mode-' + id);
|
||||
|
||||
// Check if the new mode exists
|
||||
if (!newMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if there's any active
|
||||
if (current) {
|
||||
// Hide them all
|
||||
for (i in current) {
|
||||
current[i].className = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
// Set the new to active
|
||||
newMode.className = 'profile-mode-current';
|
||||
}
|
||||
|
||||
function handleImageChange(file, url, query) {
|
||||
var ajax = new Sakura.AJAX,
|
||||
formData = new FormData;
|
||||
|
||||
formData.append('session', Sakura.Config.SessionId);
|
||||
formData.append('file', file, file.name);
|
||||
|
||||
ajax.SetFormData(formData);
|
||||
ajax.SetUrl(url);
|
||||
|
||||
ajax.AddCallback(200, function () {
|
||||
var result = ajax.JSON();
|
||||
|
||||
if (result.error) {
|
||||
var diag = new Sakura.Dialogue;
|
||||
diag.Title = "Error";
|
||||
diag.Text = result.error;
|
||||
diag.Display();
|
||||
} else {
|
||||
refreshImage(url, query);
|
||||
}
|
||||
});
|
||||
|
||||
ajax.Start(Sakura.HTTPMethod.POST);
|
||||
}
|
||||
|
||||
function handleImageDelete(url, query) {
|
||||
var confirm = new Sakura.Dialogue;
|
||||
|
||||
confirm.SetType(Sakura.DialogueType.ConfirmNegative);
|
||||
confirm.Title = "Deleting image";
|
||||
confirm.Text = "Are you sure?";
|
||||
|
||||
confirm.AddCallback(Sakura.DialogueButton.Yes, function () {
|
||||
var client = new Sakura.AJAX;
|
||||
client.SetUrl(url + "?session=" + Sakura.Config.SessionId);
|
||||
client.AddCallback(200, function () {
|
||||
refreshImage(url, query);
|
||||
});
|
||||
client.Start(Sakura.HTTPMethod.DELETE);
|
||||
this.Close();
|
||||
});
|
||||
|
||||
confirm.Display();
|
||||
}
|
||||
|
||||
function refreshImage(url, query) {
|
||||
var elements = document.querySelectorAll(query);
|
||||
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var element = elements[i],
|
||||
url = url + "?" + Date.now();
|
||||
|
||||
if (element.tagName === "IMG") {
|
||||
element.src = url;
|
||||
} else {
|
||||
element.style.backgroundImage = "url('" + url + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
8
resources/views/yuuno/user/profile_macros.twig
Normal file
8
resources/views/yuuno/user/profile_macros.twig
Normal file
|
@ -0,0 +1,8 @@
|
|||
{% macro profile_image_changer(url, query) %}
|
||||
<div class="uploader">
|
||||
<label class="uploader__button uploader__button--change fa fa-edit">
|
||||
<input type="file" class="uploader__change" onchange="handleImageChange(this.files[0], '{{ url }}', '{{ query }}')">
|
||||
</label>
|
||||
<button class="uploader__button uploader__button--delete fa fa-trash" onclick="handleImageDelete('{{ url }}', '{{ query }}')"></button>
|
||||
</div>
|
||||
{% endmacro %}
|
Reference in a new issue