flashwave
383e2ed0e0
This one took multiple days and it pretty invasive into the core of Misuzu so issue might (will) arise, there's also some features that have gone temporarily missing in the mean time and some inefficiencies introduced that will be fixed again at a later time. The old class isn't gone entirely because I still have to figure out what I'm gonna do about validation, but for the most part this knocks out one of the "layers of backwards compatibility", as I've been referring to it, and is moving us closer to a future where Flashii actually gets real updates. If you run into anything that's broken and you're inhibited from reporting it through the forum, do it through chat or mail me at flashii-issues@flash.moe.
400 lines
26 KiB
Twig
400 lines
26 KiB
Twig
{% extends 'profile/master.twig' %}
|
|
{% from 'macros.twig' import container_title %}
|
|
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text, input_checkbox, input_file, input_file_raw, input_select %}
|
|
|
|
{% if profile_user is defined %}
|
|
{% set canonical_url = url('user-profile', {'user': profile_user.id}) %}
|
|
{% set title = profile_user.name %}
|
|
{% else %}
|
|
{% set title = 'User not found!' %}
|
|
{% endif %}
|
|
|
|
{% block content %}
|
|
{% if profile_is_editing %}
|
|
<form class="profile" method="post" action="{{ url('user-profile', {'user': profile_user.id}) }}" enctype="multipart/form-data">
|
|
{{ input_csrf('profile') }}
|
|
|
|
{% if perms.edit_avatar %}
|
|
{{ input_file_raw('avatar[file]', 'profile__hidden', ['image/png', 'image/jpeg', 'image/gif'], {'id':'avatar-selection'}) }}
|
|
|
|
<script>
|
|
function updateAvatarPreview(name, url, preview) {
|
|
url = url || "{{ url('user-avatar', {'user': profile_user.id, 'res': 240})|raw }}";
|
|
preview = preview || document.getElementById('avatar-preview');
|
|
preview.src = url;
|
|
preview.title = name;
|
|
}
|
|
|
|
document.getElementById('avatar-selection').addEventListener('change', function (ev) {
|
|
updateAvatarPreview(ev.target.files[0].name, URL.createObjectURL(ev.target.files[0]));
|
|
});
|
|
</script>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="profile">
|
|
{% endif %}
|
|
|
|
{% include 'profile/_layout/header.twig' %}
|
|
|
|
{% if profile_is_editing %}
|
|
<div class="container profile__container profile__guidelines">
|
|
<ul class="profile__guidelines__section">
|
|
<li class="profile__guidelines__line profile__guidelines__line--header">General</li>
|
|
<li class="profile__guidelines__line">Keep things sane and generally suitable for all ages.</li>
|
|
<li class="profile__guidelines__line">Make sure to adhere to the <a href="{{ url('info', {'title': 'rules'}) }}" class="profile__guidelines__link">rules</a>.</li>
|
|
</ul>
|
|
|
|
{% if perms.edit_avatar %}
|
|
<ul class="profile__guidelines__section">
|
|
<li class="profile__guidelines__line profile__guidelines__line--header">Avatar</li>
|
|
<li class="profile__guidelines__line">May not exceed the <span class="profile__guidelines__emphasis">{{ byte_symbol(profile_avatar_info.maxBytes) }}</span> file size limit.</li>
|
|
<li class="profile__guidelines__line">May not be larger than <span class="profile__guidelines__emphasis">{{ profile_avatar_info.maxWidth }}x{{ profile_avatar_info.maxHeight }}</span>.</li>
|
|
<li class="profile__guidelines__line">Will be centre cropped and scaled to at most <span class="profile__guidelines__emphasis">240x240</span>.</li>
|
|
<li class="profile__guidelines__line">Animated GIF images are allowed.</li>
|
|
</ul>
|
|
{% endif %}
|
|
|
|
{% if perms.edit_background %}
|
|
<ul class="profile__guidelines__section">
|
|
<li class="profile__guidelines__line profile__guidelines__line--header">Background</li>
|
|
<li class="profile__guidelines__line">May not exceed the <span class="profile__guidelines__emphasis">{{ byte_symbol(profile_background_info.maxBytes) }}</span> file size limit.</li>
|
|
<li class="profile__guidelines__line">May not be larger than <span class="profile__guidelines__emphasis">{{ profile_background_info.maxWidth }}x{{ profile_background_info.maxHeight }}</span>.</li>
|
|
<li class="profile__guidelines__line">GIF images, in general, are only allowed when tiling.</li>
|
|
</ul>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if profile_notices|length > 0 %}
|
|
<div class="warning">
|
|
<div class="warning__content">
|
|
{% for notice in profile_notices %}
|
|
<p>{{ notice }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="profile__content">
|
|
{% set show_profile_fields = not profile_is_guest and (profile_is_editing ? perms.edit_profile : profile_fields_display_values|default([]) is not empty) %}
|
|
{% set show_background_settings = profile_is_editing and perms.edit_background %}
|
|
{% set show_birthdate = profile_is_editing and perms.edit_birthdate %}
|
|
{% set show_active_forum_info = not profile_is_editing and (profile_active_category_info.forum_id|default(0) > 0 or profile_active_topic_info.topic_id|default(0) > 0) %}
|
|
{% set show_warnings = profile_warnings is defined and profile_warnings|length > 0 %}
|
|
{% set show_sidebar = (not profile_is_banned or profile_can_edit) and (profile_is_guest or show_profile_fields or show_background_settings or show_birthdate or show_active_forum_info or show_warnings) %}
|
|
|
|
{% if show_sidebar %}
|
|
<div class="profile__content__side">
|
|
{% if show_background_settings %}
|
|
<div class="container profile__container profile__background-settings">
|
|
{{ container_title('Background') }}
|
|
|
|
<div class="profile__background-settings__content">
|
|
{{ input_file('background[file]', '', ['image/png', 'image/jpeg', 'image/gif'], {'id':'background-selection'}) }}
|
|
|
|
{{ input_checkbox('background[attach]', 'None', true, '', 0, true, {'onchange':'profileChangeBackgroundAttach(this.value)'}) }}
|
|
{% for key, value in background_attachments %}
|
|
{{ input_checkbox('background[attach]', value, key == profile_background_info.attachment, '', key, true, {'onchange':'profileChangeBackgroundAttach(this.value)'}) }}
|
|
{% endfor %}
|
|
|
|
{{ input_checkbox('background[attr][blend]', 'Blend', profile_background_info.blend, '', '', false, {'onchange':'profileToggleBackgroundAttr(\'blend\', this.checked)'}) }}
|
|
{{ input_checkbox('background[attr][slide]', 'Slide', profile_background_info.slide, '', '', false, {'onchange':'profileToggleBackgroundAttr(\'slide\', this.checked)'}) }}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if profile_is_guest and not profile_is_deleted %}
|
|
<div class="container profile__container">
|
|
<div class="profile__accounts__notice">
|
|
You must <a href="{{ url('auth-login') }}" class="profile__accounts__link">log in</a> to view full profiles!
|
|
</div>
|
|
</div>
|
|
{% elseif show_profile_fields %}
|
|
<div class="container profile__container profile__accounts">
|
|
{{ container_title('Elsewhere') }}
|
|
|
|
<div class="profile__accounts__content">
|
|
{% for fieldInfo in profile_fields_infos %}
|
|
{% if profile_is_editing or profile_fields_display_values[fieldInfo.name] is defined %}
|
|
<label class="profile__accounts__item">
|
|
<div class="profile__accounts__title">
|
|
{{ fieldInfo.title }}
|
|
</div>
|
|
|
|
{% if profile_is_editing %}
|
|
{{ input_text('profile[' ~ fieldInfo.name ~ ']', 'profile__accounts__input', profile_fields_raw_values[fieldInfo.name]|default('')) }}
|
|
{% else %}
|
|
<div class="profile__accounts__value">
|
|
{% if profile_fields_link_values[fieldInfo.name] is defined %}
|
|
<a href="{{ profile_fields_link_values[fieldInfo.name] }}" class="profile__accounts__link" target="_blank" rel="noreferrer noopener">{{ profile_fields_display_values[fieldInfo.name] }}</a>
|
|
{% else %}
|
|
{{ profile_fields_display_values[fieldInfo.name] }}
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</label>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if show_active_forum_info %}
|
|
<div class="container profile__container profile__forum-activity">
|
|
{{ container_title('Forum Activity') }}
|
|
|
|
<div class="profile__forum-activity__content">
|
|
{% if profile_active_category_info is not empty %}
|
|
<div class="profile__forum-activity__category">
|
|
{% set forum = profile_active_category_info %}
|
|
{% if forum.forum_icon is defined and forum.forum_icon is not empty %}
|
|
{% set forum_icon = forum.forum_icon %}
|
|
{% elseif forum.forum_archived is defined and forum.forum_archived %}
|
|
{% set forum_icon = 'fas fa-archive fa-fw' %}
|
|
{% elseif forum.forum_type is defined and forum.forum_type != constant('MSZ_FORUM_TYPE_DISCUSSION') %}
|
|
{% if forum.forum_type == constant('MSZ_FORUM_TYPE_LINK') %}
|
|
{% set forum_icon = 'fas fa-link fa-fw' %}
|
|
{% elseif forum.forum_type == constant('MSZ_FORUM_TYPE_CATEGORY') %}
|
|
{% set forum_icon = 'fas fa-folder fa-fw' %}
|
|
{% endif %}
|
|
{% else %}
|
|
{% set forum_icon = 'fas fa-comments fa-fw' %}
|
|
{% endif %}
|
|
|
|
<div class="profile__forum-activity__leader">
|
|
Most active category
|
|
</div>
|
|
|
|
<div class="forum__category">
|
|
<a href="{{ url('forum-category', {'forum': forum.forum_id}) }}" class="forum__category__link"></a>
|
|
|
|
<div class="forum__category__container">
|
|
<div class="forum__category__icon">
|
|
<span class="{{ forum_icon }}"></span>
|
|
</div>
|
|
|
|
<div class="forum__category__details">
|
|
<div class="forum__category__title">
|
|
{{ forum.forum_name }}
|
|
</div>
|
|
|
|
<div class="forum__category__description">
|
|
{{ profile_active_category_stats.post_count|number_format }} post{{ profile_active_category_stats.post_count == 1 ? '' : 's' }}
|
|
/ {{ ((profile_active_category_stats.post_count / profile_stats.forum_post_count) * 100)|number_format(2) }}% of total posts
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if profile_active_topic_info is not empty %}
|
|
<div class="profile__forum-activity__topic">
|
|
{% set topic = profile_active_topic_info %}
|
|
{% if topic.topic_deleted is defined and topic.topic_deleted is not null %}
|
|
{% set topic_icon = 'fas fa-trash-alt' %}
|
|
{% elseif topic.topic_type is defined and topic.topic_type != constant('MSZ_TOPIC_TYPE_DISCUSSION') %}
|
|
{% if topic.topic_type == constant('MSZ_TOPIC_TYPE_ANNOUNCEMENT') or topic.topic_type == constant('MSZ_TOPIC_TYPE_GLOBAL_ANNOUNCEMENT') %}
|
|
{% set topic_icon = 'fas fa-bullhorn' %}
|
|
{% elseif topic.topic_type == constant('MSZ_TOPIC_TYPE_STICKY') %}
|
|
{% set topic_icon = 'fas fa-thumbtack' %}
|
|
{% endif %}
|
|
{% elseif topic.topic_locked is defined and topic.topic_locked is not null %}
|
|
{% set topic_icon = 'fas fa-lock' %}
|
|
{% else %}
|
|
{% set topic_icon = 'fas fa-comment' %}
|
|
{% endif %}
|
|
|
|
<div class="profile__forum-activity__leader">
|
|
Most active topic
|
|
</div>
|
|
|
|
<div class="forum__topic{% if topic.topic_locked is not null %} forum__topic--locked{% endif %}">
|
|
<a href="{{ url('forum-topic', {'topic': topic.topic_id}) }}" class="forum__topic__link"></a>
|
|
|
|
<div class="forum__topic__container">
|
|
<div class="forum__topic__icon">
|
|
<i class="{{ topic_icon }} fa-fw"></i>
|
|
</div>
|
|
|
|
<div class="forum__topic__details">
|
|
<div class="forum__topic__title">
|
|
<span class="forum__topic__title__inner">
|
|
{{ topic.topic_title }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="forum__topic__info">
|
|
{{ profile_active_topic_stats.post_count|number_format }} post{{ profile_active_topic_stats.post_count == 1 ? '' : 's' }}
|
|
/ {{ ((profile_active_topic_stats.post_count / profile_stats.forum_post_count) * 100)|number_format(2) }}% of total posts
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if show_birthdate %}
|
|
<div class="container profile__container profile__birthdate">
|
|
{{ container_title('Birthdate') }}
|
|
|
|
<div class="profile__birthdate__content">
|
|
<div class="profile__birthdate__date">
|
|
<label class="profile__birthdate__label">
|
|
<div class="profile__birthdate__title">
|
|
Day
|
|
</div>
|
|
{{ input_select('birthdate[day]', ['-']|merge(range(1, 31)), profile_user.hasBirthdate ? profile_user.birthdate.day : 0, '', '', true, 'profile__birthdate__select profile__birthdate__select--day') }}
|
|
</label>
|
|
|
|
<label class="profile__birthdate__label">
|
|
<div class="profile__birthdate__title">
|
|
Month
|
|
</div>
|
|
{{ input_select('birthdate[month]', ['-']|merge(range(1, 12)), profile_user.hasBirthdate ? profile_user.birthdate.month : 0, '', '', true, 'profile__birthdate__select profile__birthdate__select--month') }}
|
|
</label>
|
|
</div>
|
|
|
|
<div class="profile__birthdate__year">
|
|
<label class="profile__birthdate__label">
|
|
<div class="profile__birthdate__title">
|
|
Year (may be left empty)
|
|
</div>
|
|
{{ input_select('birthdate[year]', ['-']|merge(range(null|date('Y'), null|date('Y') - 100)), profile_user.birthdate.year|default(0), '', '', true, 'profile__birthdate__select profile__birthdate__select--year') }}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if show_warnings %}
|
|
<div class="container profile__container" style="--accent-colour: #c84;">
|
|
{{ container_title('Warnings') }}
|
|
|
|
<div class="profile__warnings">
|
|
{% for warning in profile_warnings %}
|
|
<div class="profile__warnings__item">
|
|
<div class="profile__warnings__datetime">
|
|
<time datetime="{{ warning.createdTime|date('c') }}" title="{{ warning.createdTime|date('r') }}">{{ warning.createdTime|time_format }}</time>
|
|
</div>
|
|
<div class="profile__warnings__body">
|
|
{% for line in warning.bodyLines %}
|
|
<p>{{ line }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if profile_user is defined %}
|
|
<div class="profile__content__main">
|
|
{% if (not profile_is_banned or profile_can_edit) and ((profile_is_editing and perms.edit_about) or profile_user.hasAboutContent) %}
|
|
<div class="container profile__container profile__about" id="about">
|
|
{{ container_title('About ' ~ profile_user.name) }}
|
|
|
|
{% if profile_is_editing %}
|
|
<div class="profile__signature__editor">
|
|
{{ input_select('about[parser]', constant('\\Misuzu\\Parsers\\Parser::NAMES'), profile_user.aboutParser, '', '', false, 'profile__about__select') }}
|
|
<textarea name="about[text]" class="input__textarea profile__about__text" id="about-textarea">{{ profile_user.aboutContent }}</textarea>
|
|
</div>
|
|
{% else %}
|
|
<div class="profile__about__content{% if profile_is_editing %} profile__about__content--edit{% elseif profile_user.aboutParser == constant('\\Misuzu\\Parsers\\Parser::MARKDOWN') %} markdown{% endif %}">
|
|
{{ profile_user.aboutContent|parse_text(profile_user.aboutParser)|raw }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if (not profile_is_banned or profile_can_edit) and ((profile_is_editing and perms.edit_signature) or profile_user.hasSignatureContent) %}
|
|
<div class="container profile__container profile__signature" id="signature">
|
|
{{ container_title('Signature') }}
|
|
|
|
{% if profile_is_editing %}
|
|
<div class="profile__signature__editor">
|
|
{{ input_select('signature[parser]', constant('\\Misuzu\\Parsers\\Parser::NAMES'), profile_user.signatureParser, '', '', false, 'profile__signature__select') }}
|
|
<textarea name="signature[text]" class="input__textarea profile__signature__text" id="signature-textarea">{{ profile_user.signatureContent }}</textarea>
|
|
</div>
|
|
{% else %}
|
|
<div class="profile__signature__content{% if profile_is_editing %} profile__signature__content--edit{% elseif profile_user.signatureParser == constant('\\Misuzu\\Parsers\\Parser::MARKDOWN') %} markdown{% endif %}">
|
|
{{ profile_user.signatureContent|parse_text(profile_user.signatureParser)|raw }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% if profile_is_editing %}
|
|
</form>
|
|
<script>
|
|
let profilePreviousBackground = null;
|
|
|
|
function profileToggleBackground(checked) {
|
|
let currentBg = document.body.style.getPropertyValue('--background-image');
|
|
|
|
if(currentBg != 'initial' && checked) {
|
|
profilePreviousBackground = currentBg;
|
|
currentBg = 'initial';
|
|
} else if(currentBg == 'initial' && !checked) {
|
|
currentBg = profilePreviousBackground;
|
|
}
|
|
|
|
document.body.style.setProperty('--background-image', currentBg);
|
|
}
|
|
|
|
function profileChangeBackgroundAttach(mode) {
|
|
const modes = {
|
|
1: 'cover',
|
|
2: 'stretch',
|
|
3: 'tile',
|
|
4: 'contain',
|
|
};
|
|
|
|
profileToggleBackground(mode == 0);
|
|
|
|
for(let i = 1; i <= Object.keys(modes).length; i++)
|
|
document.body.classList.remove('main--bg-' + modes[i]);
|
|
|
|
if(!modes[mode])
|
|
return;
|
|
|
|
document.body.classList.add('main--bg-' + modes[mode]);
|
|
}
|
|
|
|
function profileToggleBackgroundAttr(attr, mode) {
|
|
let className = '';
|
|
|
|
switch(attr) {
|
|
case 'blend':
|
|
className = 'main--bg-blend';
|
|
break;
|
|
|
|
case 'slide':
|
|
className = 'main--bg-slide';
|
|
break;
|
|
}
|
|
|
|
if(className) {
|
|
if(mode)
|
|
document.body.classList.add(className);
|
|
else
|
|
document.body.classList.remove(className);
|
|
}
|
|
}
|
|
|
|
document.getElementById('background-selection').addEventListener('change', ev => {
|
|
const image = new Image();
|
|
image.src = URL.createObjectURL(ev.target.files[0]);
|
|
image.addEventListener('load', () => {
|
|
document.body.style.setProperty('--background-image', 'url(%)'.replace('%', image.src));
|
|
document.body.style.setProperty('--background-width', '%px'.replace('%', image.width));
|
|
document.body.style.setProperty('--background-height', '%px'.replace('%', image.height));
|
|
});
|
|
});
|
|
</script>
|
|
{% else %}
|
|
</div>
|
|
{% endif %}
|
|
{% endblock %}
|