This commit is contained in:
Le Van Duc 2023-09-06 07:23:21 +07:00
commit 19d61bf796
85 changed files with 4162 additions and 2699 deletions

View file

@ -193,6 +193,15 @@
}
</style>
</noscript>
{% if request.profile.css_background %}
<style>
@media(min-width: 800px) {
#page-container {
background: {{request.profile.css_background|safe}};
}
}
</style>
{% endif %}
</head>
<body>
<svg width="0" height="0" style="display: block">
@ -202,7 +211,7 @@
</svg>
<nav id="navigation" class="unselectable">
<div id="nav-container">
<a id="navicon" href="javascript:void(0)"><i class="fa fa-bars"></i></a>
<span id="navicon"><i class="fa fa-bars"></i></span>
<ul id="nav-list">
<li class="home-nav-element"><a href="{{ url('home') }}">{% include "site-logo-fragment.html" %}</a></li>
<li class="home-nav-element"><span class="nav-divider"></span></li>
@ -261,34 +270,48 @@
</span>
{% if request.user.is_authenticated %}
<span id="user-links">
<ul><li><a href="javascript:void(0)">
<ul><li>
<span>
<img src="{{ gravatar(request.user, 32) }}" height="24" width="24">{# -#}
<img src="{{ gravatar(request.profile, 32) }}" height="24" width="24">{# -#}
<span>
<b class="{{request.profile.css_class}}">{{ request.user.username }}</b>
</span>
</span>
</a></li></ul>
</li></ul>
</span>
<div class="dropdown" id="userlink_dropdown" role="tooptip">
<div class="dropdown-item"><a href="{{ url('user_page') }}">{{ _('Profile') }}</a></div>
<a href="{{ url('user_page') }}">
<div class="dropdown-item">{{ _('Profile') }}</div>
</a>
{% if request.user.is_staff or request.user.is_superuser %}
<div class="dropdown-item"><a href="{{ url('admin:index') }}">{{ _('Admin') }}</a></div>
<a href="{{ url('admin:index') }}">
<div class="dropdown-item">{{ _('Admin') }}</div>
</a>
{% endif %}
{% if request.user.is_superuser %}
<div class="dropdown-item"><a href="{{ url('internal_problem') }}">{{ _('Internal') }}</a></div>
<div class="dropdown-item"><a href="{{ url('site_stats') }}">{{ _('Stats') }}</a></div>
<a href="{{ url('internal_problem') }}">
<div class="dropdown-item">{{ _('Internal') }}</div>
</a>
<a href="{{ url('site_stats') }}">
<div class="dropdown-item">{{ _('Stats') }}</div>
</a>
{% endif %}
<div class="dropdown-item"><a href="{{ url('user_edit_profile') }}">{{ _('Edit profile') }}</a></div>
<a href="{{ url('user_edit_profile') }}">
<div class="dropdown-item">{{ _('Edit profile') }}</div>
</a>
{% if request.user.is_impersonate %}
<div class="dropdown-item"><a href="{{ url('impersonate-stop') }}">Stop impersonating</a></div>
<a href="{{ url('impersonate-stop') }}">
<div class="dropdown-item">Stop impersonating</div>
</a>
{% else %}
<div class="dropdown-item">
<a href="#" id="logout" class="red">{{ _('Log out') }}</a>
<form id="logout-form" action="{{ url('auth_logout') }}" method="POST">
{% csrf_token %}
</form>
</div>
<a href="#" id="logout" class="red">
<div class="dropdown-item">
{{ _('Log out') }}
<form id="logout-form" action="{{ url('auth_logout') }}" method="POST">
{% csrf_token %}
</form>
</div>
</a>
{% endif %}
</div>
{% else %}
@ -302,6 +325,7 @@
</div>
<div id="nav-shadow"></div>
</nav>
<div id="loading-bar"></div>
{% if request.in_contest %}
<div id="contest-info">
<div id="contest-info-main">

View file

@ -3,7 +3,7 @@
</h3>
<div class="sidebox-content">
<div class="user-gravatar">
<img src="{{ gravatar(request.user, 135) }}"
<img src="{{ gravatar(request.profile, 135) }}"
alt="gravatar" width="135px" height="135px">
</div>
<div class="recently-attempted">

View file

@ -42,8 +42,8 @@
{% block left_sidebar %}
<div class="left-sidebar">
{{ make_tab_item('blog', 'fa fa-rss', url('home'), _('News')) }}
{{ make_tab_item('comment', 'fa fa-comments', url('comment_feed'), _('Comments')) }}
{{ make_tab_item('ticket', 'fa fa-question-circle', url('ticket_feed'), _('Tickets')) }}
{{ make_tab_item('comment', 'fa fa-comments', url('comment_feed'), _('Comment')) }}
{{ make_tab_item('ticket', 'fa fa-question-circle', url('ticket_feed'), _('Ticket')) }}
{{ make_tab_item('event', 'fa fa-calendar', '#', _('Events')) }}
</div>
{% endblock %}

View file

@ -7,21 +7,11 @@
<script type="text/javascript" src="{{ static('mathjax3_config.js') }}"></script>
<script type="text/javascript" src="{{ static('event.js') }}"></script>
<script src="https://unpkg.com/@popperjs/core@2"></script>
<script type="module" src="https://unpkg.com/emoji-picker-element@1"></script>
<script type="text/javascript">
let message_template = `
{% with message=message_template %}
{% include "chat/message.html" %}
{% endwith %}
`;
let META_HEADER = [
"{{_('Recent')}}",
"{{_('Following')}}",
"{{_('Admin')}}",
"{{_('Other')}}",
];
</script>
{% compress js %}
{% include "chat/chat_js.html" %}
{% endcompress %}
<script type="text/javascript">
window.limit_time = 24;
window.room_id = "{{room if room else ''}}";
@ -30,75 +20,11 @@
window.lock = false;
window.lock_click_space = false;
window.pushed_messages = new Set();
let isMobile = window.matchMedia("only screen and (max-width: 799px)").matches;
function load_next_page(last_id, refresh_html=false) {
var param = {
'last_id': last_id,
'only_messages': true,
}
$.get("{{ url('chat', '') }}" + window.room_id, param)
.fail(function() {
console.log("Fail to load page, last_id = " + last_id);
})
.done(function(data) {
if (refresh_html) {
$('#chat-log').html('');
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
window.lock = true;
}
var time = refresh_html ? 0 : 200;
setTimeout(function() {
$(".has_next").remove();
let $chat_box = $('#chat-box');
let lastMsgPos = scrollTopOfBottom($chat_box)
$('#loader').hide();
if (refresh_html) {
$('#chat-log').append(data);
}
else {
$('#chat-log').prepend(data);
}
register_time($('.time-with-rel'));
merge_authors();
if (!refresh_html) {
$chat_box.scrollTop(scrollTopOfBottom($chat_box) - lastMsgPos);
}
else {
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
}
window.lock = false;
window.has_next = parseInt($(".has_next").attr("value"));
}, time);
})
}
function scrollTopOfBottom(container) {
return container[0].scrollHeight - container.innerHeight()
}
function scrollContainer(container, loader) {
container.scroll(function() {
if (container.scrollTop() == 0) {
if (!window.lock && window.has_next) {
loader.show();
var message_ids = $('.message').map(function() {
return parseInt($(this).attr('message-id'));
}).get();
load_next_page(Math.min(...message_ids));
}
}
})}
window.load_dynamic_update = function (last_msg) {
var receiver = new EventReceiver(
"{{ EVENT_DAEMON_LOCATION }}", "{{ EVENT_DAEMON_POLL_LOCATION }}",
['chat_lobby', 'chat_{{request.profile.id}}'], last_msg, function (message) {
['{{chat_lobby_channel}}', '{{chat_channel}}'], last_msg, function (message) {
if (window.pushed_messages.has(message.message)) {
return;
}
@ -115,474 +41,13 @@
return receiver;
}
function refresh_status() {
$.get("{{url('online_status_ajax')}}")
.fail(function() {
console.log("Fail to get online status");
})
.done(function(data) {
if (data.status == 403) {
console.log("Fail to retrieve data");
}
else {
$('#chat-online-list').html(data).find('.toggle').each(function () {
register_toggle($(this));
});
register_click_space();
}
})
var data = {
'user': window.other_user_id,
};
$.get("{{url('user_online_status_ajax')}}", data)
.fail(function() {
console.log("Fail to get user online status");
})
.done(function(data) {
$('#chat-info').html(data);
register_time($('.time-with-rel'));
register_setting();
})
}
function add_message(data) {
var $data = $(data);
$('#chat-log').append($data);
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
register_time($('.time-with-rel'));
MathJax.typeset();
merge_authors();
}
function add_new_message(message, room, is_self_author) {
function callback(update) {
if (!document['hidden']) {
if (update) update_last_seen();
refresh_status();
}
else if (!is_self_author) {
window.unread_message++;
document.title = "(" + window.unread_message + ") " + "{{ _('New message(s)') }}";
}
}
if (room == window.room_id) {
$.get({
url: "{{ url('chat_message_ajax') }}",
data: {
message: message,
},
success: function (data) {
add_message(data);
callback(true);
},
error: function (data) {
console.log('Could not add new message');
}
});
}
else {
callback(false);
}
}
function check_new_message(message, tmp_id, room) {
if (room == room_id) {
$.get({
url: "{{ url('chat_message_ajax') }}",
data: {
message: message,
},
success: function (data) {
var $body_block = $(data).find('.body-block');
if ($('#message-'+tmp_id).length) {
$('#message-'+tmp_id).replaceWith(data);
}
else if ($('#body-block-'+tmp_id).length) {
$('#body-block-'+tmp_id).replaceWith($body_block);
}
else {
add_new_message(message, room, true);
}
MathJax.typeset();
register_time($('.time-with-rel'));
remove_unread_current_user();
merge_authors();
},
error: function (data) {
console.log('Fail to check message');
var $body = $('#body-block-'+tmp_id + ' p');
$body.css('text-decoration', 'line-through');
$body.css('text-decoration-color', 'red');
}
});
}
}
function merge_authors() {
var time_limit = 5; // minutes
var last = {
username: null,
time: null,
$content: null
};
$('.body-message').each(function() {
var username = $(this).find(".username a").text().trim();
var $body = $(this).find(".content-message .body-block");
var time = moment($(this).find(".time-with-rel").attr('data-iso'));
var $content = $(this).children('.content-message');
if (username == window.user.name) {
$(this).find('.message-text').each(function() {
$(this).removeClass('message-text-other').addClass('message-text-myself');
});
}
if (username == last.username && time.diff(last.time, 'minutes') <= time_limit) {
last.$content.append($body);
$(this).parent().remove();
}
else {
last.username = username;
last.time = time;
last.$content = $content;
}
});
}
function add_message_from_template(body, tmp_id) {
var html = message_template;
html = html.replaceAll('$body', body).replaceAll('$id', tmp_id);
var $html = $(html);
$html.find('.time-with-rel').attr('data-iso', (new Date()).toISOString());
add_message($html[0].outerHTML);
}
function submit_chat() {
{% if last_msg and not request.profile.mute %}
if ($("#chat-input").val().trim()) {
var body = $('#chat-input').val().trim();
// body = body.split('\n').join('\n\n');
var message = {
body: body,
room: window.room_id,
tmp_id: Date.now(),
};
$('#chat-input').val('');
add_message_from_template(body, message.tmp_id);
$.post("{{ url('post_chat_message') }}", message)
.fail(function(res) {
console.log('Fail to send message');
})
.done(function(res, status) {
$('#empty_msg').hide();
$('#chat-input').focus();
})
}
{% endif %}
}
function resize_emoji(element) {
var html = element.html();
html = html.replace(/(\p{Extended_Pictographic})/ug, `<span class="big-emoji">$1</span>`);
element.html(html);
}
function insert_char_after_cursor(elem, char) {
var val = elem.value;
if (typeof elem.selectionStart == "number" && typeof elem.selectionEnd == "number") {
var start = elem.selectionStart;
var prefix = elem.value.slice(0, start);
var prefix_added = prefix + char;
var chars = [...val];
chars.splice([...prefix].length, 0, char);
elem.value = chars.join('');
elem.selectionStart = elem.selectionEnd = prefix_added.length;
} else if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
elem.focus();
range.text = char;
range.collapse(false);
range.select();
}
}
function load_room(encrypted_user) {
if (window.lock_click_space) return;
function callback() {
history.replaceState(null, '', "{{url('chat', '')}}" + window.room_id);
load_next_page(null, true);
update_last_seen();
refresh_status();
$('#chat-input').focus();
}
window.lock_click_space = true;
if (encrypted_user) {
$.get("{{url('get_or_create_room')}}" + `?other=${encrypted_user}`)
.done(function(data) {
window.room_id = data.room;
window.other_user_id = data.other_user_id;
callback();
})
.fail(function() {
console.log('Fail to get_or_create_room');
})
}
else {
window.room_id = '';
window.other_user_id = '';
callback();
}
window.lock_click_space = false;
}
function register_click_space() {
$('.click_space').on('click', function(e) {
if ($(this).attr('id') == 'click_space_' + window.other_user_id) {
return;
}
var other_user = $(this).attr('value');
load_room(other_user);
});
$('#lobby_row').on('click', function(e) {
if (window.room_id) {
load_room(null);
}
});
if (isMobile) {
$('#chat-tab a').click();
}
}
function update_last_seen() {
var data = {
room: window.room_id
};
$.post("{{ url('update_last_seen') }}", data)
.fail(function(data) {
console.log('Fail to update last seen');
})
.done(function(data) {
})
}
function remove_unread_current_user() {
if (window.other_user_id) {
$("#unread-count-" + window.other_user_id).hide();
}
else {
$('#unread-count-lobby').hide();
}
}
function register_setting() {
$('#setting-button').on('click', function() {
$('#setting-content').toggle();
});
$('#setting-content li').on('click', function() {
$(this).children('a')[0].click();
})
$('#setting-content a').on('click', function() {
var href = $(this).attr('href');
href += '?next=' + window.location.pathname;
$(this).attr('href', href);
})
}
let message_template = `
{% with message=message_template %}
{% include "chat/message.html" %}
{% endwith %}
`;
$(function() {
$('#loader').hide();
merge_authors();
window.has_next = parseInt($(".has_next").attr("value"));
scrollContainer($('#chat-box'), $('#loader'))
{% if request.user.is_staff %}
$(document).on("click", ".chat_remove", function() {
var elt = $(this);
$.ajax({
url: "{{ url('delete_chat_message') }}",
type: 'post',
data: {
message: elt.attr('value'),
},
dataType: 'json',
success: function(data){
var $block = elt.parent();
if ($block.parent().find('.body-block').length > 1) {
$block.remove();
}
else {
elt.closest('li').remove();
}
},
fail: function(data) {
console.log('Fail to delete');
},
});
});
$(document).on("click", ".chat_mute", function() {
if (confirm("{{_('Mute this user and delete all messages?')}}")) {
var elt = $(this);
$.ajax({
url: "{{ url('mute_chat_message') }}",
type: 'post',
data: {
message: elt.attr('value'),
},
dataType: 'json',
success: function(data){
window.location.reload();
},
fail: function(data) {
console.log('Fail to delete');
},
});
}
});
{% endif %}
$("#chat-log").show();
$("#chat-log").change(function() {
$('#chat-log').scrollTop($('#chat-log')[0].scrollHeight);
});
$('#chat-input').focus();
$('#chat-input').keydown(function(e) {
if (e.keyCode === 13) {
if (e.ctrlKey || e.shiftKey) {
insert_char_after_cursor(this, "\n");
}
else {
e.preventDefault();
submit_chat();
}
return false
}
return true
});
$('.chat-right-panel').hide();
$('#chat-tab').find('a').click(function (e) {
e.preventDefault();
$('#chat-tab').addClass('active');
$('#online-tab').removeClass('active');
$('.chat-left-panel').show();
$('.chat-right-panel').hide();
});
$('#online-tab').find('a').click(function (e) {
e.preventDefault();
$('#online-tab').addClass('active');
$('#chat-tab').removeClass('active');
$('.chat-left-panel').hide();
$('.chat-right-panel').show();
});
$('#refresh-button').on('click', function(e) {
e.preventDefault();
refresh_status();
});
setInterval(refresh_status, 2 * 60 * 1000);
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
load_dynamic_update({{last_msg}});
const button = document.querySelector('#emoji-button')
const tooltip = document.querySelector('.tooltip')
Popper.createPopper(button, tooltip)
function toggleEmoji() {
tooltip.classList.toggle('shown')
}
$('#emoji-button').on('click', function(e) {
e.preventDefault();
toggleEmoji();
});
$('emoji-picker').on('emoji-click', function(e) {
var $chat = $('#chat-input').get(0);
insert_char_after_cursor($chat, e.detail.unicode);
$chat.focus();
})
register_click_space();
document.addEventListener('keydown', function(e) {
if (e.keyCode === 27 && $('.tooltip').hasClass('shown')) {
toggleEmoji();
}
})
$('#search-handle').replaceWith($('<select>').attr({
id: 'search-handle',
name: 'other',
onchange: 'form.submit()'
}));
var in_user_redirect = false;
$('#search-handle').select2({
placeholder: '{{ _('Search by handle...') }}',
ajax: {
url: '{{ url('chat_user_search_select2_ajax') }}'
},
minimumInputLength: 1,
escapeMarkup: function (markup) {
return markup;
},
templateResult: function (data, container) {
return $('<span>')
.append($('<img>', {
'class': 'user-search-image', src: data.gravatar_url,
width: 24, height: 24
}))
.append($('<span>', {'class': data.display_rank + ' user-search-name'}).text(data.text))
.append($('<a>', {href: '/user/' + data.text, 'class': 'user-redirect'})
.append($('<i>', {'class': 'fa fa-mail-forward'}))
.mouseover(function () {
in_user_redirect = true;
}).mouseout(function () {
in_user_redirect = false;
}));
}
}).on('select2:selecting', function () {
return !in_user_redirect;
});
$("#chat-input").on("keyup", function() {
$("#chat-input").scrollTop($("#chat-input")[0].scrollHeight);
});
// https://stackoverflow.com/questions/42121565/detecting-class-change-without-setinterval
if (typeof(MutationObserver) !== undefined) {
var observer = new MutationObserver(function (event) {
if (!document['hidden'] && window.unread_message > 0) {
update_last_seen();
refresh_status();
window.unread_message = 0;
document.title = "{{_('Chat Box')}}";
}
})
observer.observe(document.body, {
attributes: true,
attributeFilter: ['class'],
childList: false,
characterData: false
})
}
register_setting();
});
</script>
@ -605,38 +70,36 @@
<div id="chat-container">
<div id="chat-online" class="chat-right-panel sidebox">
<h3>
<i class="fa fa-users"></i>{{_('Online Users')}}
</h3>
<div id="chat-online-content">
<div id="search-container">
<center>
<form id="search-form" name="form" action="{{ url('get_or_create_room') }}" method="post">
{% csrf_token %}
<input id="search-handle" type="text" name="search"
placeholder="{{ _('Search by handle...') }}">
</form>
</center>
<form id="chat-search-form" name="form" action="{{ url('get_or_create_room') }}" method="post">
{% csrf_token %}
<input id="search-handle" type="text" name="search"
placeholder="{{ _('Search by handle...') }}">
</form>
</div>
<div id="chat-online-list">
{% include "chat/online_status.html" %}
</div>
</div>
</div>
<div id="chat-area" class="chat-left-panel" style="width:100%">
<div id="chat-info" style="height: 10%">
<div id="chat-area" class="chat-left-panel">
<div id="chat-info">
{% include 'chat/user_online_status.html' %}
</div>
<div id="chat-box">
<img src="{{static('loading.gif')}}" id="loader">
<ul id="chat-log" style="display: none">
<ul id="chat-log">
{% include 'chat/message_list.html' %}
</ul>
</div>
<div style="height: 15%">
<a id="emoji-button" href="#" title="{{_('Emoji')}}"><i class="icofont-slightly-smile"></i></a>
<div id="chat-input-container">
<textarea maxlength="5000" id="chat-input" placeholder="{{_('Enter your message')}}"></textarea>
<div class="chat-input-icon" id="emoji-button" href="#" title="{{_('Emoji')}}"><i class="icofont-slightly-smile"></i>
</div>
<div class="chat-input-icon" id="submit-button">
<i class="fa fa-play"></i>
</div>
</div>
<div class="tooltip" role="tooltip">
<emoji-picker></emoji-picker>

View file

@ -1,133 +1,200 @@
<style>
footer {
display: none;
}
#content {
padding-top: 0;
}
::-webkit-scrollbar {
width: 20px;
}
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-thumb {
background-color: #d6dee1;
border-radius: 20px;
border: 6px solid transparent;
background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
background-color: #a8bbbf;
}
#page-container {
width: 100%;
}
.body-message img{
max-height: 12em;
}
.tooltip:not(.shown) {
display: none;
}
textarea {
resize: none;
}
.tooltip {
left: 120vh !important;
transform: translate(100px, 0) !important;
position: absolute;
}
#loader {
display: block;
margin-left: auto;
margin-right: auto;
width: 4%;
}
.profile-pic {
height: 2.6em;
width: 2.6em;
border-radius: 0.3em;
margin-top: 0.1em;
float: left;
}
.body-message {
padding-left: 3em;
padding-bottom: 0.5em;
border-bottom: 1px dotted lightgray;
}
.user-time {
margin-bottom: 0.3em;
}
.time {
margin-left: 0.5em;
}
.clear {
clear: both;
}
.content-message {
word-wrap: break-word;
}
.content-message p {
margin: 0;
}
#content {
width: 100%;
}
#content-body {
padding-bottom: 0;
}
#page-container {
min-height: 0;
}
.sidebox h3 {
border-radius: 0;
}
.body-block {
border-radius: 4px;
padding: 0.05em 0.6em;
width: 100%;
}
#search-form {
float: inherit;
}
#search-container {
margin-bottom: 0.4em;
}
#setting {
position: relative;
}
@media (min-width: 800px) {
#page-container {
position:fixed;
overflow:hidden;
}
}
@media (max-width: 799px) {
html, body {
max-width: 100%;
overflow-x: hidden;
}
#mobile ul {
width: 100%;
}
.info-pic {
margin-left: 0.5em;
}
.active-span {
{% compress css %}
<style>
footer {
display: none;
}
}
</style>
#content {
padding-top: 0;
}
::-webkit-scrollbar {
width: 20px;
}
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-thumb {
background-color: #d6dee1;
border-radius: 20px;
border: 6px solid transparent;
background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
background-color: #a8bbbf;
}
#page-container {
width: 100%;
}
.body-message img{
max-height: 12em;
}
.tooltip:not(.shown) {
display: none;
}
textarea {
resize: none;
}
.tooltip {
position: absolute;
z-index: 1000;
}
#loader {
display: block;
margin-left: auto;
margin-right: auto;
width: 80px;
}
.profile-pic {
height: 2.6em;
width: 2.6em;
border-radius: 0.3em;
margin-top: 0.1em;
float: left;
}
.body-message {
padding-left: 3em;
padding-bottom: 0.5em;
}
.user-time {
margin-bottom: 0.3em;
}
.time {
margin-left: 0.5em;
}
.clear {
clear: both;
}
.content-message {
word-wrap: break-word;
}
.content-message p {
margin: 0;
}
#content {
width: 100%;
}
#content-body {
padding-bottom: 0;
}
#page-container {
min-height: 0;
}
.sidebox h3 {
border-radius: 0;
}
.body-block {
border-radius: 4px;
padding: 0.05em 0.6em;
width: 100%;
}
#search-form {
float: inherit;
}
#search-container {
padding: 1em;
}
#setting {
position: relative;
}
.status-user {
display: flex;
flex-direction: column;
min-width: 0;
flex-grow: 1;
}
.wrapline {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
#chat-input-container {
display: flex;
align-items: center;
gap: 1em;
}
#chat-input {
padding: 10px 20px;
font-size: 16px;
border-radius: 20px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s ease-in-out;
width: 80%;
resize: none;
height: 100%;
}
#chat-input:focus {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
outline: none;
}
.chat-input-icon {
font-size: 16px;
border-radius: 50%;
width: 2em;
height: 2em;
display: flex;
justify-content: center;
align-items: center;
border: 2px solid;
}
.chat-input-icon:hover {
cursor: pointer;
}
#chat-area {
display: flex;
flex-direction: column;
}
.info-circle {
position: absolute;
cx: 5px;
cy: 5px;
r: 5px;
stroke: white;
stroke-width: 1;
}
.info-pic {
border-radius: 5px;
margin-left: 1em;
}
#chat-info {
height: 3em;
}
@media (min-width: 800px) {
#page-container {
position:fixed;
overflow:hidden;
}
#chat-input-container {
padding-left: 10%;
}
#chat-area {
padding-bottom: 1.5em;
}
}
@media (max-width: 799px) {
html, body {
max-width: 100%;
overflow-x: hidden;
}
#mobile ul {
width: 100%;
}
.info-pic {
margin-left: 0.5em;
}
.active-span {
display: none;
}
}
</style>
{% endcompress %}

568
templates/chat/chat_js.html Normal file
View file

@ -0,0 +1,568 @@
<script type="text/javascript">
let META_HEADER = [
"{{_('Recent')}}",
"{{_('Following')}}",
"{{_('Admin')}}",
"{{_('Other')}}",
];
let isMobile = window.matchMedia("only screen and (max-width: 799px)").matches;
function load_next_page(last_id, refresh_html=false) {
var param = {
'last_id': last_id,
'only_messages': true,
}
$.get("{{ url('chat', '') }}" + window.room_id, param)
.fail(function() {
console.log("Fail to load page, last_id = " + last_id);
})
.done(function(data) {
if (refresh_html) {
$('#chat-log').html('');
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
window.lock = true;
}
var time = refresh_html ? 0 : 200;
setTimeout(function() {
$(".has_next").remove();
let $chat_box = $('#chat-box');
let lastMsgPos = scrollTopOfBottom($chat_box)
$('#loader').hide();
if (refresh_html) {
$('#chat-log').append(data);
}
else {
$('#chat-log').prepend(data);
}
register_time($('.time-with-rel'));
merge_authors();
if (!refresh_html) {
$chat_box.scrollTop(scrollTopOfBottom($chat_box) - lastMsgPos);
}
else {
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
}
window.lock = false;
window.has_next = parseInt($(".has_next").attr("value"));
}, time);
})
}
function scrollTopOfBottom(container) {
return container[0].scrollHeight - container.innerHeight()
}
function scrollContainer(container, loader) {
container.scroll(function() {
if (container.scrollTop() == 0) {
if (!window.lock && window.has_next) {
loader.show();
var message_ids = $('.message').map(function() {
return parseInt($(this).attr('message-id'));
}).get();
load_next_page(Math.min(...message_ids));
}
}
})}
function refresh_status() {
$.get("{{url('online_status_ajax')}}")
.fail(function() {
console.log("Fail to get online status");
})
.done(function(data) {
if (data.status == 403) {
console.log("Fail to retrieve data");
}
else {
$('#chat-online-list').html(data).find('.toggle').each(function () {
register_toggle($(this));
});
register_click_space();
color_selected_room();
}
})
var data = {
'user': window.other_user_id,
};
$.get("{{url('user_online_status_ajax')}}", data)
.fail(function() {
console.log("Fail to get user online status");
})
.done(function(data) {
$('#chat-info').html(data);
register_time($('.time-with-rel'));
register_setting();
})
}
function add_message(data) {
var $data = $(data);
$('#chat-log').append($data);
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
register_time($('.time-with-rel'));
MathJax.typeset();
merge_authors();
}
function add_new_message(message, room, is_self_author) {
function callback(update) {
if (!document['hidden']) {
if (update) update_last_seen();
refresh_status();
}
else if (!is_self_author) {
window.unread_message++;
document.title = "(" + window.unread_message + ") " + "{{ _('New message(s)') }}";
}
}
if (room == window.room_id) {
$.get({
url: "{{ url('chat_message_ajax') }}",
data: {
message: message,
},
success: function (data) {
add_message(data);
callback(true);
},
error: function (data) {
console.log('Could not add new message');
}
});
}
else {
callback(false);
}
}
function check_new_message(message, tmp_id, room) {
if (room == room_id) {
$.get({
url: "{{ url('chat_message_ajax') }}",
data: {
message: message,
},
success: function (data) {
var $body_block = $(data).find('.body-block');
if ($('#message-'+tmp_id).length) {
$('#message-'+tmp_id).replaceWith(data);
}
else if ($('#body-block-'+tmp_id).length) {
$('#body-block-'+tmp_id).replaceWith($body_block);
}
else {
add_new_message(message, room, true);
}
MathJax.typeset();
register_time($('.time-with-rel'));
remove_unread_current_user();
merge_authors();
},
error: function (data) {
console.log('Fail to check message');
var $body = $('#body-block-'+tmp_id + ' p');
$body.css('text-decoration', 'line-through');
$body.css('text-decoration-color', 'red');
}
});
}
}
function merge_authors() {
var time_limit = 5; // minutes
var last = {
username: null,
time: null,
$content: null
};
$('.body-message').each(function() {
var username = $(this).find(".username a").text().trim();
var $body = $(this).find(".content-message .body-block");
var time = moment($(this).find(".time-with-rel").attr('data-iso'));
var $content = $(this).children('.content-message');
if (username == window.user.name) {
$(this).find('.message-text').each(function() {
$(this).removeClass('message-text-other').addClass('message-text-myself');
});
}
if (username == last.username && time.diff(last.time, 'minutes') <= time_limit) {
last.$content.append($body);
$(this).parent().remove();
}
else {
last.username = username;
last.time = time;
last.$content = $content;
}
});
}
function add_message_from_template(body, tmp_id) {
if (window.room_id) {
$("#last_msg-" + window.room_id).html(body);
}
var html = message_template;
html = html.replaceAll('$body', body).replaceAll('$id', tmp_id);
var $html = $(html);
$html.find('.time-with-rel').attr('data-iso', (new Date()).toISOString());
add_message($html[0].outerHTML);
}
function submit_chat() {
{% if last_msg and not request.profile.mute %}
if ($("#chat-input").val().trim()) {
$('#chat-input-container').height('auto');
var body = $('#chat-input').val().trim();
// body = body.split('\n').join('\n\n');
var message = {
body: body,
room: window.room_id,
tmp_id: Date.now(),
};
$('#chat-input').val('');
add_message_from_template(body, message.tmp_id);
$.post("{{ url('post_chat_message') }}", message)
.fail(function(res) {
console.log('Fail to send message');
})
.done(function(res, status) {
$('#empty_msg').hide();
$('#chat-input').focus();
})
}
{% endif %}
}
function resize_emoji(element) {
var html = element.html();
html = html.replace(/(\p{Extended_Pictographic})/ug, `<span class="big-emoji">$1</span>`);
element.html(html);
}
function insert_char_after_cursor(elem, char) {
var val = elem.value;
if (typeof elem.selectionStart == "number" && typeof elem.selectionEnd == "number") {
var start = elem.selectionStart;
var prefix = elem.value.slice(0, start);
var prefix_added = prefix + char;
var chars = [...val];
chars.splice([...prefix].length, 0, char);
elem.value = chars.join('');
elem.selectionStart = elem.selectionEnd = prefix_added.length;
} else if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
elem.focus();
range.text = char;
range.collapse(false);
range.select();
}
}
function color_selected_room() {
$(".status-row").removeClass("selected-status-row");
$("#click_space_" + window.other_user_id).addClass("selected-status-row");
}
function load_room(encrypted_user) {
if (window.lock_click_space) return;
function callback() {
history.replaceState(null, '', "{{url('chat', '')}}" + window.room_id);
load_next_page(null, true);
update_last_seen();
refresh_status();
$('#chat-input').focus();
}
window.lock_click_space = true;
if (encrypted_user) {
$.get("{{url('get_or_create_room')}}" + `?other=${encrypted_user}`)
.done(function(data) {
window.room_id = data.room;
window.other_user_id = data.other_user_id;
color_selected_room();
callback();
})
.fail(function() {
console.log('Fail to get_or_create_room');
})
}
else {
window.room_id = '';
window.other_user_id = '';
color_selected_room();
callback();
}
window.lock_click_space = false;
}
function register_click_space() {
$('.click_space').on('click', function(e) {
if ($(this).attr('id') == 'click_space_' + window.other_user_id) {
return;
}
var other_user = $(this).attr('value');
load_room(other_user);
});
$('#lobby_row').on('click', function(e) {
if (window.room_id) {
load_room(null);
}
});
if (isMobile) {
$('#chat-tab a').click();
}
}
function update_last_seen() {
var data = {
room: window.room_id
};
$.post("{{ url('update_last_seen') }}", data)
.fail(function(data) {
console.log('Fail to update last seen');
})
.done(function(data) {
})
}
function remove_unread_current_user() {
if (window.other_user_id) {
$("#unread-count-" + window.other_user_id).hide();
}
else {
$('#unread-count-lobby').hide();
}
}
function register_setting() {
$('#setting-button').on('click', function() {
$('#setting-content').toggle();
});
$('#setting-content li').on('click', function() {
$(this).children('a')[0].click();
})
$('#setting-content a').on('click', function() {
var href = $(this).attr('href');
href += '?next=' + window.location.pathname;
$(this).attr('href', href);
})
}
$(function() {
$('#loader').hide();
update_last_seen();
merge_authors();
window.has_next = parseInt($(".has_next").attr("value"));
scrollContainer($('#chat-box'), $('#loader'))
{% if request.user.is_staff %}
$(document).on("click", ".chat_remove", function() {
var elt = $(this);
$.ajax({
url: "{{ url('delete_chat_message') }}",
type: 'post',
data: {
message: elt.attr('value'),
},
dataType: 'json',
success: function(data){
var $block = elt.parent();
if ($block.parent().find('.body-block').length > 1) {
$block.remove();
}
else {
elt.closest('li').remove();
}
},
fail: function(data) {
console.log('Fail to delete');
},
});
});
$(document).on("click", ".chat_mute", function() {
if (confirm("{{_('Mute this user and delete all messages?')}}")) {
var elt = $(this);
$.ajax({
url: "{{ url('mute_chat_message') }}",
type: 'post',
data: {
message: elt.attr('value'),
},
dataType: 'json',
success: function(data){
window.location.reload();
},
fail: function(data) {
console.log('Fail to delete');
},
});
}
});
{% endif %}
$("#chat-log").show();
$("#chat-log").change(function() {
$('#chat-log').scrollTop($('#chat-log')[0].scrollHeight);
});
$('#chat-input').focus();
$('#chat-input').keydown(function(e) {
if (e.keyCode === 13) {
if (e.ctrlKey || e.shiftKey) {
insert_char_after_cursor(this, "\n");
}
else {
e.preventDefault();
submit_chat();
}
return false
}
return true
});
$('#chat-tab').find('a').click(function (e) {
e.preventDefault();
$('#chat-tab').addClass('active');
$('#online-tab').removeClass('active');
$('.chat-left-panel').show();
$('.chat-right-panel').hide();
});
$('#online-tab').find('a').click(function (e) {
e.preventDefault();
$('#online-tab').addClass('active');
$('#chat-tab').removeClass('active');
$('.chat-left-panel').hide();
$('.chat-right-panel').show();
});
$('#refresh-button').on('click', function(e) {
e.preventDefault();
refresh_status();
});
setInterval(refresh_status, 2 * 60 * 1000);
$('#chat-box').scrollTop($('#chat-box')[0].scrollHeight);
const button = document.querySelector('#emoji-button');
const tooltip = document.querySelector('.tooltip');
Popper.createPopper(button, tooltip, {
placement: 'left-end',
});
function toggleEmoji() {
tooltip.classList.toggle('shown')
}
$('#emoji-button').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
toggleEmoji();
});
// Đóng bảng emoji khi click bất kỳ chỗ nào trên màn hình
document.addEventListener("click", function(e) {
if (!tooltip.contains(e.target)) {
tooltip.classList.remove('shown'); // Ẩn bảng emoji
}
});
$('emoji-picker').on('emoji-click', function(e) {
var $chat = $('#chat-input').get(0);
insert_char_after_cursor($chat, e.detail.unicode);
$chat.focus();
})
register_click_space();
document.addEventListener('keydown', function(e) {
if (e.keyCode === 27 && $('.tooltip').hasClass('shown')) {
toggleEmoji();
}
})
$('#search-handle').replaceWith($('<select>').attr({
id: 'search-handle',
name: 'other',
onchange: 'form.submit()'
}));
var in_user_redirect = false;
$('#search-handle').select2({
placeholder: '<i class="fa fa-search"></i> {{ _('Search by handle...') }}',
ajax: {
url: '{{ url('chat_user_search_select2_ajax') }}'
},
minimumInputLength: 1,
escapeMarkup: function (markup) {
return markup;
},
templateResult: function (data, container) {
return $('<span>')
.append($('<img>', {
'class': 'user-search-image', src: data.gravatar_url,
width: 24, height: 24
}))
.append($('<span>', {'class': data.display_rank + ' user-search-name'}).text(data.text))
.append($('<a>', {href: '/user/' + data.text, 'class': 'user-redirect'})
.append($('<i>', {'class': 'fa fa-mail-forward'}))
.mouseover(function () {
in_user_redirect = true;
}).mouseout(function () {
in_user_redirect = false;
}));
}
}).on('select2:selecting', function () {
return !in_user_redirect;
});
$("#chat-input").on("keyup", function() {
$("#chat-input").scrollTop($("#chat-input")[0].scrollHeight);
});
// https://stackoverflow.com/questions/42121565/detecting-class-change-without-setinterval
if (typeof(MutationObserver) !== undefined) {
var observer = new MutationObserver(function (event) {
if (!document['hidden'] && window.unread_message > 0) {
update_last_seen();
refresh_status();
window.unread_message = 0;
document.title = "{{_('Chat Box')}}";
}
})
observer.observe(document.body, {
attributes: true,
attributeFilter: ['class'],
childList: false,
characterData: false
})
}
register_setting();
color_selected_room();
$('#chat-input').on('input', function () {
$('#chat-input-container').height('auto');
$('#chat-input-container').height((this.scrollHeight) + 'px');
});
$('#submit-button').on('click', submit_chat);
});
</script>

View file

@ -27,11 +27,18 @@
fill="{{'green' if user.is_online else 'red'}}"/>
</svg>
</div>
<span style="padding-left:0.3em" class="username {{ user.user.css_class }}">
{{ user.user.username }}
</span>
<span class="spacer">
<span class="unread-count" id="unread-count-{{user.user.id}}">{{user.unread_count if user.unread_count}}</span>
<div class="status-user">
<span class="username {{ user.user.css_class }} wrapline">
{{ user.user.username }}
</span>
{% if user.last_msg %}
<span class="status_last_message wrapline" {% if user.room %}id="last_msg-{{user.room}}"{% endif %}>
{{ user.last_msg }}
</span>
{% endif %}
</div>
<span class="unread-count" id="unread-count-{{user.user.id}}">
{{user.unread_count if user.unread_count}}
</span>
</li>
{% endfor %}

View file

@ -1,7 +1,7 @@
{% if other_user %}
<div class="status-container" style="height: 100%">
<img src="{{ gravatar(other_user.user, 135) }}" class="info-pic">
<svg style="position:absolute; height:100%; width: 100%">
<img src="{{ gravatar(other_user, 135) }}" class="info-pic">
<svg style="position:absolute; height:100%; width: 100%; transform: rotate(180deg);" >
<circle class="info-circle"
fill="{{'green' if other_online else 'red'}}"/>
</svg>

View file

@ -25,17 +25,32 @@
</style>
{% endblock %}
{% block js_media %}
<script type="text/javascript">
$(function() {
$("#id_organization").select2();
});
</script>
{% endblock %}
{% block body %}
<form id="contest-clone-panel" action="" method="post" class="form-area">
{% csrf_token %}
{% if form.errors %}
{% if form.key.errors %}
<div id="form-errors">
{{ form.key.errors }}
<div>{{ form.key.errors }}</div>
</div>
{% endif %}
<div><label class="inline-header grayed">{{ _('Enter a new key for the cloned contest:') }}</label></div>
<div id="contest-key-container"><span class="fullwidth">{{ form.key }}</span></div>
<div><label class="inline-header grayed">{{ _('Group:') }}</label></div>
{{form.organization}}
{% if form.organization.errors %}
<div id="form-errors">
<div>{{ form.organization.errors }}</div>
</div>
{% endif %}
<hr>
<button style="float: right;" type="submit">{{ _('Clone!') }}</button>
</form>

View file

@ -278,11 +278,11 @@
<br>
{% endif %}
<h3 class="toggle open contest-group-header">
<i class="fa fa-chevron-right fa-fw"></i>
{{ _('Ongoing Contests') }}
</h3>
{% if current_contests %}
<h3 class="toggle open contest-group-header">
<i class="fa fa-chevron-right fa-fw"></i>
{{ _('Ongoing Contests') }}
</h3>
<div id="ongoing-table" class="toggled">
<table class="contest-list table striped">
<thead>
@ -319,6 +319,11 @@
</table>
<br>
</div>
{% else %}
<div class="toggled">
<i> {{ _('There is no ongoing contest at this time.') }} </i>
<br>
</div>
{% endif %}
<h3 class="toggle open contest-group-header">
@ -354,15 +359,17 @@
</table>
</div>
{% else %}
<i>{{ _('There are no scheduled contests at this time.') }}</i>
<br>
<div class="toggled">
<i>{{ _('There is no scheduled contest at this time.') }}</i>
<br>
</div>
{% endif %}
<br>
<h3 class="toggle open contest-group-header">
{{ _('Past Contests') }}
</h3>
{% if past_contests %}
<h3 class="toggle open contest-group-header">
{{ _('Past Contests') }}
</h3>
{% if page_obj and page_obj.num_pages > 1 %}
<div style="margin-bottom: 4px;">
{% include "list-pages.html" %}
@ -411,6 +418,11 @@
{% include "list-pages.html" %}
</div>
{% endif %}
{% else %}
<div class="toggled">
<i> {{ _('There is no past contest.') }} </i>
<br>
</div>
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block media %}
<style type="text/css">
.errorlist {
font-size: 0.8em;
}
</style>
{% endblock %}
{% block body %}
<form action="" method="post" class="form-area">{% csrf_token %}
<table border="0">{{ form.as_table() }}</table>
<hr>
<button style="float:right;" type="submit">{{ _('Verify Email') }}</button>
</form>
{% endblock %}

View file

@ -0,0 +1,4 @@
{% extends "base.html" %}
{% block body %}
<p>{{ _('Invalid reset link.') }}</p>
{% endblock %}

View file

@ -0,0 +1,6 @@
{% extends "base.html" %}
{% block body %}
{% if request.profile.email_change_pending %}
<p>{{ _('An email was sent to') }} {{request.profile.email_change_pending}}. {{_('If you don\'t see it, kindly check your spam folder as well.')}}</p>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,4 @@
{% extends "base.html" %}
{% block body %}
<p>{{ _('Your email was sucessfully changed to') }} {{user.email}}</p>
{% endblock %}

View file

@ -0,0 +1,21 @@
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
background-color: #f7f7f7;
}
</style>
<div style="max-width: 600px; margin: 0 auto; padding: 20px; background-color: #ffffff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);">
<div style="text-align: center; margin-bottom: 20px;">
<img src="{{ protocol }}://{{ domain }}{{static('icons/logo.png')}}" alt="{{site_name}}" style="max-width: 150px;">
</div>
<div style="margin-bottom: 20px; text-align: center;">
<h2>{{title}}</h2>
<p>{{_('Dear')}} {{username}},</p>
<p>{{message}}</p>
</div>
<div style="text-align: center;">
<a href="{{ protocol }}://{{ domain }}{{ url_path }}" style="display: inline-block; padding: 10px 20px; background-color: #007bff; color: #ffffff; text-decoration: none; border-radius: 4px;">{{button_text}}</a>
</div>
</div>

View file

@ -29,6 +29,11 @@
{% endblock %}
{% block middle_content %}
<form>
<label for="name">{{_('Search')}}:</label>
<input type="text" name="q" value="{{query}}">
</form>
<br>
<table class="table">
<thead>
<tr>

View file

@ -17,7 +17,7 @@
display: inline-flex;
}
.problems-problem {
width: 40%;
max-width: 60vh;
}
input[type=number] {
width: 5em;
@ -34,11 +34,10 @@
{% block middle_content %}
<form action="" method="post">
{% csrf_token %}
{% if form.errors %}
{% if form.errors or problems_form.errors %}
<div class="alert alert-danger alert-dismissable">
<a href="#" class="close">x</a>
{{ form.non_field_errors() }}
{{ form.errors }}
{{_("Please fix below errors")}}
</div>
{% endif %}
{% for field in form %}
@ -64,7 +63,7 @@
<tr>
{% for field in problems_form[0] %}
{% if not field.is_hidden %}
<th>
<th class="problems-{{field.name}}">
{{field.label}}
</th>
{% endif %}

View file

@ -3,8 +3,7 @@
{% if form.errors %}
<div class="alert alert-danger alert-dismissable">
<a href="#" class="close">x</a>
{{ form.non_field_errors() }}
{{ form.errors }}
{{ _("Please fix below errors") }}
</div>
{% endif %}
{% for field in form %}

View file

@ -6,7 +6,7 @@
{% if show_preview %}
<div id="{{ postfix|safe }}-preview" data-preview-url="{{ preview_url }}" data-textarea-id="wmd-input-{{ postfix }}"
data-timeout="{{ preview_timeout or '' }}" class="wmd-panel wmd-preview dmmd-preview {{ extra_classes }}">
<div class="dmmd-preview-update"><i class="fa fa-refresh"></i> Update Preview</div>
<div class="dmmd-preview-update"><i class="fa fa-refresh"></i> {{_('Update Preview')}}</div>
<div class="dmmd-preview-content content-description"></div>
</div>
{% endif %}

View file

@ -166,12 +166,12 @@
$checker.change(function () {
$tr_checker.toggle($checker.val() == 'custom').change();
$tr_validator.toggle($checker.val() == 'customval' || $checker.val() == 'testlib').change();
$tr_interactive.toggle($checker.val() == 'interact').change();
$sample.toggle(['custom', 'customval', 'interact'].includes($checker.val())).change();
}).change();
$ioi_signature.change(function() {
$tr_interactive.toggle($ioi_signature.is(':checked')).change();
$tr_sig_header.toggle($ioi_signature.is(':checked')).change();
$tr_sig_handler.toggle($ioi_signature.is(':checked')).change();
}).change();

View file

@ -1,23 +1,16 @@
<b>Thanks for registering on the {{ site.name }}! We're glad to have you.</b>
<br><br>
The last step is activating your account. Please activate your {{ SITE_NAME }} account in the next {{ expiration_days }} days.
<br><br>
Please click on the following link to activate your account:
<p style="margin-left:1em">
<a href="http://{{ site.domain }}/accounts/activate/{{ activation_key }}/">http://{{ site.domain }}/accounts/activate/{{ activation_key }}</a>
</p>
Alternatively, you can reply to this message to activate your account.
Your reply must keep the following text intact for this to work:
{% set url_path = "/accounts/activate/" + activation_key %}
{% set title = _("Account activation") %}
{% set message = _("Thanks for registering! We're glad to have you. The last step is activating your account. Please activate your account in the next %(expiration_days)d days.", expiration_days=expiration_days) %}
{% set username = user.get_username() %}
{% set button_text = _("Activate") %}
{% set domain = site.domain %}
{% set protocol = "http" %}
{% include "general_email.html" %}
<br>
{{_("Alternatively, you can reply to this message to activate your account. Your reply must keep the following text intact for this to work:")}}
<pre style="margin-left:1em">
{{ activation_key }}
</pre>
{% if SITE_ADMIN_EMAIL %}
See you soon!
<br>
If you have problems activating your account, feel free to send us an email at <a href="mailto:{{ SITE_ADMIN_EMAIL }}">{{ SITE_ADMIN_EMAIL }}</a>.
{% else %}
See you soon!
{% endif %}
{{_("See you soon!")}}

View file

@ -1 +1 @@
Activate your {{ SITE_NAME }} account
{% trans %}Activate your {{ SITE_NAME }} account{% endtrans %}

View file

@ -1,19 +1,6 @@
<span style="display:block;margin: 0 2px;padding: 1em;border: 3px solid #FFDE05;background-color: #000;border-radius: 6px;font-size: .95em;color: #444;margin-bottom:0.75em">
<div style="display:table;margin-left:2em"><img src="https://avatars1.githubusercontent.com/u/6934864?v=3&s=101" style="display:inline;vertical-align: middle">
<h1 style="font-size:4em;display:inline;vertical-align: middle"><a href="//{{ domain }}" style="text-decoration:none;color:gray"><span style="color: #FFDE05">LQD</span>OJ</a>
</h1>
</div>
</span>
<div style="display:block;margin: 0 2px;padding: 1em;border: 3px solid #2980B9;background-color: #f8f8f8;border-radius: 6px;font-size: .95em;color: #444;">
<b>Forgot your password on the {{ site_name }}? Don't worry!</b><br><br>
To reset the password for your account "{{ user.get_username() }}", click the below button.
<p align="center">
<a href="{{ protocol }}://{{ domain }}{{ url('password_reset_confirm', uidb64=uid, token=token) }}" style="cursor: pointer;display:block;text-align: center;padding: 4px 2px 5px;color: white;border: 1px solid #666;border-radius: 1px;background: #2980b9;background: linear-gradient(180deg, #00aee0, #2980b9);text-decoration: none;line-height:2em;font-size:1emm;width:12em;">Reset password</a>
</p>
{% if SITE_ADMIN_EMAIL %}
See you soon! If you have problems resetting your email, feel free to shoot us an email at <a href="mailto:{{ SITE_ADMIN_EMAIL }}">{{ SITE_ADMIN_EMAIL }}</a>
{% else %}
See you soon!
{% endif %}
</div>
{% set url_path = url('password_reset_confirm', uidb64=uid, token=token) %}
{% set title = _("Password Reset") %}
{% set message = _("We have received a request to reset your password. Click the button below to reset your password:") %}
{% set username = user.get_username() %}
{% set button_text = _("Reset Password") %}
{% include "general_email.html" %}

View file

@ -1,4 +1,4 @@
{% extends "base.html" %}
{% block body %}
<p>{{ _('You have successfully been registered. An email has been sent to the email address you provided to confirm your registration.') }}</p>
<p>{{ _('You have successfully been registered. An email has been sent to the email address you provided to confirm your registration. If you don\'t see it, kindly check your spam folder as well.') }}</p>
{% endblock %}

View file

@ -78,12 +78,6 @@
<div class="block-header">{{ _('Default language') }}</div>
<span class="fullwidth">{{ form.language }}</span>
<div class="block-header">{{ _('Affiliated organizations') }}</div>
{{ form.organizations }}
{% if form.organizations.errors %}
<div class="form-field-error">{{ form.organizations.errors }}</div>
{% endif %}
{% if form.captcha %}
<div style="margin-top: 0.5em">{{ form.captcha }}</div>
{% if form.captcha.errors %}
@ -92,12 +86,6 @@
{% endif %}
<hr>
{% if tos_url %}
<span class="tos-section">
{{ _('By registering, you agree to our') }}
<a href="{{ tos_url }}">{{ _('Terms & Conditions') }}</a>.
</span>
{% endif %}
<button style="float:right;" type="submit">{{ _('Register!') }}</button>
</form>
</div>

View file

@ -1,8 +1,3 @@
{% if submission.contest_or_none %}
{% set prefix_length = submission.contest_or_none.problem.output_prefix_override %}
{% else %}
{% set prefix_length = None %}
{% endif %}
{% set is_pretest = submission.is_pretested %}
{% if submission.status != 'IE' %}

View file

@ -118,7 +118,7 @@
{% macro make_tab_item(name, fa, url, text) %}
<div class="left-sidebar-item {% if page_type == name %}active{% endif %}" data-href="{{ url }}" id="{{ name }}-tab">
<span class="sidebar-icon"><i class="{{ fa }}"></i></span>
<span>{{ text }}</span>
<span class="sidebar-text">{{ text }}</span>
</div>
{% endmacro %}

View file

@ -162,7 +162,7 @@
<section class="message new-message">
<div class="info">
<a href="{{ url('user_page', request.user.username) }}" class="user">
<img src="{{ gravatar(request.user, 135) }}" class="gravatar">
<img src="{{ gravatar(request.profile, 135) }}" class="gravatar">
<div class="username {{ request.profile.css_class }}">{{ request.user.username }}</div>
</a>
</div>

View file

@ -43,9 +43,13 @@
#center-float {
position: relative;
margin: 0 auto auto -28.5em;
left: 60%;
width: 700px;
width: 100%;
display: flex;
justify-content: center;
}
.main-info tr td {
padding-bottom: 1em;
}
</style>
{% endblock %}
@ -79,17 +83,19 @@
{% block body %}
<div id="center-float">
<form id="edit-form" action="" method="post" class="form-area">
{% if form.errors %}
<form id="edit-form" action="" method="post" class="form-area" enctype="multipart/form-data">
{% if form.errors or form_user.errors %}
<div class="alert alert-danger alert-dismissable">
<a href="#" class="close">x</a>
{{ form.non_field_errors() }}
{{ form.errors }}
<br>
{{ form_user.errors }}
</div>
{% endif %}
{% csrf_token %}
<table class="block-header grayed">
<table class="block-header grayed main-info">
<tr>
<td> {{ _('Fullname') }}: </td>
<td> {{ form_user.first_name }} </td>
@ -98,6 +104,27 @@
<td> {{ _('School') }}: </td>
<td> {{ form_user.last_name }} </td>
</tr>
<tr>
<td> {{ _('Password') }}: </td>
<td>
<a href="{{ url('password_change') }}">
{{ _('Change your password') }}
</a>
</td>
</tr>
<tr>
<td> {{ _('Email') }}: </td>
<td>
{{ request.user.email }}
<a href="{{ url('email_change') }}">
({{ _('Change email') }})
</a>
</td>
</tr>
<tr>
<td>{{ _('Avatar') }}: </td>
<td>{{ form.profile_image }}</td>
</tr>
</table>
<hr>
@ -127,38 +154,29 @@
<td><span class="fullwidth">{{ form.math_engine }}</span></td>
</tr>
{% endif %}
<tr>
<td colspan="2">
<a href="http://www.gravatar.com/" title="{{ _('Change your avatar') }}"
target="_blank" class="inline-header">{{ _('Change your avatar') }}</a>
</td>
</tr>
<tr>
<td colspan="2">
<a href="{{ url('password_change') }}" class="inline-header">
{{ _('Change your password') }}
</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
<br>
<div>
{% if profile.is_totp_enabled %}
{{ _('Two Factor Authentication is enabled.') }}
{% if require_staff_2fa and request.user.is_staff %}
<a id="disable-2fa-button" class="button inline-button">Disable</a>
{% else %}
<a href="{{ url('disable_2fa') }}" class="button inline-button">Disable</a>
<a href="{{ url('disable_2fa') }}" class="button inline-button">{{_('Disable')}}</a>
{% endif %}
{% else %}
{{ _('Two Factor Authentication is disabled.') }}
<a href="{{ url('enable_2fa') }}" class="button inline-button">Enable</a>
<a href="{{ url('enable_2fa') }}" class="button inline-button">{{_('Enable')}}</a>
{% endif %}
</div>
<br><hr>
<span class="block-header">{{ _('CSS background') }}:</span>
{{ form.css_background }}
<div><i>{{form.css_background.help_text}}</i></div>
<br><br>
<div class="block-header">{{ _('User-script') }}:</div>
{{ form.user_script }}
<hr>

View file

@ -59,16 +59,6 @@
{{user.user.first_name}}{% if user.user.last_name %} ({{user.user.last_name}}){% endif %}
</p>
{% endif %}
{% with orgs=user.organizations.all() %}
{% if orgs %}
<p style="margin-top: 0"><i class="fa fa-university"></i> {{ _('From') }}
{% for org in orgs -%}
<a href="{{ org.get_absolute_url() }}">{{ org.name }}</a>
{%- if not loop.last %}, {% endif %}
{% endfor %}
</p>
{% endif %}
{% endwith %}
{% if perms.judge.change_profile %}
{% with notes=user.notes %}
{% if notes %}
@ -78,7 +68,6 @@
{% endif %}
{% endwith %}
{% endif%}
{% if user.about %}
<h4>{{ _('About') }}</h4>
{% cache 86400 'user_about' user.id MATH_ENGINE %}

View file

@ -0,0 +1,13 @@
{% if widget.is_initial %}
<div>
<a href="{{widget.value.url}}" target=_blank>
<img src="{{widget.value.url}}" width="{{widget.width}}" height="{{widget.height}}" style="border-radius: 3px;">
</a>
<div>
{{ widget.input_text }}:
{% endif %}
<input type="{{ widget.type }}" name="{{ widget.name }}">
{% if widget.is_initial %}
</div>
</div>
{% endif %}