Cloned DMOJ
This commit is contained in:
parent
f623974b58
commit
49dc9ff10c
513 changed files with 132349 additions and 39 deletions
20
templates/admin/auth/user/change_form.html
Normal file
20
templates/admin/auth/user/change_form.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
{% extends "admin/change_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script>
|
||||
django.jQuery(function ($) {
|
||||
$('.profilelink').appendTo('div#bottombar').show();
|
||||
});
|
||||
</script>
|
||||
{% endblock extrahead %}
|
||||
|
||||
{% block after_field_sets %}{{ block.super }}
|
||||
{% if original %}
|
||||
<a style="display: none" title="{% trans "Edit profile" %}" href="{% url 'admin:judge_profile_change' original.profile.pk %}"
|
||||
class="button profilelink">
|
||||
<i class="fa fa-lg fa-user-plus"></i>
|
||||
<span class="text">{% trans "Edit profile" %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
23
templates/admin/judge/contest/change_form.html
Normal file
23
templates/admin/judge/contest/change_form.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
{% extends "admin/change_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script>
|
||||
django.jQuery(function ($) {
|
||||
$('.rerate-link').appendTo('div#bottombar').show();
|
||||
$('.rejudge-link').click(function () {
|
||||
return confirm('{{ _('Are you sure you want to rejudge ALL the submissions?') }}');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock extrahead %}
|
||||
|
||||
{% block after_field_sets %}{{ block.super }}
|
||||
{% if original and original.is_rated and perms.judge.contest_rating %}
|
||||
<a style="display: none" title="{% trans "Rate" %}" href="{% url 'admin:judge_contest_rate' original.pk %}"
|
||||
class="button rerate-link">
|
||||
<i class="fa fa-lg fa-signal"></i>
|
||||
<span class="text">{% trans "Rate" %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
13
templates/admin/judge/contest/change_list.html
Normal file
13
templates/admin/judge/contest/change_list.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "reversion/change_list.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block object-tools-items %}
|
||||
{{ block.super }}
|
||||
{% if not is_popup and perms.judge.contest_rating %}
|
||||
<li>
|
||||
<a href="{% url 'admin:judge_contest_rate_all' %}" class="ratealllink">
|
||||
<i class="fa fa-signal"></i> {% trans "Rate all ratable contests" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock %}
|
26
templates/admin/judge/judge/change_form.html
Normal file
26
templates/admin/judge/judge/change_form.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
{% extends "admin/change_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script>
|
||||
django.jQuery(function ($) {
|
||||
$('.disconnect-link').appendTo('div#bottombar').show();
|
||||
$('.terminate-link').appendTo('div#bottombar').show();
|
||||
});
|
||||
</script>
|
||||
{% endblock extrahead %}
|
||||
|
||||
{% block after_field_sets %}{{ block.super }}
|
||||
{% if original %}
|
||||
<a style="display: none" title="{% trans "Disconnect" %}" href="{% url 'admin:judge_judge_disconnect' original.pk %}"
|
||||
class="button disconnect-link">
|
||||
<i class="fa fa-lg fa-power-off"></i>
|
||||
<span class="text">{% trans "Disconnect" %}</span>
|
||||
</a>
|
||||
<a style="display: none" title="{% trans "Terminate" %}" href="{% url 'admin:judge_judge_terminate' original.pk %}"
|
||||
class="button terminate-link">
|
||||
<i class="fa fa-lg fa-plug"></i>
|
||||
<span class="text">{% trans "Terminate" %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
20
templates/admin/judge/problem/change_form.html
Normal file
20
templates/admin/judge/problem/change_form.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
{% extends "admin/change_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script>
|
||||
django.jQuery(function ($) {
|
||||
$('.submissions-link').appendTo('div#bottombar').show();
|
||||
});
|
||||
</script>
|
||||
{% endblock extrahead %}
|
||||
|
||||
{% block after_field_sets %}{{ block.super }}
|
||||
{% if original %}
|
||||
<a style="display: none" title="{% trans "View Submissions" %}" class="button submissions-link"
|
||||
href="{% url 'admin:judge_submission_changelist' %}?problem__code={{ original.code }}">
|
||||
<i class="fa fa-lg fa-search-plus"></i>
|
||||
<span class="text">{% trans "View submissions" %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
20
templates/admin/judge/profile/change_form.html
Normal file
20
templates/admin/judge/profile/change_form.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
{% extends "admin/change_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script>
|
||||
django.jQuery(function ($) {
|
||||
$('.userlink').appendTo('div#bottombar').show();
|
||||
});
|
||||
</script>
|
||||
{% endblock extrahead %}
|
||||
|
||||
{% block after_field_sets %}{{ block.super }}
|
||||
{% if original %}
|
||||
<a style="display: none" title="{% trans "Edit user" %}" href="{% url 'admin:auth_user_change' original.user.pk %}"
|
||||
class="button userlink">
|
||||
<i class="fa fa-lg fa-user"></i>
|
||||
<span class="text">{% trans "Edit user" %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
20
templates/admin/judge/submission/change_form.html
Normal file
20
templates/admin/judge/submission/change_form.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
{% extends "admin/change_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
<script>
|
||||
django.jQuery(function ($) {
|
||||
$('.rejudgelink').appendTo('div#bottombar').show();
|
||||
});
|
||||
</script>
|
||||
{% endblock extrahead %}
|
||||
|
||||
{% block after_field_sets %}{{ block.super }}
|
||||
{% if original %}
|
||||
<a style="display: none" title="{% trans "Rejudge" %}" href="{% url 'admin:judge_submission_rejudge' original.pk %}"
|
||||
class="button rejudgelink">
|
||||
<i class="fa fa-lg fa-refresh"></i>
|
||||
<span class="text">{% trans "Rejudge" %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
313
templates/base.html
Normal file
313
templates/base.html
Normal file
|
@ -0,0 +1,313 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ LANGUAGE_CODE }}">
|
||||
<head>
|
||||
<title>{% block title %}{{ title }} - {{ SITE_LONG_NAME }}{% endblock %}</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
{% if misc_config.meta_keywords %}
|
||||
<meta name="keywords" content="{{ misc_config.meta_keywords }}">
|
||||
{% endif %}
|
||||
{% if meta_description %}
|
||||
<meta name="description" content="{{ meta_description }}">
|
||||
{% endif %}
|
||||
<meta id="viewport" name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- Favicons-->
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
|
||||
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/android-chrome-192x192.png" sizes="192x192">
|
||||
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<meta name="msapplication-TileColor" content="#FFBB33">
|
||||
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
|
||||
{# Chrome 39 for Android colour #}
|
||||
<meta name="theme-color" content="#FFBB33">
|
||||
{% if og_image %}
|
||||
<meta property="og:image" content="{{ request.build_absolute_uri(og_image) }}">
|
||||
{% endif %}
|
||||
{% block og_title %}{% endblock %}
|
||||
<meta property="og:site_name" content="{{ SITE_LONG_NAME }}">
|
||||
<meta property="og:url"
|
||||
content="{{ DMOJ_SCHEME }}://{{ DMOJ_CANONICAL|default(site.domain) }}{{ request.get_full_path() }}">
|
||||
{% if meta_description %}
|
||||
<meta property="og:description" content="{{ meta_description }}">
|
||||
{% endif %}
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script>window.bad_browser = true</script>
|
||||
<![endif]-->
|
||||
{% block meta %}{% endblock %}
|
||||
{% if not INLINE_FONTAWESOME %}
|
||||
<link rel="stylesheet" href="{{ FONTAWESOME_CSS }}">
|
||||
{% endif %}
|
||||
{% compress css %}
|
||||
<link rel="stylesheet" href="{{ static('style.css') }}">
|
||||
{% if PYGMENT_THEME %}
|
||||
<link rel="stylesheet" href="{{ static(PYGMENT_THEME) }}">
|
||||
{% endif %}{% if INLINE_FONTAWESOME %}
|
||||
<link rel="stylesheet" href="{{ static('libs/fontawesome/font-awesome.css') }}">{% endif %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ static('libs/featherlight/featherlight.min.css') }}">
|
||||
<link rel="stylesheet" type="text/css" href="{{ static('libs/clipboard/tooltip.css') }}">
|
||||
<link rel="stylesheet" type="text/css" href="{{ static('libs/select2/select2.css') }}">
|
||||
{% endcompress %}
|
||||
<link rel="canonical"
|
||||
href="{{ DMOJ_SCHEME }}://{{ DMOJ_CANONICAL|default(site.domain) }}{{ request.get_full_path() }}">
|
||||
{% if request.user.is_impersonate %}
|
||||
<style>
|
||||
#nav-container {
|
||||
background: #893e89 !important;
|
||||
}
|
||||
</style>
|
||||
{% endif %}
|
||||
{% block media %}{% endblock %}
|
||||
{% if not INLINE_JQUERY %}
|
||||
<script src="{{ JQUERY_JS }}"></script>
|
||||
{% endif %}
|
||||
|
||||
{% compress js %}
|
||||
<script>{{ inlinei18n(LANGUAGE_CODE)|safe }}</script>
|
||||
{% if INLINE_JQUERY %}
|
||||
<script src="{{ static('libs/jquery-3.4.1.min.js') }}"></script>
|
||||
{% endif %}
|
||||
<script src="{{ static('libs/jquery-cookie.js') }}"></script>
|
||||
<script src="{{ static('libs/jquery-taphold.js') }}"></script>
|
||||
<script src="{{ static('libs/jquery.unveil.js') }}"></script>
|
||||
<script src="{{ static('libs/moment.js') }}"></script>
|
||||
<script src="{{ static('libs/select2/select2.js') }}"></script>
|
||||
{% include "extra_js.html" %}
|
||||
<script src="{{ static('common.js') }}"></script>
|
||||
<script>
|
||||
moment.locale('{{ LANGUAGE_CODE }}');
|
||||
$(function () {
|
||||
$('img.unveil').unveil(200);
|
||||
});
|
||||
</script>
|
||||
{% endcompress %}
|
||||
{% block js_media %}{% endblock %}
|
||||
{% if request.in_contest %}
|
||||
<script>$(function () {
|
||||
count_down($("#contest-time-remaining"));
|
||||
|
||||
var selected = null,
|
||||
x_pos = 0, y_pos = 0,
|
||||
x_elem = 0, y_elem = 0;
|
||||
|
||||
$('#contest-info').mousedown(function () {
|
||||
selected = $(this);
|
||||
x_elem = x_pos - selected.offset().left;
|
||||
y_elem = y_pos - (selected.offset().top - $(window).scrollTop());
|
||||
return false;
|
||||
});
|
||||
|
||||
if (localStorage.getItem("contest_timer_pos")) {
|
||||
data = localStorage.getItem("contest_timer_pos").split(":");
|
||||
$("#contest-info").css({
|
||||
left: data[0],
|
||||
top: data[1]
|
||||
});
|
||||
}
|
||||
|
||||
$("#contest-info").show();
|
||||
|
||||
$(document).mousemove(function (e) {
|
||||
x_pos = e.screenX;
|
||||
y_pos = e.screenY;
|
||||
x_pos = Math.max(Math.min(x_pos, window.innerWidth), 0);
|
||||
y_pos = Math.max(Math.min(y_pos, window.innerHeight), 0);
|
||||
|
||||
if (selected !== null) {
|
||||
left_px = (x_pos - x_elem) + 'px';
|
||||
top_px = (y_pos - y_elem) + 'px';
|
||||
|
||||
localStorage.setItem("contest_timer_pos", left_px + ":" + top_px);
|
||||
|
||||
selected.css({
|
||||
left: left_px,
|
||||
top: top_px
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$(document).mouseup(function () {
|
||||
selected = null;
|
||||
})
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
<script>
|
||||
window.user = {
|
||||
email: '{{ request.user.email|escapejs }}',
|
||||
id: '{{ request.user.id|escapejs }}',
|
||||
name: '{{ request.user.username|escapejs }}'
|
||||
};
|
||||
</script>
|
||||
{% else %}
|
||||
<script>window.user = {};</script>
|
||||
{% endif %}
|
||||
|
||||
{% if misc_config.analytics %}
|
||||
{{ misc_config.analytics|safe }}
|
||||
{% endif %}
|
||||
|
||||
{# Don't run userscript since it may be malicious #}
|
||||
{% if request.user.is_authenticated and request.profile.user_script and not request.user.is_impersonate %}
|
||||
<script type="text/javascript">{{ request.profile.user_script|safe }}</script>
|
||||
{% endif %}
|
||||
|
||||
<noscript>
|
||||
<style>
|
||||
#content {
|
||||
margin: 80px auto auto;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
top: 27px;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
</head>
|
||||
<body>
|
||||
<svg width="0" height="0" style="display: block">
|
||||
<defs>
|
||||
<clipPath id="rating-clip"><circle cx="8" cy="8" r="7"/></clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
<nav id="navigation" class="unselectable">
|
||||
<div id="nav-container">
|
||||
<a id="navicon" href="javascript:void(0)"><i class="fa fa-bars"></i></a>
|
||||
<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>
|
||||
<li class="home-menu-item"><a href="{{ url('home') }}" class="nav-home">{{ _('Home') }}</a></li>
|
||||
{% for node in mptt_tree(nav_bar) recursive %}
|
||||
<li>
|
||||
<a href="{{ node.path }}" class="nav-{{ node.key }}{% if node.key in nav_tab %} active{% endif %}">
|
||||
{{ user_trans(node.label) }}
|
||||
{% if not node.is_leaf_node %}
|
||||
<div href="javascript:void(0)" class="nav-expand">></div>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% with children=node.get_children() %}
|
||||
{% if children %}<ul>{{ loop(children) }}</ul>{% endif %}
|
||||
{% endwith %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<span id="user-links">
|
||||
{% if request.user.is_authenticated %}
|
||||
<ul>
|
||||
<li>
|
||||
<a href="{{ url('user_page') }}">
|
||||
<span>
|
||||
<img src="{{ gravatar(request.user, 32) }}" height="24" width="24">{# -#}
|
||||
<span>
|
||||
{%- trans username=request.user.username -%}
|
||||
Hello, <b>{{ username }}</b>.
|
||||
{%- endtrans %}
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
<ul style="width: 150px">
|
||||
{% if request.user.is_staff or request.user.is_superuser %}
|
||||
<li><a href="{{ url('admin:index') }}">{{ _('Admin') }}</a></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ url('user_edit_profile') }}">{{ _('Edit profile') }}</a></li>
|
||||
{% if request.user.is_impersonate %}
|
||||
<li><a href="{{ url('impersonate-stop') }}">Stop impersonating</a></li>
|
||||
{% else %}
|
||||
<li>
|
||||
<form action="{{ url('auth_logout') }}" method="POST">
|
||||
{% csrf_token %}
|
||||
<button type="submit">{{ _('Log out') }}</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<span class="anon">
|
||||
<a href="{{ url('auth_login') }}?next={{ LOGIN_RETURN_PATH|urlencode }}"><b>{{ _('Log in') }}</b></a>
|
||||
{{ _('or') }}
|
||||
<a href="{{ url('registration_register') }}"><b>{{ _('Sign up') }}</b></a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div id="nav-shadow"></div>
|
||||
</nav>
|
||||
{% if request.in_contest %}
|
||||
<div id="contest-info">
|
||||
<a href="{{ url('contest_view', request.participation.contest.key) }}" style="vertical-align: middle">
|
||||
{{ request.participation.contest.name }} -
|
||||
{% if request.participation.spectate %}
|
||||
{{ _('spectating') }}
|
||||
{% elif request.participation.end_time %}
|
||||
<div id="contest-time-remaining" data-secs="{{ request.participation.time_remaining|seconds }}">
|
||||
{{ request.participation.time_remaining|timedelta("localized") }}
|
||||
</div>
|
||||
{% else %}
|
||||
{{ _('virtual') }}
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="page-container">
|
||||
<noscript>
|
||||
<div id="noscript">{{ _('This site works best with JavaScript enabled.') }}</div>
|
||||
</noscript>
|
||||
<br>
|
||||
<main id="content">
|
||||
{% block title_row %}
|
||||
<h2 style="color:#393630; display:inline">
|
||||
{% block content_title %}
|
||||
{% if content_title %}{{ content_title }}{% else %}{{ title }}{% endif %}
|
||||
{% endblock %}
|
||||
</h2>
|
||||
{% endblock %}
|
||||
{% block header %}{% endblock %}
|
||||
{% block title_ruler %}
|
||||
<hr>
|
||||
{% endblock %}
|
||||
<div id="content-body">{% block body %}{% endblock %}</div>
|
||||
</main>
|
||||
|
||||
{% if i18n_config.announcement %}
|
||||
<div id="announcement">{{ i18n_config.announcement|safe }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% block bodyend %}{% endblock %}
|
||||
|
||||
<footer>
|
||||
<span id="footer-content">
|
||||
<br>
|
||||
<a style="color: #808080" href="https://dmoj.ca">proudly powered by <b>DMOJ</b></a> |
|
||||
{% if i18n_config.footer %}
|
||||
{{ i18n_config.footer|safe }} |
|
||||
{% endif %}
|
||||
<form action="{{ url('set_language') }}" method="post" style="display: inline">
|
||||
{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{{ request.get_full_path() }}">
|
||||
<select name="language" onchange="form.submit()" style="height: 1.5em">
|
||||
{% for language in language_info_list(LANGUAGES) %}
|
||||
<option value="{{ language.code }}" {% if language.code == LANGUAGE_CODE %}selected{% endif %}>
|
||||
{{ language.name_local }} ({{ language.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
53
templates/blog/content.html
Normal file
53
templates/blog/content.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block js_media %}
|
||||
{% include "comments/media-js.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block media %}
|
||||
{% include "comments/media-css.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
{% if perms.judge.change_blogpost %}
|
||||
<div class="title-line-action">[<a href="{{ url('admin:judge_blogpost_change', post.id) }}">{{ _('Edit') }}</a>]
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="post-full">
|
||||
<div class="time">
|
||||
{% with authors=post.authors.all() %}
|
||||
{% if authors %}
|
||||
<span class="post-authors">{{ link_users(authors) }}</span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<span class="post-time">
|
||||
{% trans time=post.publish_on|date(_("N j, Y, g:i a")) %}
|
||||
posted on {{ time }}
|
||||
{% endtrans %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="body content-description">
|
||||
{% cache 86400 'post_content' post.id MATH_ENGINE %}
|
||||
{{ post.content|markdown('blog', MATH_ENGINE)|reference|str|safe}}
|
||||
{% endcache %}
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<span class="social">
|
||||
{{ post_to_gplus(request, post, '<i class="fa fa-google-plus-square"></i>') }}
|
||||
{{ post_to_facebook(request, post, '<i class="fa fa-facebook-official"></i>') }}
|
||||
{{ post_to_twitter(request, SITE_NAME + ':', post, '<i class="fa fa-twitter"></i>') }}
|
||||
</span>
|
||||
{% include "comments/list.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block bodyend %}
|
||||
{{ super() }}
|
||||
{% if REQUIRE_JAX %}
|
||||
{% include "mathjax-load.html" %}
|
||||
{% endif %}
|
||||
{% include "comments/math.html" %}
|
||||
{% endblock %}
|
34
templates/blog/dashboard.html
Normal file
34
templates/blog/dashboard.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
<div class="sidebox dashboard">
|
||||
<h3>Dashboard <i class="fa fa-dashboard"></i>
|
||||
</h3>
|
||||
<div class="sidebox-content">
|
||||
<div class="user-gravatar">
|
||||
<img src="{{ gravatar(request.user, 135) }}"
|
||||
alt="gravatar" width="135px" height="135px">
|
||||
</div>
|
||||
<div class="recently-attempted">
|
||||
<h4>Recently attempted problems</h4>
|
||||
<ul>
|
||||
{% for code, name, problem_points, user_points, s_date in recently_attempted_problems %}
|
||||
<li>
|
||||
<a href="{{ url('problem_detail', code) }}">{{ name }}</a>
|
||||
[<a href="{{ url('user_submissions', code, request.user.username }}">
|
||||
{{- user_points }}/{{ problem_points|floatformat }}</a>]
|
||||
|
||||
<span class="time">
|
||||
<span data-unix="{{ submission.date|utc|date("c") }}"
|
||||
class="recent-time moment-time-toggle">
|
||||
{% trans time=submission.date|date(_("N j, Y, g:i a")) %}
|
||||
on {{ time }}
|
||||
{% endtrans %}
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="recommended-problems">
|
||||
<h4>Recommended problems</h4><i>Coming soon.</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
268
templates/blog/list.html
Normal file
268
templates/blog/list.html
Normal file
|
@ -0,0 +1,268 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title_row %}{% endblock %}
|
||||
{% block title_ruler %}{% endblock %}
|
||||
|
||||
{% block media %}
|
||||
<link rel="alternate" type="application/atom+xml" href="{{ url('blog_atom') }}" title="Atom Blog Feed">
|
||||
<link rel="alternate" type="application/rss+xml" href="{{ url('blog_rss') }}" title="RSS Blog Feed">
|
||||
<link rel="alternate" type="application/atom+xml" href="{{ url('comment_atom') }}" title="Atom Comment Feed">
|
||||
<link rel="alternate" type="application/rss+xml" href="{{ url('comment_rss') }}" title="RSS Comment Feed">
|
||||
<link rel="alternate" type="application/atom+xml" href="{{ url('problem_atom') }}" title="Atom Problem Feed">
|
||||
<link rel="alternate" type="application/rss+xml" href="{{ url('problem_rss') }}" title="RSS Problem Feed">
|
||||
{% include "blog/media-css.html" %}
|
||||
<style>
|
||||
.post {
|
||||
margin: 0 1.4em;
|
||||
}
|
||||
|
||||
.post:first-child {
|
||||
margin-top: 0.6em;
|
||||
}
|
||||
|
||||
.comment-count {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.comment-icon {
|
||||
padding: 0.1em 0.2em 0 0.5em;
|
||||
}
|
||||
|
||||
.comment-count-link {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.own-open-tickets .title a, .open-tickets .title a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.own-open-tickets .object, .open-tickets .object {
|
||||
margin-left: 1em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.open-tickets .user {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.no-clarifications-message {
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block js_media %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.time-remaining').each(function () {
|
||||
count_down($(this));
|
||||
});
|
||||
|
||||
$('.blog-sidebar').hide();
|
||||
$('#blog-tab').find('a').click(function (e) {
|
||||
e.preventDefault();
|
||||
$('#blog-tab').addClass('active');
|
||||
$('#event-tab').removeClass('active');
|
||||
$('.blog-content').show();
|
||||
$('.blog-sidebar').hide();
|
||||
});
|
||||
$('#event-tab').find('a').click(function (e) {
|
||||
e.preventDefault();
|
||||
$('#event-tab').addClass('active');
|
||||
$('#blog-tab').removeClass('active');
|
||||
$('.blog-content').hide();
|
||||
$('.blog-sidebar').show();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% block before_posts %}{% endblock %}
|
||||
<div id="mobile" class="tabs">
|
||||
<ul>
|
||||
<li id="blog-tab" class="tab active"><a href="#">
|
||||
<i class="tab-icon fa fa-info-circle"></i> {{ _('Blog') }}
|
||||
</a></li>
|
||||
<li id="event-tab" class="tab"><a href="#"><i class="tab-icon fa fa-rss"></i> {{ _('Events') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="blog-container">
|
||||
<div class="blog-content sidebox">
|
||||
<h3>{{ _('News') }} <i class="fa fa-terminal"></i></h3>
|
||||
<div class="sidebox-content">
|
||||
{% for post in posts %}
|
||||
<section class="{% if post.sticky %}sticky {% endif %}post">
|
||||
<h2 class="title">
|
||||
<a href="{{ url('blog_post', post.id, post.slug) }}">{{ post.title }}</a>
|
||||
</h2>
|
||||
<span class="time">
|
||||
{%- if post.sticky %}<i title="Sticky" class="fa fa-star fa-fw"></i>{% endif -%}
|
||||
{% with authors=post.authors.all() %}
|
||||
{%- if authors -%}
|
||||
<span class="post-authors">{{ link_users(authors) }}</span>
|
||||
{%- endif -%}
|
||||
{% endwith %}
|
||||
{{ relative_time(post.publish_on, abs=_('posted on {time}'), rel=_('posted {time}')) -}}
|
||||
</span><span class="comment-data">
|
||||
<a href="{{ url('blog_post', post.id, post.slug) }}#comments" class="comment-count-link">
|
||||
<i class="fa fa-comments comment-icon"></i><span class="comment-count">
|
||||
{{- post_comment_counts[post.id] or 0 -}}
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<div class="summary content-description">
|
||||
{% cache 86400 'post_summary' post.id %}
|
||||
{{ post.summary|default(post.content, true)|markdown('blog', 'svg', lazy_load=True)|reference|str|safe }}
|
||||
{% endcache %}
|
||||
</div>
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if page_obj.num_pages > 1 %}
|
||||
<div style="margin-bottom:10px;margin-top:10px">{% include "list-pages.html" %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="blog-sidebar">
|
||||
{% if request.in_contest and request.participation.contest.use_clarifications %}
|
||||
<div class="blog-sidebox sidebox">
|
||||
<h3>{{ _('Clarifications') }} <i class="fa fa-question-circle"></i></h3>
|
||||
<div class="sidebox-content">
|
||||
{% if has_clarifications %}
|
||||
<ul>
|
||||
{% for clarification in clarifications %}
|
||||
<li class="clarification">
|
||||
<a href="{{ url('problem_detail', clarification.problem.code) }}"
|
||||
class="problem">
|
||||
{{ clarification.problem.name }}
|
||||
</a>
|
||||
<span class="time">{{ relative_time(clarification.date) }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="no-clarifications-message">
|
||||
{{ _('No clarifications have been made at this time.') }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if current_contests %}
|
||||
<div class="blog-sidebox sidebox">
|
||||
<h3>{{ _('Ongoing contests') }} <i class="fa fa-trophy"></i></h3>
|
||||
<div class="sidebox-content">
|
||||
{% for contest in current_contests %}
|
||||
<div class="contest">
|
||||
<div class="contest-list-title">
|
||||
<a href="{{ url('contest_view', contest.key) }}">{{ contest.name }}</a>
|
||||
</div>
|
||||
<div class="time">
|
||||
{{ _('Ends in %(countdown)s.', countdown=contest.time_before_end|as_countdown) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if future_contests %}
|
||||
<div class="blog-sidebox sidebox">
|
||||
<h3>{{ _('Upcoming contests') }} <i class="fa fa-trophy"></i></h3>
|
||||
<div class="sidebox-content">
|
||||
{% for contest in future_contests %}
|
||||
<div class="contest">
|
||||
<div class="contest-list-title">
|
||||
<a href="{{ url('contest_view', contest.key) }}">{{ contest.name }}</a>
|
||||
</div>
|
||||
<div class="time">
|
||||
{{ _('Starting in %(countdown)s.', countdown=contest.time_before_start|as_countdown) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="blog-sidebox sidebox">
|
||||
<h3>{{ _('Comment stream') }} <i class="fa fa-comments"></i></h3>
|
||||
<div class="sidebox-content">
|
||||
<ul>
|
||||
{% for comment in comments %}
|
||||
<li>
|
||||
<span style="padding-left:0.25em" class="poster">
|
||||
{{ link_user(comment.author) }}
|
||||
</span> →
|
||||
<a href="{{ comment.link }}#comment-{{ comment.id }}">{{ page_titles[comment.page] }}</a>
|
||||
</li>{% endfor %}
|
||||
</ul>
|
||||
<span class="rssatom">
|
||||
<a href="{{ url('comment_rss') }}"><span><i class="fa fa-rss"></i></span> RSS</a>
|
||||
/
|
||||
<a href="{{ url('comment_atom') }}">Atom</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="blog-sidebox sidebox">
|
||||
<h3>{{ _('New problems') }} <i class="fa fa-puzzle-piece"></i>
|
||||
</h3>
|
||||
<div class="sidebox-content">
|
||||
<ul class="problem-list">
|
||||
{% for problem in new_problems %}
|
||||
<li><a href="{{ url('problem_detail', problem.code) }}">{{ problem.name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<span class="rssatom">
|
||||
<a href="{{ url('problem_rss') }}"><span><i class="fa fa-rss"></i></span> RSS</a>
|
||||
/
|
||||
<a href="{{ url('problem_atom') }}">Atom</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% if perms.judge.test_site and own_open_tickets %}
|
||||
<div class="blog-sidebox sidebox">
|
||||
<h3>{{ _('My open tickets') }} <i class="fa fa-question-circle"></i></h3>
|
||||
<div class="sidebox-content">
|
||||
<ul class="own-open-tickets">
|
||||
{% for ticket in own_open_tickets %}
|
||||
<li>
|
||||
<div class="title">
|
||||
<a href="{{ url('ticket', ticket.id) }}">{{ ticket.title }}</a>
|
||||
</div>
|
||||
<div class="object">
|
||||
<a href="{{ ticket.linked_item.get_absolute_url() }}">
|
||||
{{ ticket.linked_item|item_title }}</a>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.judge.test_site and open_tickets %}
|
||||
<div class="blog-sidebox sidebox">
|
||||
<h3>{{ _('New tickets') }} <i class="fa fa-exclamation-circle"></i></h3>
|
||||
<div class="sidebox-content">
|
||||
<ul class="open-tickets">
|
||||
{% for ticket in open_tickets %}
|
||||
<li>
|
||||
<div class="title">
|
||||
<a href="{{ url('ticket', ticket.id) }}">{{ ticket.title }}</a>
|
||||
</div>
|
||||
<div class="object">
|
||||
<a href="{{ ticket.linked_item.get_absolute_url() }}">
|
||||
{{ ticket.linked_item|item_title }}</a>
|
||||
</div>
|
||||
<div>{{ link_user(ticket.user) }}</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% block after_posts %}{% endblock %}
|
||||
{% endblock %}
|
30
templates/blog/media-css.html
Normal file
30
templates/blog/media-css.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
<style>
|
||||
.user-gravatar {
|
||||
display: inline-block;
|
||||
padding-right: 15px;
|
||||
padding-top: 8px;
|
||||
width: 135px;
|
||||
}
|
||||
|
||||
.user-gravatar img {
|
||||
width: 135px;
|
||||
height: 135px;
|
||||
display: block;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.recently-attempted, .recommended-problems {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.recently-attempted ul {
|
||||
list-style: none;
|
||||
padding-left: 1em;
|
||||
padding-right: .5em;
|
||||
margin: 0.2em;
|
||||
}
|
||||
|
||||
.recently-attempted h4, .recommended-problems h4 {
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
1
templates/blog/preview.html
Normal file
1
templates/blog/preview.html
Normal file
|
@ -0,0 +1 @@
|
|||
{{ preview_data|markdown('blog', MATH_ENGINE)|reference|str|safe }}
|
1
templates/comments/content.html
Normal file
1
templates/comments/content.html
Normal file
|
@ -0,0 +1 @@
|
|||
{{ comment.body|markdown('comment', MATH_ENGINE)|reference|str|safe }}
|
17
templates/comments/edit-ajax.html
Normal file
17
templates/comments/edit-ajax.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<div class="comment-submit">
|
||||
<form id="comment-edit" action="{{ request.get_full_path() }}" method="post">
|
||||
<span style="display: none" class="comment-id">{{ comment.id }}</span>
|
||||
<span style="display: none" class="read-back">{{ url('comment_content', comment.id) }}</span>
|
||||
{% csrf_token %}
|
||||
<div class="form-errors">
|
||||
{{ form.non_field_errors() }}
|
||||
{{ form.body.errors }}
|
||||
|
||||
</div>
|
||||
<div class="comment-post-wrapper">
|
||||
<div id="comment-form-body">{{ form.body }}</div>
|
||||
</div>
|
||||
<hr>
|
||||
<input style="float: right" type="submit" value="Post!" class="button">
|
||||
</form>
|
||||
</div>
|
13
templates/comments/edit.html
Normal file
13
templates/comments/edit.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
{% block media %}
|
||||
{% compress css %}
|
||||
{{ form.media.css }}
|
||||
{% endcompress %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js_media %}
|
||||
{{ form.media.js }}
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div class="form-area">{% include "comments/edit-ajax.html" %}</div>
|
||||
{% endblock %}
|
159
templates/comments/list.html
Normal file
159
templates/comments/list.html
Normal file
|
@ -0,0 +1,159 @@
|
|||
<div id="comments" class="comment-area">
|
||||
<h2><i style="padding-right: 0.3em" class="fa fa-comments"></i>{{ _('Comments') }}</h2>
|
||||
{% if has_comments %}
|
||||
<ul class="comments top-level-comments new-comments">
|
||||
{% set logged_in = request.user.is_authenticated %}
|
||||
{% set profile = request.profile if logged_in else None %}
|
||||
{% for node in mptt_tree(comment_list) recursive %}
|
||||
<li id="comment-{{ node.id }}" data-revision="{{ node.revisions - 1 }}"
|
||||
data-max-revision="{{ node.revisions - 1 }}"
|
||||
data-revision-ajax="{{ url('comment_revision_ajax', node.id) }}" class="comment">
|
||||
<div class="comment-display{% if node.score <= vote_hide_threshold %} bad-comment{% endif %}">
|
||||
<div class="info">
|
||||
<div class="vote">
|
||||
{% if logged_in %}
|
||||
<a href="javascript:comment_upvote({{ node.id }})"
|
||||
class="upvote-link fa fa-chevron-up fa-fw{% if node.vote_score == 1 %} voted{% endif %}"></a>
|
||||
{% else %}
|
||||
<a href="javascript:alert('{{ _('Please login to vote')|escapejs }}')" title="{{ _('Please login to vote') }}"
|
||||
class="upvote-link fa fa-chevron-up fa-fw"></a>
|
||||
{% endif %}
|
||||
<br>
|
||||
<div class="comment-score">{{ node.score }}</div>
|
||||
{% if logged_in %}
|
||||
<a href="javascript:comment_downvote({{ node.id }})"
|
||||
class="downvote-link fa fa-chevron-down fa-fw{% if node.vote_score == -1 %} voted{% endif %}"></a>
|
||||
{% else %}
|
||||
<a href="javascript:alert('{{ _('Please login to vote')|escapejs }}')" title="{{ _('Please login to vote') }}"
|
||||
class="downvote-link fa fa-chevron-down fa-fw"></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% with author=node.author, user=node.author.user %}
|
||||
<a href="{{ url('user_page', user.username) }}" class="user">
|
||||
<img src="{{ gravatar(author, 135) }}" class="gravatar">
|
||||
</a>
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="header">
|
||||
{{ link_user(node.author) }}
|
||||
{{ relative_time(node.time, abs=_('commented on {time}'), rel=_('commented {time}')) }}
|
||||
<span class="comment-spacer"></span>
|
||||
<span class="comment-operation">
|
||||
{% if node.revisions > 1 %}
|
||||
<span class="comment-edits">
|
||||
<a href="javascript:show_revision({{ node.id }}, -1)"
|
||||
class="previous-revision">←</a>
|
||||
<span class="comment-edit-text">
|
||||
{% if node.revisions > 2 %}
|
||||
{% trans edits=node.revisions - 1 %}edit {{ edits }}{% endtrans %}
|
||||
{% else %}
|
||||
{{ _('edited') }}
|
||||
{% endif %}
|
||||
</span>
|
||||
<a href="javascript:show_revision({{ node.id }}, 1)" style="visibility: hidden"
|
||||
class="next-revision">→</a>
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="comment-edits"></span>
|
||||
{% endif %}
|
||||
<a href="#comment-{{ node.id }}" title="{{ _('Link') }}" class="comment-link">
|
||||
<i class="fa fa-link fa-fw"></i>
|
||||
</a>
|
||||
{% if logged_in and not comment_lock %}
|
||||
{% set can_edit = node.author.id == profile.id and not profile.mute %}
|
||||
{% if can_edit %}
|
||||
<a data-featherlight="{{ url('comment_edit_ajax', node.id) }}"
|
||||
href="{{ url('comment_edit', node.id) }}"
|
||||
title="{{ _('Edit') }}" class="edit-link">
|
||||
<i class="fa fa-pencil fa-fw"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="javascript:comment_set_parent({{ node.id }})"
|
||||
title="{{ _('Reply') }}">
|
||||
<i class="fa fa-reply fa-fw"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if perms.judge.change_comment %}
|
||||
{% if can_edit %}
|
||||
<a href="javascript:comment_set_parent({{ node.id }})"
|
||||
title="{{ _('Reply') }}"><i class="fa fa-reply fa-fw"></i></a>
|
||||
{% else %}
|
||||
<a data-featherlight="{{ url('comment_edit_ajax', node.id) }}"
|
||||
href="{{ url('comment_edit', node.id) }}" title="{{ _('Edit') }}"
|
||||
class="edit-link"><i class="fa fa-pencil fa-fw"></i></a>
|
||||
{% endif %}
|
||||
<a href="javascript:" title="{{ _('Hide') }}" data-id="{{ node.id }}"
|
||||
class="hide-comment"><i class="fa fa-trash fa-fw"></i></a>
|
||||
<a href="{{ url('admin:judge_comment_change', node.id) }}"
|
||||
title="{{ _('Admin') }}"><i class="fa fa-cog fa-fw"></i></a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="content content-description">
|
||||
<div class="comment-body"{% if node.score <= vote_hide_threshold %} style="display:none"{% endif %}>
|
||||
{{ node.body|markdown('comment', MATH_ENGINE, True)|reference|str|safe }}
|
||||
</div>
|
||||
{% if node.score <= vote_hide_threshold %}
|
||||
<div class="comment-body bad-comment-body">
|
||||
<p>
|
||||
{% trans id=node.id %}
|
||||
This comment is hidden due to too much negative feedback.
|
||||
Click <a href="javascript:comment_show_content({{ id }})">here</a> to view it.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% with children=node.get_children() %}
|
||||
{% if children %}
|
||||
<ul id="comment-{{ node.id }}-children" class="comments">{{ loop(children) }}</ul>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% elif not comment_lock %}
|
||||
<p class="no-comments-message">{{ _('There are no comments at the moment.') }}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if request.user.is_authenticated and comment_form and not comment_lock %}
|
||||
<div class="form-area comment-submit">
|
||||
{% block comment_submit_title %}
|
||||
<h3>{{ _('New comment') }}</h3>
|
||||
<hr>
|
||||
{% endblock %}
|
||||
{% if is_new_user %}
|
||||
<div style="margin-bottom: 0" class="alert alert-info">
|
||||
{{ _('You need to have solved at least one problem before your voice can be heard.') }}
|
||||
</div>
|
||||
{% else %}
|
||||
<form id="comment-submit" action="" method="post">
|
||||
{% csrf_token %}
|
||||
{% if comment_form.errors %}
|
||||
<div id="form-errors">
|
||||
{{ comment_form.non_field_errors() }}
|
||||
{{ comment_form.parent.errors }}
|
||||
{% if comment_form.body.errors %}{{ _('Invalid comment body.') }}{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ comment_form.parent }}
|
||||
<div class="comment-post-wrapper">
|
||||
<div id="comment-form-body">{{ comment_form.body }}</div>
|
||||
</div>
|
||||
<hr>
|
||||
<input style="float:right" type="submit" value="{{ _('Post!') }}" class="button">
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if comment_lock %}
|
||||
<div class="alert alert-warning comment-lock">
|
||||
{{ _('Comments are disabled on this page.') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
3
templates/comments/math.html
Normal file
3
templates/comments/math.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
{% compress js, inline %}
|
||||
<script src="{{ static('pagedown_math.js') }}"></script>
|
||||
{% endcompress %}
|
103
templates/comments/media-css.html
Normal file
103
templates/comments/media-css.html
Normal file
|
@ -0,0 +1,103 @@
|
|||
{% compress css %}
|
||||
{{ comment_form.media.css }}
|
||||
<style media="all">
|
||||
.featherlight {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.revision-text p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.revision-text p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.featherlight-edit .featherlight-content {
|
||||
background: #FAFAFA;
|
||||
padding: 10px 15px 10px 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #CCC;
|
||||
}
|
||||
|
||||
.new-comments .comment-display {
|
||||
display: flex;
|
||||
margin-top: -0.25em !important;
|
||||
padding-left: 1em;
|
||||
padding-top: 0.5em !important;
|
||||
border: 1px solid #ccc;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.new-comments .comment .detail {
|
||||
margin: 0px 15px 0px;
|
||||
width: 100%;
|
||||
max-width: calc(100% - 134px);
|
||||
}
|
||||
|
||||
.new-comments .comment-edits {
|
||||
padding-right: 0.75em;
|
||||
}
|
||||
|
||||
.new-comments .comment .detail .header {
|
||||
display: flex;
|
||||
padding: 2px 0px;
|
||||
font-weight: normal;
|
||||
border-bottom: 1px #888 solid;
|
||||
color: #888;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.previous-revision, .next-revision {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.new-comments .header i {
|
||||
color: #888 !important;
|
||||
}
|
||||
|
||||
.new-comments .info {
|
||||
padding-top: 0.4em;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.new-comments .gravatar {
|
||||
width: 75px;
|
||||
max-width: 75px;
|
||||
}
|
||||
|
||||
.new-comments .vote {
|
||||
margin-right: 1em;
|
||||
height: 75px;
|
||||
padding-top: 0.4em;
|
||||
}
|
||||
|
||||
.new-comments .comment-display {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.new-comments .comment:target > .comment-display {
|
||||
border: 1px solid #2980b9;
|
||||
border-left: 10px solid #2980b9;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.comments.top-level-comments {
|
||||
margin-bottom: -3px;
|
||||
}
|
||||
|
||||
.bad-comment {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.bad-comment:hover {
|
||||
opacity: 1;
|
||||
/* This is necessary to prevent random flickering */
|
||||
-webkit-transform: translatez(0)
|
||||
-moz-transform: translatez(0);
|
||||
-ms-transform: translatez(0);
|
||||
-o-transform: translatez(0);
|
||||
transform: translatez(0);
|
||||
}
|
||||
</style>
|
||||
{% endcompress %}
|
206
templates/comments/media-js.html
Normal file
206
templates/comments/media-js.html
Normal file
|
@ -0,0 +1,206 @@
|
|||
<script src="{{ static('libs/featherlight/featherlight.min.js') }}" type="text/javascript"></script>
|
||||
{% compress js %}
|
||||
{{ comment_form.media.js }}
|
||||
{% if not REQUIRE_JAX %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$('#id_body').keypress(function () {
|
||||
if (!("MathJax" in window)) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: '{{ static('mathjax_config.js') }}',
|
||||
dataType: "script",
|
||||
cache: true,
|
||||
success: function () {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/config=TeX-AMS_HTML',
|
||||
dataType: "script",
|
||||
cache: true,
|
||||
success: function () {
|
||||
mathjax_pagedown($);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
window.comment_set_parent = function (parent) {
|
||||
$('form#comment-submit input#id_parent').val(parent);
|
||||
$('html, body').animate({
|
||||
scrollTop: $('.form-area.comment-submit').offset().top - $('#navigation').height() - 4
|
||||
}, 500);
|
||||
};
|
||||
|
||||
function update_math($comment) {
|
||||
if ('MathJax' in window) {
|
||||
var $body = $comment.find('.comment-body');
|
||||
MathJax.Hub.Queue(['Typeset', MathJax.Hub, $body[0]], function () {
|
||||
$body.find('.tex-image').hide();
|
||||
$body.find('.tex-text').show();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.show_revision = function (comment_id, offset) {
|
||||
var $comment = $("#comment-" + comment_id);
|
||||
|
||||
// If .comment-body is hidden, then this is a bad comment that the user has not clicked
|
||||
// Thus the revision retrieval should do nothing
|
||||
if (!$comment.find('.comment-body').is(':visible'))
|
||||
return;
|
||||
|
||||
var cur_revision = parseInt($comment.attr("data-revision"));
|
||||
var max_revision = parseInt($comment.attr("data-max-revision"));
|
||||
var revision_ajax = $comment.attr("data-revision-ajax");
|
||||
var show_revision = cur_revision + offset;
|
||||
|
||||
$comment.attr("data-revision", show_revision);
|
||||
|
||||
$.get(revision_ajax, {
|
||||
revision: show_revision
|
||||
}).done(function (body) {
|
||||
$comment.find('.previous-revision').css({visibility: show_revision == 0 ? 'hidden' : ''});
|
||||
$comment.find('.next-revision').css({visibility: show_revision == max_revision ? 'hidden' : ''});
|
||||
$comment.find('.content').html(body);
|
||||
|
||||
var edit_text = '{{ _('edit {edits}') }}'.replace("{edits}", show_revision);
|
||||
|
||||
if (show_revision == 0) {
|
||||
edit_text = '{{ _('original') }}';
|
||||
} else if (show_revision == max_revision && max_revision == 1) {
|
||||
edit_text = '{{ _('edited') }}';
|
||||
}
|
||||
|
||||
$comment.find('.comment-edit-text').text(' ' + edit_text + ' ');
|
||||
update_math($comment);
|
||||
});
|
||||
};
|
||||
|
||||
function ajax_vote(url, id, delta, on_success) {
|
||||
return $.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
id: id
|
||||
},
|
||||
success: function (data, textStatus, jqXHR) {
|
||||
var score = $('#comment-' + id + ' .comment-score').first();
|
||||
score.text(parseInt(score.text()) + delta);
|
||||
if (typeof on_success !== 'undefined')
|
||||
on_success();
|
||||
},
|
||||
error: function (data, textStatus, jqXHR) {
|
||||
alert('Could not vote: ' + data.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var get_$votes = function (id) {
|
||||
var $comment = $('#comment-' + id);
|
||||
return {
|
||||
upvote: $comment.find('.upvote-link').first(),
|
||||
downvote: $comment.find('.downvote-link').first()
|
||||
};
|
||||
};
|
||||
|
||||
window.comment_upvote = function (id) {
|
||||
ajax_vote('{{ url('comment_upvote') }}', id, 1, function () {
|
||||
var $votes = get_$votes(id);
|
||||
if ($votes.downvote.hasClass('voted'))
|
||||
$votes.downvote.removeClass('voted');
|
||||
else
|
||||
$votes.upvote.addClass('voted');
|
||||
});
|
||||
};
|
||||
|
||||
window.comment_downvote = function (id) {
|
||||
ajax_vote('{{ url('comment_downvote') }}', id, -1, function () {
|
||||
var $votes = get_$votes(id);
|
||||
if ($votes.upvote.hasClass('voted'))
|
||||
$votes.upvote.removeClass('voted');
|
||||
else
|
||||
$votes.downvote.addClass('voted');
|
||||
});
|
||||
};
|
||||
|
||||
var $comments = $('.comments');
|
||||
$comments.find('a.hide-comment').click(function (e) {
|
||||
e.preventDefault();
|
||||
if (!(e.ctrlKey || e.metaKey || confirm('Are you sure you want to hide this comment?')))
|
||||
return;
|
||||
|
||||
var id = $(this).attr('data-id');
|
||||
$.post('{{ url('comment_hide') }}', {id: id}).then(function () {
|
||||
$('#comment-' + id).remove();
|
||||
$('#comment-' + id + '-children').remove();
|
||||
}).catch(function () {
|
||||
alert('Failed.');
|
||||
});
|
||||
});
|
||||
|
||||
$comments.find('a.edit-link').featherlight({
|
||||
afterOpen: function () {
|
||||
if ('DjangoPagedown' in window) {
|
||||
var $wmd = $('.featherlight .wmd-input');
|
||||
if ($wmd.length) {
|
||||
window.DjangoPagedown.createEditor($wmd.get(0));
|
||||
if ('MathJax' in window) {
|
||||
var preview = $('.featherlight div.wmd-preview')[0];
|
||||
window.editors[$wmd.attr('id')].hooks.chain('onPreviewRefresh', function () {
|
||||
MathJax.Hub.Queue(['Typeset', MathJax.Hub, preview]);
|
||||
});
|
||||
MathJax.Hub.Queue(['Typeset', MathJax.Hub, preview]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$('#comment-edit').submit(function (event) {
|
||||
event.preventDefault();
|
||||
var id = $('#comment-edit').find('.comment-id').text();
|
||||
var readback = $('#comment-edit').find('.read-back').text();
|
||||
$.post($(this).attr('action'), $(this).serialize()).done(function (data) {
|
||||
$.featherlight.current().close();
|
||||
$.ajax({
|
||||
url: readback
|
||||
}).done(function (data) {
|
||||
var $comment = $('#comment-' + id);
|
||||
var $area = $comment.find('.comment-body').first();
|
||||
$area.html(data);
|
||||
update_math($comment);
|
||||
var $edits = $comment.find('.comment-edits').first();
|
||||
$edits.text('updated');
|
||||
}).fail(function () {
|
||||
console.log('Failed to update comment:' + id);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
variant: 'featherlight-edit'
|
||||
});
|
||||
|
||||
var $root = $('html, body');
|
||||
$comments.find('a.comment-link').click(function () {
|
||||
var href = $.attr(this, 'href');
|
||||
$root.animate({
|
||||
scrollTop: $(href).offset().top
|
||||
}, 500, function () {
|
||||
window.location.hash = href;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('img.unveil').unveil(200);
|
||||
|
||||
window.comment_show_content = function (comment_id) {
|
||||
var $comment = $('#comment-' + comment_id);
|
||||
$comment.find('.comment-body').show();
|
||||
$comment.find('.bad-comment-body').hide();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
{% endcompress %}
|
4
templates/comments/preview.html
Normal file
4
templates/comments/preview.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
{{ preview_data|markdown('comment', MATH_ENGINE)|reference|str|safe }}
|
||||
{% if REQUIRE_JAX %}
|
||||
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
||||
{% endif %}
|
3
templates/comments/revision-ajax.html
Normal file
3
templates/comments/revision-ajax.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
{% with node=revision.field_dict %}
|
||||
<div class="comment-body">{{ node.body|markdown('comment', MATH_ENGINE)|reference|str|safe }}</div>
|
||||
{% endwith %}
|
17
templates/comments/votes.html
Normal file
17
templates/comments/votes.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<h2>Votes</h2>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Voter</th>
|
||||
<th>Score</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for vote in votes %}
|
||||
<tr>
|
||||
<td>{{ link_user(vote.voter) }}</td>
|
||||
<td>{{ vote.score }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
81
templates/common-content.html
Normal file
81
templates/common-content.html
Normal file
|
@ -0,0 +1,81 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block media %}
|
||||
{% block content_media %}{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js_media %}
|
||||
{% compress js %}
|
||||
<script src="{{ static('libs/clipboard/clipboard.js') }}"></script>
|
||||
<script src="{{ static('libs/clipboard/tooltip.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
var info_float = $('.info-float');
|
||||
if (info_float.length) {
|
||||
var container = $('#content-right');
|
||||
if (window.bad_browser) {
|
||||
container.css('float', 'right');
|
||||
} else if (!featureTest('position', 'sticky')) {
|
||||
fix_div(info_float, 55);
|
||||
$(window).resize(function () {
|
||||
info_float.width(container.width());
|
||||
});
|
||||
info_float.width(container.width());
|
||||
}
|
||||
}
|
||||
|
||||
var copyButton;
|
||||
$('pre code').each(function () {
|
||||
$(this).parent().before($('<div>', {'class': 'copy-clipboard'})
|
||||
.append(copyButton = $('<span>', {
|
||||
'class': 'btn-clipboard',
|
||||
'data-clipboard-text': $(this).text(),
|
||||
'title': 'Click to copy'
|
||||
}).text('Copy')));
|
||||
|
||||
$(copyButton.get(0)).mouseleave(function () {
|
||||
$(this).attr('class', 'btn-clipboard');
|
||||
$(this).removeAttr('aria-label');
|
||||
});
|
||||
|
||||
var curClipboard = new Clipboard(copyButton.get(0));
|
||||
|
||||
curClipboard.on('success', function (e) {
|
||||
e.clearSelection();
|
||||
showTooltip(e.trigger, 'Copied!');
|
||||
});
|
||||
|
||||
curClipboard.on('error', function (e) {
|
||||
showTooltip(e.trigger, fallbackMessage(e.action));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endcompress %}
|
||||
{% block content_js_media %}{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div id="common-content">
|
||||
<div id="content-right">
|
||||
<div class="info-float">{% block info_float %}{% endblock %}</div>
|
||||
</div>
|
||||
<div id="content-left" class="split-common-content">
|
||||
<div class="content-description screen">
|
||||
{% block description %}{% endblock %}
|
||||
{% block description_end %}
|
||||
<hr>
|
||||
{% endblock %}
|
||||
{% block post_description_end %}{% endblock %}
|
||||
</div>
|
||||
{% block comments %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block bodyend %}
|
||||
{% if REQUIRE_JAX %}
|
||||
{% include "mathjax-load.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
35
templates/contest/access_code.html
Normal file
35
templates/contest/access_code.html
Normal file
|
@ -0,0 +1,35 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block media %}
|
||||
<style>
|
||||
#access-code-form {
|
||||
margin: 50px auto 0;
|
||||
display: block;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
#id_access_code {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button-line {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form id="access-code-form" action="" method="post" class="form-area">
|
||||
{% csrf_token %}
|
||||
{% if form.errors or wrong_code %}
|
||||
<div id="form-errors">
|
||||
<p class="error">{{ _('Invalid access code.') }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
<p>{{ _('Please enter your access code:') }}</p>
|
||||
<p>{{ form.access_code }}</p>
|
||||
<p class="button-line">
|
||||
<button type="submit">{{ _('Join Contest') }}</button>
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
46
templates/contest/calendar.html
Normal file
46
templates/contest/calendar.html
Normal file
|
@ -0,0 +1,46 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title_ruler %}{% endblock %}
|
||||
|
||||
{% block title_row %}
|
||||
{% set tab = 'calendar' %}
|
||||
{% include "contest/contest-list-tabs.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<table id="contest-calendar">
|
||||
<tr>
|
||||
<th>{{ _('Sunday') }}</th>
|
||||
<th>{{ _('Monday') }}</th>
|
||||
<th>{{ _('Tuesday') }}</th>
|
||||
<th>{{ _('Wednesday') }}</th>
|
||||
<th>{{ _('Thursday') }}</th>
|
||||
<th>{{ _('Friday') }}</th>
|
||||
<th>{{ _('Saturday') }}</th>
|
||||
</tr>
|
||||
{% for week in calendar %}
|
||||
<tr>{% for day in week %}
|
||||
<td class="{{ day.weekday }}{% if day.is_today %} today{% endif %}{% if day.is_pad %} noday{% endif %}">
|
||||
<span class="num">{{ day.date.day }}</span>
|
||||
<ul class="fa-ul">
|
||||
{% for contest in day.starts %}
|
||||
<li class="start"><i class="fa fa-li fa-lg fa-step-forward"></i>
|
||||
<a href="{{ url('contest_view', contest.key) }}">{{ contest.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% for contest in day.oneday %}
|
||||
<li class="oneday">
|
||||
<i class="fa fa-li fa-lg fa-play"></i>
|
||||
<a href="{{ url('contest_view', contest.key) }}">{{ contest.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% for contest in day.ends %}
|
||||
<li class="end"><i class="fa fa-li fa-lg fa-step-backward"></i>
|
||||
<a href="{{ url('contest_view', contest.key) }}">{{ contest.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
{% endfor %}</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
42
templates/contest/clone.html
Normal file
42
templates/contest/clone.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block media %}
|
||||
<style>
|
||||
#contest-clone-panel {
|
||||
position: relative;
|
||||
margin: 5em auto auto -10em;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
#contest-key-container {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
#id_key {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ul.errorlist {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form id="contest-clone-panel" action="" method="post" class="form-area">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
<div id="form-errors">
|
||||
{{ form.key.errors }}
|
||||
</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>
|
||||
<hr>
|
||||
<button style="float: right;" type="submit">{{ _('Clone!') }}</button>
|
||||
</form>
|
||||
{% endblock %}
|
26
templates/contest/contest-list-tabs.html
Normal file
26
templates/contest/contest-list-tabs.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
{% extends "tabs-base.html" %}
|
||||
|
||||
{% block post_tab_spacer %}
|
||||
{% if tab == 'calendar' %}
|
||||
<div style="font-size: 1.6em; margin-top: 0.3em">
|
||||
{% if prev_month %}
|
||||
<a href="{{ url('contest_calendar', prev_month.year, prev_month.month) }}">« {{ _('Prev') }}</a>
|
||||
{% endif %}
|
||||
{% if not (curr_month.year == now.year and curr_month.month == now.month) %}
|
||||
<a href="{{ url('contest_calendar', now.year, now.month) }}"> {{ _('Today') }}</a>
|
||||
{% endif %}
|
||||
{% if next_month %}
|
||||
<a href="{{ url('contest_calendar', next_month.year, next_month.month) }}">{{ _('Next') }} »</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<span class="spacer"></span>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block tabs %}
|
||||
{{ make_tab('list', 'fa-list', url('contest_list'), _('List')) }}
|
||||
{{ make_tab('calendar', 'fa-calendar', url('contest_calendar', now.year, now.month), _('Calendar')) }}
|
||||
{% if perms.judge.change_contest %}
|
||||
{{ make_tab('admin', 'fa-edit', url('admin:judge_contest_changelist'), _('Admin')) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
88
templates/contest/contest-tabs.html
Normal file
88
templates/contest/contest-tabs.html
Normal file
|
@ -0,0 +1,88 @@
|
|||
{% extends "tabs-base.html" %}
|
||||
|
||||
{% block tabs %}
|
||||
{{ make_tab('detail', 'fa-info-circle', url('contest_view', contest.key), _('Info')) }}
|
||||
{% if contest.ended or contest.is_editable_by(request.user) %}
|
||||
{{ make_tab('stats', 'fa-pie-chart', url('contest_stats', contest.key), _('Statistics')) }}
|
||||
{% endif %}
|
||||
|
||||
{% if contest.start_time <= now or perms.judge.see_private_contest %}
|
||||
{% if contest.show_scoreboard or contest.can_see_scoreboard(request.user) %}
|
||||
{{ make_tab('ranking', 'fa-bar-chart', url('contest_ranking', contest.key), _('Rankings')) }}
|
||||
{% else %}
|
||||
{{ make_tab('ranking', 'fa-bar-chart', None, _('Hidden Rankings')) }}
|
||||
{% endif %}
|
||||
{% if contest.show_scoreboard and request.user.is_authenticated %}
|
||||
{{ make_tab('participation', 'fa-users', url('contest_participation_own', contest.key), _('Participation')) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if contest.is_editable_by(request.user) %}
|
||||
{% if perms.judge.moss_contest and has_moss_api_key %}
|
||||
{{ make_tab('moss', 'fa-gavel', url('contest_moss', contest.key), _('MOSS')) }}
|
||||
{% endif %}
|
||||
{{ make_tab('edit', 'fa-edit', url('admin:judge_contest_change', contest.id), _('Edit')) }}
|
||||
{% endif %}
|
||||
{% if perms.judge.clone_contest %}
|
||||
{{ make_tab('clone', 'fa-copy', url('contest_clone', contest.key), _('Clone')) }}
|
||||
{% endif %}
|
||||
|
||||
{% if request.user.is_authenticated %}
|
||||
{% if contest.can_join or participating or is_organizer %}
|
||||
{% if contest.ended %}
|
||||
{% if in_contest %}
|
||||
{# They're in the contest because they're participating virtually #}
|
||||
<form action="{{ url('contest_leave', contest.key) }}" method="post"
|
||||
class="contest-join-pseudotab unselectable button full">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="leaving-forever" value="{{ _('Leave contest') }}">
|
||||
</form>
|
||||
{% else %}
|
||||
{# They're in the contest because they're participating virtually #}
|
||||
<form action="{{ url('contest_join', contest.key) }}" method="post"
|
||||
class="contest-join-pseudotab unselectable button full">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{{ _('Virtual join') }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if in_contest %}
|
||||
{# Allow people with ended participations to continue spectating #}
|
||||
<form action="{{ url('contest_leave', contest.key) }}" method="post"
|
||||
class="contest-join-pseudotab unselectable button full">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="
|
||||
{%- if participating and participation.ended or request.profile in contest.organizers.all() %}
|
||||
{{- _('Stop spectating') -}}
|
||||
{% else %}
|
||||
{{- _('Leave contest') -}}
|
||||
{% endif %}">
|
||||
</form>
|
||||
{% elif participating and participation.ended or is_organizer %}
|
||||
<form action="{{ url('contest_join', contest.key) }}" method="post"
|
||||
class="contest-join-pseudotab unselectable button full">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{{ _('Spectate contest') }}">
|
||||
</form>
|
||||
{% elif participating %}
|
||||
<form action="{{ url('contest_join', contest.key) }}" method="post"
|
||||
class="contest-join-pseudotab unselectable button full">
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="{{ _('Join contest') }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="{{ url('contest_join', contest.key) }}" method="post"
|
||||
class="contest-join-pseudotab unselectable button full">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="first-join" value="{{ _('Join contest') }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% elif contest.can_join %}
|
||||
<form action="{{ url('auth_login') }}" method="get"
|
||||
class="contest-join-pseudotab unselectable button full">
|
||||
<input type="hidden" name="next" value="{{ LOGIN_RETURN_PATH|urlencode }}">
|
||||
<input type="submit" value="{{ _('Login to participate') }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
137
templates/contest/contest.html
Normal file
137
templates/contest/contest.html
Normal file
|
@ -0,0 +1,137 @@
|
|||
{% extends "common-content.html" %}
|
||||
|
||||
{% block title_ruler %}{% endblock %}
|
||||
|
||||
{% block title_row %}
|
||||
{% set tab = 'detail' %}
|
||||
{% set title = contest.name %}
|
||||
{% include "contest/contest-tabs.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content_js_media %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.time-remaining').each(function () {
|
||||
count_down($(this));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% include "contest/media-js.html" %}
|
||||
{% include "comments/media-js.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content_media %}
|
||||
{% include "comments/media-css.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div id="banner">
|
||||
<a href="https://www.timeanddate.com/worldclock/fixedtime.html?msg={{ contest.name|urlquote('') }}&iso=
|
||||
{{- contest.start_time|utc|date('Y-m-d\TH:i:s') }}" class="date">
|
||||
{%- if participating and participation.virtual and not participation.ended -%}
|
||||
{% if participation.spectate %}
|
||||
{{- _('Spectating, contest ends in %(countdown)s.', countdown=contest.time_before_end|as_countdown) -}}
|
||||
{% elif participation.end_time %}
|
||||
{{- _('Participating virtually, %(countdown)s remaining.', countdown=participation.time_remaining|as_countdown) -}}
|
||||
{% else %}
|
||||
{{- _('Participating virtually.') -}}
|
||||
{% endif %}
|
||||
{%- else -%}
|
||||
{% if contest.start_time > now %}
|
||||
{{- _('Starting in %(countdown)s', countdown=contest.time_before_start|as_countdown) -}}
|
||||
{% elif contest.end_time < now %}
|
||||
{{- _('Contest is over.') -}}
|
||||
{% else %}
|
||||
{%- if participating -%}
|
||||
{% if participation.ended %}
|
||||
{{- _('Your time is up! Contest ends in %(countdown)s.', countdown=contest.time_before_end|as_countdown) -}}
|
||||
{% else %}
|
||||
{{- _('You have %(countdown)s remaining.', countdown=participation.time_remaining|as_countdown) -}}
|
||||
{% endif %}
|
||||
{%- else -%}
|
||||
{{ _('Contest ends in %(countdown)s.', countdown=contest.time_before_end|as_countdown) }}
|
||||
{%- endif -%}
|
||||
{% endif %}
|
||||
{%- endif -%}
|
||||
</a>
|
||||
<div id="time">
|
||||
{% if contest.time_limit %}
|
||||
{% trans trimmed start_time=contest.start_time|date(_("F j, Y, G:i T")), end_time=contest.end_time|date(_("F j, Y, G:i T")), time_limit=contest.time_limit|timedelta('localized-no-seconds') %}
|
||||
<b>{{ time_limit }}</b> window between <b>{{ start_time }}</b> and <b>{{ end_time }}</b>
|
||||
{% endtrans %}
|
||||
{% else %}
|
||||
{% trans trimmed length=contest.contest_window_length|timedelta("localized-no-seconds"), start_time=contest.start_time|date(_("F j, Y, G:i T")) %}
|
||||
<b>{{ length }}</b> long starting on <b>{{ start_time }}</b>
|
||||
{% endtrans %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-description">
|
||||
{% cache 3600 'contest_html' contest.id MATH_ENGINE %}
|
||||
{{ contest.description|markdown('contest', MATH_ENGINE)|reference|str|safe }}
|
||||
{% endcache %}
|
||||
</div>
|
||||
|
||||
{% if contest.ended or request.user.is_superuser or is_organizer %}
|
||||
<hr>
|
||||
<div class="contest-problems">
|
||||
<h2 style="margin-bottom: 0.2em"><i class="fa fa-fw fa-question-circle"></i>{{ _('Problems') }} </h2>
|
||||
<table id="contest-problems" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Problem') }}</th>
|
||||
<th>{{ _('Points') }}</th>
|
||||
<th>{{ _('AC Rate') }}</th>
|
||||
<th>{{ _('Users') }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for problem in contest_problems %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if problem.is_public %}
|
||||
<a href="{{ url('problem_detail', problem.code) }}">{{ problem.i18n_name }}</a>
|
||||
{% else %}
|
||||
{{ problem.i18n_name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ problem.points|floatformat }}{% if problem.partial %}p{% endif %}</td>
|
||||
<td>{{ problem.ac_rate|floatformat(1) }}%</td>
|
||||
<td>
|
||||
{% if problem.is_public %}
|
||||
<a href="{{ url('ranked_submissions', problem.code) }}">{{ problem.user_count }}</a>
|
||||
{% else %}
|
||||
{{ problem.user_count }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if problem.is_public and problem.has_public_editorial %}
|
||||
<a href="{{ url('problem_editorial', problem.code) }}">{{ _('Editorial') }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<hr>
|
||||
<span class="social">
|
||||
{{ post_to_gplus(request, contest, '<i class="fa fa-google-plus-square"></i>') }}
|
||||
{{ post_to_facebook(request, contest, '<i class="fa fa-facebook-official"></i>') }}
|
||||
{{ post_to_twitter(request, SITE_NAME + ':', contest, '<i class="fa fa-twitter"></i>') }}
|
||||
</span>
|
||||
|
||||
{% include "comments/list.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block description_end %}{% endblock %}
|
||||
|
||||
{% block bodyend %}
|
||||
{{ super() }}
|
||||
{% include "comments/math.html" %}
|
||||
{% endblock %}
|
||||
k
|
307
templates/contest/list.html
Normal file
307
templates/contest/list.html
Normal file
|
@ -0,0 +1,307 @@
|
|||
{% extends "common-content.html" %}
|
||||
{% block meta %}
|
||||
<meta name="description" content="The {{ SITE_NAME }}'s contest list - past, present, and future.">
|
||||
{% endblock %}
|
||||
|
||||
{% block media %}
|
||||
<style>
|
||||
.time-left {
|
||||
text-align: left;
|
||||
color: #777;
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
.content-description ul {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block js_media %}
|
||||
<script src="{{ static('libs/featherlight/featherlight.min.js') }}" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.time-remaining').each(function () {
|
||||
count_down($(this));
|
||||
});
|
||||
|
||||
$('.contest-tag').find('a[data-featherlight]').featherlight();
|
||||
|
||||
$('.join-warning').click(function () {
|
||||
return confirm('{{ _('Are you sure you want to join?') }}\n' +
|
||||
'{{ _('Joining a contest for the first time starts your timer, after which it becomes unstoppable.') }}');
|
||||
});
|
||||
|
||||
// var tooltip_classes = 'tooltipped tooltipped-e';
|
||||
//
|
||||
// $('.contest-tag').each(function () {
|
||||
// var link = $(this);//
|
||||
// link.mouseenter(function (e) {
|
||||
// link.addClass(tooltip_classes).attr('aria-label', link.attr('data-description'));
|
||||
// }).mouseleave(function (e) {
|
||||
// link.removeClass(tooltip_classes).removeAttr('aria-label');
|
||||
// });
|
||||
// });
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title_ruler %}{% endblock %}
|
||||
|
||||
{% block title_row %}
|
||||
{% set tab = 'list' %}
|
||||
{% set title = 'Contests' %}
|
||||
{% include "contest/contest-list-tabs.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% macro contest_head(contest) %}
|
||||
{% spaceless %}
|
||||
<a href="{{ url('contest_view', contest.key) }}" class="contest-list-title">
|
||||
{{- contest.name -}}
|
||||
</a>
|
||||
<span class="contest-tags">
|
||||
{% if not contest.is_visible %}
|
||||
<span style="background-color: #000000; color: #ffffff" class="contest-tag">
|
||||
<i class="fa fa-eye-slash"></i> {{ _('hidden') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if contest.is_private %}
|
||||
<span style="background-color: #666666; color: #ffffff" class="contest-tag">
|
||||
<i class="fa fa-lock"></i> {{ _('private') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if contest.is_organization_private %}
|
||||
{% for org in contest.organizations.all() %}
|
||||
<span style="background-color: #cccccc" class="contest-tag">
|
||||
<a href="{{ org.get_absolute_url() }}" style="color: #000000">
|
||||
<i class="fa fa-lock"></i> {{ org.name }}
|
||||
</a>
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if contest.is_rated %}
|
||||
<span style="background-color: #e54c14; color: #ffffff" class="contest-tag">
|
||||
<i class="fa fa-bar-chart"></i> {{ _('rated') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% for tag in contest.tags.all() %}
|
||||
<span style="background-color: {{ tag.color }}" class="contest-tag">
|
||||
<a href="{{ url('contest_tag', tag.name) }}"
|
||||
style="color: {{ tag.text_color }}"
|
||||
data-featherlight="{{ url('contest_tag_ajax', tag.name) }}">
|
||||
{{- tag.name -}}
|
||||
</a>
|
||||
</span>
|
||||
{% endfor %}
|
||||
</span>
|
||||
{% endspaceless %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro time_left(contest) %}
|
||||
<div class="time time-left">
|
||||
{% if contest.time_limit %}
|
||||
{{ contest.start_time|date(_("M j, Y, G:i")) }} -
|
||||
{{ contest.end_time|date(_("M j, Y, G:i")) }}
|
||||
{% else %}
|
||||
{{ contest.start_time|date(_("M j, Y, G:i")) }}
|
||||
{% endif %}
|
||||
<br>
|
||||
{% if contest.time_limit %}
|
||||
{{ _('%(time_limit)s window', time_limit=contest.time_limit|timedelta('localized-no-seconds')) }}
|
||||
{% else %}
|
||||
{{ _('%(duration)s long', duration=contest.contest_window_length|timedelta('localized-no-seconds')) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro user_count(contest, user) %}
|
||||
{% if contest.show_scoreboard or contest.can_see_scoreboard(user) %}
|
||||
<a href="{{ url('contest_ranking', contest.key) }}">{{ contest.user_count }}</a>
|
||||
{% else %}
|
||||
{{ contest.user_count }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro contest_join(contest, request) %}
|
||||
{% if not request.in_contest %}
|
||||
<td>
|
||||
{% if request.profile in contest.organizers.all() %}
|
||||
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="unselectable button full participate-button"
|
||||
value="{{ _('Spectate') }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="unselectable button full participate-button join-warning"
|
||||
value="{{ _('Join') }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% block body %}
|
||||
<div class="content-description">
|
||||
{% if active_participations %}
|
||||
<h4>{{ _('Active Contests') }}</h4>
|
||||
<table class="contest-list table striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:90%">{{ _('Contest') }}</th>
|
||||
<th>{{ _('Users') }}</th>
|
||||
{% if not request.in_contest %}
|
||||
<th style="width:15%"></th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for participation in active_participations %}
|
||||
{% with contest=participation.contest %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="contest-block">
|
||||
{{ contest_head(contest) }}
|
||||
{% if contest.start_time %}
|
||||
<br>
|
||||
{% if contest.time_limit %}
|
||||
<span class="time">{{ _('Window ends in %(countdown)s', countdown=participation.time_remaining|as_countdown)}}
|
||||
{% elif contest.time_before_end %}
|
||||
<span class="time">{{ _('Ends in %(countdown)s', countdown=contest.time_before_end|as_countdown) }}</span>
|
||||
{% endif %}
|
||||
{{ time_left(contest) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ user_count(contest, request.user) }}
|
||||
</td>
|
||||
{{ contest_join(contest, request) }}
|
||||
</tr>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
{% endif %}
|
||||
|
||||
{% if current_contests %}
|
||||
<h4>{{ _('Ongoing Contests') }}</h4>
|
||||
<table class="contest-list table striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:90%">{{ _('Contest') }}</th>
|
||||
<th>{{ _('Users') }}</th>
|
||||
{% if not request.in_contest %}
|
||||
<th style="width:15%"></th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for contest in current_contests %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="contest-block">
|
||||
{{ contest_head(contest) }}
|
||||
{% if contest.start_time %}
|
||||
<br>
|
||||
{% if contest.time_before_end %}
|
||||
<span class="time">{{ _('Ends in %(countdown)s', countdown=contest.time_before_end|as_countdown) }}</span>
|
||||
{% endif %}
|
||||
{{ time_left(contest) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ user_count(contest, request.user) }}
|
||||
</td>
|
||||
{{ contest_join(contest, request) }}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
{% endif %}
|
||||
|
||||
<h4>{{ _('Upcoming Contests') }}</h4>
|
||||
{% if future_contests %}
|
||||
<table class="contest-list table striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ _('Contest') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for contest in future_contests %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="contest-block">
|
||||
{{ contest_head(contest) }}
|
||||
{% if contest.start_time %}
|
||||
<br>
|
||||
{% if contest.time_before_start %}
|
||||
<span class="time">{{ _('Starting in %(countdown)s.', countdown=contest.time_before_start|as_countdown) }}</span>
|
||||
{% endif %}
|
||||
{{ time_left(contest) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<i>{{ _('There are no scheduled contests at this time.') }}</i>
|
||||
<br>
|
||||
{% endif %}
|
||||
<br>
|
||||
|
||||
{% if past_contests %}
|
||||
<h4>{{ _('Past Contests') }}</h4>
|
||||
{% if page_obj and page_obj.num_pages > 1 %}
|
||||
<div style="margin-bottom: 4px;">
|
||||
{% include "list-pages.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<table class="contest-list table striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:90%">{{ _('Contest') }}</th>
|
||||
<th>{{ _('Users') }}</th>
|
||||
{% if not request.in_contest %}
|
||||
<th style="width:15%"></th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for contest in past_contests %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="contest-block">
|
||||
{{ contest_head(contest) }}
|
||||
{{ time_left(contest) }}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{{ user_count(contest, request.user) }}
|
||||
</td>
|
||||
{% if not request.in_contest %}
|
||||
<td><form action="{{ url('contest_join', contest.key) }}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="unselectable button full participate-button"
|
||||
value="{{ _('Virtual join') }}">
|
||||
</form></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if page_obj and page_obj.num_pages > 1 %}
|
||||
<div style="margin-top: 10px;">
|
||||
{% include "list-pages.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
13
templates/contest/media-js.html
Normal file
13
templates/contest/media-js.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$('.leaving-forever').click(function () {
|
||||
return confirm('{{ _('Are you sure you want to leave?') }}\n' +
|
||||
'{{ _('You cannot come back to a virtual participation. You will have to start a new one.') }}');
|
||||
});
|
||||
|
||||
$('.first-join').click(function () {
|
||||
return confirm('{{ _('Are you sure you want to join?') }}\n' +
|
||||
'{{ _('Joining a contest starts your timer, after which it becomes unstoppable.') }}');
|
||||
});
|
||||
});
|
||||
</script>
|
87
templates/contest/moss.html
Normal file
87
templates/contest/moss.html
Normal file
|
@ -0,0 +1,87 @@
|
|||
{% extends "common-content.html" %}
|
||||
|
||||
{% block title_ruler %}{% endblock %}
|
||||
|
||||
{% block title_row %}
|
||||
{% set tab = 'moss' %}
|
||||
{% set title = contest.name %}
|
||||
{% include "contest/contest-tabs.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content_media %}
|
||||
<style>
|
||||
.panes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.pane {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content_js_media %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$('.contest-moss').click(function () {
|
||||
return confirm('{{ _('Are you sure you want MOSS the contest?') }}');
|
||||
});
|
||||
});
|
||||
$(function () {
|
||||
$('.contest-moss-delete').click(function () {
|
||||
return confirm('{{ _('Are you sure you want to delete the MOSS results?') }}');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
{% if has_results %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="header">{{ _('Problem') }}</th>
|
||||
{% for lang in languages %}
|
||||
<th class="header">{{ lang }}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for problem, results in moss_results %}
|
||||
<tr id="problem-{{ problem.code }}">
|
||||
<td>
|
||||
<a href="{{ url('problem_detail', problem.code) }}">{{ problem.name }}</a>
|
||||
</td>
|
||||
{% for result in results %}
|
||||
<td>
|
||||
{% if result.submission_count %}
|
||||
<a href="{{ result.url }}">{{ result.submission_count }} {{_('submissions')}}</a>
|
||||
{% else %}
|
||||
{{ _('No submissions') }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<div class="panes">
|
||||
<div class="pane">
|
||||
<form method="post" action="{{ url('contest_moss', contest.key) }}">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="unselectable button full contest-moss" style="padding: 10px;"
|
||||
value="{% if has_results %} {{ _('Re-MOSS contest') }} {% else %} {{ _('MOSS contest') }} {% endif %}">
|
||||
</form>
|
||||
</div>
|
||||
{% if has_results %}
|
||||
<div class="pane">
|
||||
<form method="post" action="{{ url('contest_moss_delete', contest.key) }}">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="unselectable button full contest-moss-delete" style="padding: 10px;"
|
||||
value="{{ _('Delete MOSS results') }}">
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
4
templates/contest/preview.html
Normal file
4
templates/contest/preview.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
{{ preview_data|markdown('contest', MATH_ENGINE)|reference|str|safe }}
|
||||
{% if REQUIRE_JAX %}
|
||||
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
||||
{% endif %}
|
20
templates/contest/private.html
Normal file
20
templates/contest/private.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block body %}
|
||||
{% if error.is_private %}
|
||||
<p><i>{{ _('This contest is private to specific users.') }}</i></p>
|
||||
{% endif %}
|
||||
|
||||
{% if error.is_organization_private %}
|
||||
{% if error.is_private %}
|
||||
<p>{{ _('Additionally, only the following organizations may access this contest:') }}</p>
|
||||
{% else %}
|
||||
<p>{{ _('Only the following organizations may access this contest:') }}</p>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{% for org in error.orgs %}
|
||||
<li><a href="{{ org.get_absolute_url() }}">{{ org.name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
68
templates/contest/ranking-table.html
Normal file
68
templates/contest/ranking-table.html
Normal file
|
@ -0,0 +1,68 @@
|
|||
{% extends "user/base-users-table.html" %}
|
||||
|
||||
{% block after_rank_head %}
|
||||
{% if has_rating %}
|
||||
<th>{{ _('Rating') }}</th>
|
||||
{% endif %}
|
||||
<th class="organization-column">{{ _('Organization') }}</th>
|
||||
{% endblock %}
|
||||
|
||||
{% block after_rank %}
|
||||
{% if has_rating %}
|
||||
<td>{% if user.participation_rating %}{{ rating_number(user.participation_rating) }}{% endif %}</td>
|
||||
{% endif %}
|
||||
<td class="organization-column">
|
||||
{% if user.organization %}
|
||||
<span class="organization"><a href="{{ user.organization.get_absolute_url() }}">
|
||||
{{- user.organization.short_name -}}
|
||||
</a></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endblock %}
|
||||
|
||||
{% block user_data %}
|
||||
{% if is_organizer %}
|
||||
<span class="contest-participation-operation">
|
||||
<form action="{{ url('contest_participation_disqualify', contest.key) }}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="participation" value="{{ user.participation.id }}">
|
||||
{% if user.participation.is_disqualified %}
|
||||
<a href="#" title="{{ _('Un-Disqualify') }}"
|
||||
class="un-disqualify-participation"><i class="fa fa-undo fa-fw"></i></a>
|
||||
{% else %}
|
||||
<a href="#" title="{{ _('Disqualify') }}"
|
||||
class="disqualify-participation"><i class="fa fa-trash fa-fw"></i></a>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% if perms.judge.change_contestparticipation %}
|
||||
<a href="{{ url('admin:judge_contestparticipation_change', user.participation.id) }}"
|
||||
title="{{ _('Admin') }}" class="edit-participation"><i class="fa fa-cog fa-fw"></i></a>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block before_point_head %}
|
||||
{% for problem in problems %}
|
||||
<th class="points header"><a href="{{ url('contest_ranked_submissions', contest.key, problem.problem.code) }}">
|
||||
{{- loop.index }}
|
||||
<div class="point-denominator">{{ problem.points }}</div>
|
||||
</a></th>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block row_extra %}
|
||||
{% if user.participation.is_disqualified %}
|
||||
class="disqualified"
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block before_point %}
|
||||
{% for cell in user.problem_cells %}
|
||||
{{ cell }}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
{% block point %}
|
||||
{{ user.result_cell }}
|
||||
{% endblock %}
|
259
templates/contest/ranking.html
Normal file
259
templates/contest/ranking.html
Normal file
|
@ -0,0 +1,259 @@
|
|||
{% extends "user/base-users.html" %}
|
||||
|
||||
{% block title_ruler %}{% endblock %}
|
||||
|
||||
{% block title_row %}
|
||||
{% set title = contest.name %}
|
||||
{% include "contest/contest-tabs.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block users_media %}
|
||||
<style>
|
||||
#content-left {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
#users-table .username {
|
||||
min-width: 20em;
|
||||
}
|
||||
|
||||
#users-table td {
|
||||
height: 2.5em;
|
||||
}
|
||||
|
||||
#users-table a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.userinfo a, .user-name a, .user-name form {
|
||||
display: inline !important;
|
||||
}
|
||||
|
||||
#users-table th a, #users-table th a:link, #users-table th a:visited {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#users-table th a:hover {
|
||||
color: #0F0;
|
||||
}
|
||||
|
||||
#users-table td a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.rank {
|
||||
min-width: 2.5em
|
||||
}
|
||||
|
||||
.points {
|
||||
min-width: 4em;
|
||||
}
|
||||
|
||||
.disqualified {
|
||||
background-color: #ffa8a8 !important;
|
||||
}
|
||||
|
||||
.full-score, .full-score a {
|
||||
font-weight: bold;
|
||||
color: green;
|
||||
}
|
||||
|
||||
.partial-score, .partial-score a {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.failed-score, .failed-score a {
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.pretest-full-score, .pretest-full-score a {
|
||||
font-weight: bold;
|
||||
color: #2980b9;
|
||||
}
|
||||
|
||||
.pretest-partial-score, .pretest-partial-score a {
|
||||
color: #2980b9;
|
||||
}
|
||||
|
||||
.pretest-failed-score, .pretest-failed-score a {
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.user-points {
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.solving-time {
|
||||
color: gray;
|
||||
font-weight: normal;
|
||||
font-size: 0.75em;
|
||||
padding-bottom: -0.75em;
|
||||
}
|
||||
|
||||
.point-denominator {
|
||||
border-top: 1px solid gray;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
.start-time {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.organization-column {
|
||||
display: none;
|
||||
text-align: left !important;
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
.organization-column a {
|
||||
color: gray !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% if has_rating %}
|
||||
<style>#users-table .rate-box {
|
||||
font-size: 0.85em;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#users-table td:nth-child(1) .rating {
|
||||
margin-left: 1.25em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#users-table td:nth-child(2) a {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
{% endif %}
|
||||
|
||||
<style>
|
||||
.select2-selection__arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.select2-selection__rendered {
|
||||
cursor: text;
|
||||
overflow: initial !important
|
||||
}
|
||||
|
||||
.select2-results__option--highlighted {
|
||||
background-color: #DEDEDE !important;
|
||||
}
|
||||
|
||||
.select2-results__option {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#search-contest, #search-contest + .select2 {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
#search-contest {
|
||||
width: 200px;
|
||||
height: 2.3em;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block users_js_media %}
|
||||
{% if is_organizer %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$('a.disqualify-participation').click(function (e) {
|
||||
e.preventDefault();
|
||||
if (e.ctrlKey || e.metaKey || confirm("{{ _('Are you sure you want to disqualify this participation?') }}"))
|
||||
$(this).closest('form').submit();
|
||||
})
|
||||
$('a.un-disqualify-participation').click(function (e) {
|
||||
e.preventDefault();
|
||||
if (e.ctrlKey || e.metaKey || confirm("{{ _('Are you sure you want to un-disqualify this participation?') }}"))
|
||||
$(this).closest('form').submit();
|
||||
})
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% if perms.judge.change_contestparticipation %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$('td.user').find('a.user-name').click(function (e) {
|
||||
var data = $(this).siblings('.edit-participation');
|
||||
if (e.altKey && data.length) {
|
||||
window.open(data.attr('data-link'), '_blank');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% if not contest.ended %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
window.install_tooltips = function () {
|
||||
$('td.user').find('a.user-name').each(function () {
|
||||
var link = $(this);
|
||||
link.mouseenter(function (e) {
|
||||
var start_time = link.siblings('.start-time').text();
|
||||
link.addClass('tooltipped tooltipped-e').attr('aria-label', start_time);
|
||||
}).mouseleave(function (e) {
|
||||
link.removeClass('tooltipped tooltipped-e').removeAttr('aria-label');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
install_tooltips();
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
var url = '{{ url('contest_participation', contest.key, '__username__') }}';
|
||||
var placeholder = $('#search-contest').replaceWith($('<select>').attr({
|
||||
id: 'search-contest'
|
||||
})).attr('placeholder');
|
||||
|
||||
$('#search-contest').select2({
|
||||
placeholder: placeholder,
|
||||
ajax: {
|
||||
url: '{{ url('contest_user_search_select2_ajax', contest.key) }}'
|
||||
},
|
||||
minimumInputLength: 1,
|
||||
escapeMarkup: function (markup) {
|
||||
return markup;
|
||||
},
|
||||
templateResult: function (data, container) {
|
||||
return ('<img class="user-search-image" src="' + data.gravatar_url + '" width="24" height="24">' +
|
||||
'<span class="' + data.display_rank + ' user-search-name">' + data.text + '</span>');
|
||||
}
|
||||
}).on('change', function () {
|
||||
window.location.href = url.replace('__username__', $(this).val());
|
||||
});
|
||||
|
||||
$('#show-organizations-checkbox').click(function () {
|
||||
$('.organization-column').toggle();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% include "contest/media-js.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block users_table %}
|
||||
<div style="margin-bottom: 0.5em">
|
||||
{% if tab == 'participation' %}
|
||||
{% if contest.start_time <= now or perms.judge.see_private_contest %}
|
||||
<input id="search-contest" type="text" placeholder="{{ _('View user participation') }}">
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<input id="show-organizations-checkbox" type="checkbox" style="vertical-align: bottom">
|
||||
<label for="show-organizations-checkbox" style="vertical-align: bottom">{{ _('Show organizations') }}</label>
|
||||
</div>
|
||||
{% include "contest/ranking-table.html" %}
|
||||
{% endblock %}
|
58
templates/contest/stats.html
Normal file
58
templates/contest/stats.html
Normal file
|
@ -0,0 +1,58 @@
|
|||
{% extends "common-content.html" %}
|
||||
|
||||
{% block title_ruler %}{% endblock %}
|
||||
|
||||
{% block title_row %}
|
||||
{% set tab = 'stats' %}
|
||||
{% set title = contest.name %}
|
||||
{% include "contest/contest-tabs.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content_js_media %}
|
||||
<script type="text/javascript">
|
||||
window.stats = {{ stats }};
|
||||
</script>
|
||||
{% compress js %}
|
||||
{% include "stats/media-js.html" %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
draw_stacked_bar_chart(window.stats.problem_status_count, $('#problem-status-count'));
|
||||
draw_bar_chart(window.stats.problem_ac_rate, $('#problem-ac-rate'));
|
||||
draw_pie_chart(window.stats.language_count, $('#language-count'));
|
||||
draw_bar_chart(window.stats.language_ac_rate, $('#language-ac-rate'));
|
||||
});
|
||||
</script>
|
||||
{% endcompress %}
|
||||
{% include "contest/media-js.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content_media %}
|
||||
<style>
|
||||
.chart {
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h3>{{ _('Problem Status Distribution') }}</h3>
|
||||
<div id="problem-status-count" class="chart">
|
||||
<canvas></canvas>
|
||||
</div>
|
||||
|
||||
<h3>{{ _('Problem AC Rate') }}</h3>
|
||||
<div id="problem-ac-rate" class="chart">
|
||||
<canvas></canvas>
|
||||
</div>
|
||||
|
||||
<h3>{{ _('Submissions by Language') }}</h3>
|
||||
<div id="language-count" class="chart">
|
||||
<canvas width="400" height="300"></canvas>
|
||||
<ul class="legend"></ul>
|
||||
</div>
|
||||
|
||||
<h3>{{ _('Language AC Rate') }}</h3>
|
||||
<div id="language-ac-rate" class="chart">
|
||||
<canvas></canvas>
|
||||
</div>
|
||||
{% endblock %}
|
7
templates/contest/tag-ajax.html
Normal file
7
templates/contest/tag-ajax.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
{% if not title %}
|
||||
{% include "contest/tag-title.html" %}
|
||||
<br><br>
|
||||
<hr>
|
||||
{% endif %}
|
||||
|
||||
{{ tag.description|markdown('contest_tag') }}
|
11
templates/contest/tag-title.html
Normal file
11
templates/contest/tag-title.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<h2 style="display:inline">
|
||||
<span style="background-color: {{ tag.color }}; color: {{ tag.text_color }}" class="contest-tag">
|
||||
{{- tag.name -}}
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
{% if perms.judge.change_contesttag %}
|
||||
<div class="title-line-action">
|
||||
[<a href="{{ url('admin:judge_contesttag_change', tag.id) }}">{{ _('Edit') }}</a>]
|
||||
</div>
|
||||
{% endif %}
|
9
templates/contest/tag.html
Normal file
9
templates/contest/tag.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content_title %}
|
||||
{% include "contest/tag-title.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% include "contest/tag-ajax.html" %}
|
||||
{% endblock %}
|
39
templates/error.html
Normal file
39
templates/error.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>{{ code }} - {{ id }}</title>
|
||||
<style>
|
||||
body {
|
||||
color: #FFFFFF;
|
||||
background-color: #000080;
|
||||
font-family: Lucida Console, Monospace, serif;
|
||||
font-size: 14px;
|
||||
font-smooth: never;
|
||||
-webkit-font-smoothing: none;
|
||||
margin-left: 50px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #FFFFFF;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: Lucida Console, Monospace, serif;
|
||||
line-height: 1.3;
|
||||
font-smooth: never;
|
||||
-webkit-font-smoothing: none;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>SIGSEGV: {{ id }}</h2>
|
||||
<pre>{{ description }}
|
||||
site: fatal signal: Segmentation fault
|
||||
site died (signal <b>{{ code }}</b>, exit -11)<br><br>panic: <a href="javascript:history.back()">go back</a></pre>
|
||||
{% if request.user.is_superuser and traceback %}
|
||||
<pre>{{ traceback }}</pre>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
0
templates/extra_js.html
Normal file
0
templates/extra_js.html
Normal file
5
templates/flatpages/admin_link.html
Normal file
5
templates/flatpages/admin_link.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% if perms.flatpages.change_flatpage %}
|
||||
<div class="title-line-action">
|
||||
[<a href="{{ url('admin:flatpages_flatpage_change', flatpage.id) }}">{{ _('Edit') }}</a>]
|
||||
</div>
|
||||
{% endif %}
|
15
templates/flatpages/default.html
Normal file
15
templates/flatpages/default.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}{{ flatpage.title }} - {{ SITE_LONG_NAME }}{% endblock %}
|
||||
{% block og_title %}
|
||||
<meta property="og:title" content="{{ flatpage.title }} - {{ SITE_LONG_NAME }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content_title %}{{ flatpage.title }}{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
{% include "flatpages/admin_link.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{{ render_django(flatpage.content) }}
|
||||
{% endblock %}
|
14
templates/flatpages/markdown.html
Normal file
14
templates/flatpages/markdown.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}{{ flatpage.title }} - {{ SITE_LONG_NAME }}{% endblock %}
|
||||
|
||||
{% block og_title %}
|
||||
<meta property="og:title" content="{{ flatpage.title }} - {{ SITE_LONG_NAME }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content_title %}{{ flatpage.title }}{% endblock %}
|
||||
|
||||
{% block header %}{% include "flatpages/admin_link.html" %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="content-description">{{ flatpage.content|markdown('solution')|reference|str|safe }}</div>
|
||||
{% endblock %}
|
9
templates/flatpages/markdown_math.html
Normal file
9
templates/flatpages/markdown_math.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% extends "flatpages/markdown.html" %}
|
||||
|
||||
{% block body %}
|
||||
<div class="content-description">{{ flatpage.content|markdown('solution', MATH_ENGINE)|reference }}</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block bodyend %}
|
||||
{% if REQUIRE_JAX %}{% include "mathjax-load.html" %}{% endif %}
|
||||
{% endblock %}
|
5
templates/generic-message.html
Normal file
5
templates/generic-message.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block body %}
|
||||
<h3>{{ message }}</h3>
|
||||
{% endblock %}
|
23
templates/home.html
Normal file
23
templates/home.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
{% extends "blog/list.html" %}
|
||||
{% block before_posts %}
|
||||
{% if i18n_config.home_page_top %}
|
||||
{{ render_django(i18n_config.home_page_top, request=request, user_count=user_count, problem_count=problem_count, submission_count=submission_count, language_count=language_count, perms=perms) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block meta %}
|
||||
{% if i18n_config.meta_description %}
|
||||
<meta name="description" content="{{ i18n_config['meta_description'] }}">
|
||||
{% endif %}
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
"url": "{{ DMOJ_SCHEME }}://{{ site.domain }}/",
|
||||
"potentialAction": {
|
||||
"@type": "SearchAction",
|
||||
"target": "{{ DMOJ_SCHEME }}://{{ site.domain }}/problems/?search={search_term_string}",
|
||||
"query-input": "required name=search_term_string"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
1
templates/license-preview.html
Normal file
1
templates/license-preview.html
Normal file
|
@ -0,0 +1 @@
|
|||
{{ preview_data|markdown('license') }}
|
13
templates/license.html
Normal file
13
templates/license.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "common-content.html" %}
|
||||
{% block description %}
|
||||
{% cache 3600 'license_html' license.id %}
|
||||
{{ license.text|markdown('license') }}
|
||||
{% endcache %}
|
||||
{% endblock %}
|
||||
|
||||
{% block info_float %}
|
||||
{% if perms.judge.change_license %}
|
||||
<div><a href="{{ url('admin:judge_license_change', license.id) }}">{{ _('Edit') }}</a></div>
|
||||
{% endif %}
|
||||
<div><b>{{ _('Source:') }}</b> <a href="{{ license.link }}">{{ license.link }}</a></div>
|
||||
{% endblock %}
|
31
templates/list-pages.html
Normal file
31
templates/list-pages.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
<ul class="pagination">
|
||||
{% if page_obj.has_previous() %}
|
||||
{% if page_obj.previous_page_number() == 1 and first_page_href != None %}
|
||||
<li><a href="{{ first_page_href }}">«</a></li>
|
||||
{% else %}
|
||||
<li><a href="{{ page_prefix or '' }}{{ page_obj.previous_page_number() }}{{ page_suffix or '' }}">«</a></li>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<li class="disabled-page"><span>«</span></li>
|
||||
{% endif %}
|
||||
|
||||
{% for page in page_obj.page_range %}
|
||||
{% if not page %}
|
||||
<li class="disabled-page"><span>...</span></li>
|
||||
{% else %}
|
||||
<li{% if page == page_obj.number %} class="active-page"{% endif %}><a href="
|
||||
{%- if page == 1 and first_page_href != None -%}
|
||||
{{ first_page_href }}
|
||||
{%- else -%}
|
||||
{{ page_prefix or '' }}{{ page }}{{ page_suffix or '' }}
|
||||
{%- endif -%}
|
||||
">{{ page }}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if page_obj.has_next() %}
|
||||
<li><a href="{{ page_prefix or '' }}{{ page_obj.next_page_number() }}{{ page_suffix or '' }}">»</a></li>
|
||||
{% else %}
|
||||
<li class="disabled-page"><span>»</span></li>
|
||||
{% endif %}
|
||||
</ul>
|
12
templates/mathjax-load.html
Normal file
12
templates/mathjax-load.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<script type="text/javascript" src="{{ static('mathjax_config.js') }}"></script>
|
||||
<script type="text/javascript"
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_HTML"></script>
|
||||
<script type="text/javascript">
|
||||
window.reload_mathjax = function () {
|
||||
MathJax.Hub.queue.Push(function () {
|
||||
$('.tex-image').hide();
|
||||
$('.tex-text').show();
|
||||
});
|
||||
};
|
||||
window.reload_mathjax();
|
||||
</script>
|
7
templates/messages.html
Normal file
7
templates/messages.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
{% if messages %}
|
||||
<ul class="messages">
|
||||
{% for message in messages %}
|
||||
<li class="{{ message.tags }}">{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
16
templates/newsletter/common.html
Normal file
16
templates/newsletter/common.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>{% block title %}{{ _('Newsletter') }}{% endblock title %}</title>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" >
|
||||
{% block header %}
|
||||
{% endblock header %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% block body %}
|
||||
{% endblock body %}
|
||||
</body>
|
||||
</html>
|
22
templates/newsletter/message/message.html
Normal file
22
templates/newsletter/message/message.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>{{ newsletter.title }}: {{ message.title }}</title>
|
||||
</head>
|
||||
<body>
|
||||
{% for article in message.articles.all() %}
|
||||
<div>{{ article.text|safe }}</div>
|
||||
|
||||
{% if article.url %}
|
||||
<div><a href="{{ article.url }}">Read more</a></div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<a href="http://{{ site.domain }}{{ url('newsletter_unsubscribe_request', newsletter.slug) }}" style="color:#999;float:right">Unsubscribe</a>
|
||||
{% if submission and submission.publish %}
|
||||
<a href="http://{{ site.domain }}{{ submission.get_absolute_url }}" style="color:#999">Read message online</a>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
15
templates/newsletter/message/message.txt
Normal file
15
templates/newsletter/message/message.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
++++++++++++++++++++
|
||||
|
||||
{{ newsletter.title|safe }}: {{ message.title|safe }}
|
||||
|
||||
++++++++++++++++++++
|
||||
|
||||
{% for article in message.articles.all() %}
|
||||
{{ article.title|safe }}
|
||||
{{ article.text|striptags|safe }}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
++++++++++++++++++++
|
||||
|
||||
Unsubscribe: http://{{ site }}{{ url('newsletter_unsubscribe_request', newsletter.slug) }}
|
1
templates/newsletter/message/message_subject.txt
Normal file
1
templates/newsletter/message/message_subject.txt
Normal file
|
@ -0,0 +1 @@
|
|||
{{ message.title|safe }}
|
20
templates/newsletter/message/subscribe.html
Normal file
20
templates/newsletter/message/subscribe.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
||||
"http://www.w3.org/TR/html4/strict.dtd">
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Subscription to {{ newsletter.title }}</title>
|
||||
</head>
|
||||
<body>
|
||||
Dear {{ subscription.name }},
|
||||
|
||||
you, or someone in your name requested a subscription to {{ newsletter.title }}.
|
||||
|
||||
If you would like to confirm your subscription, please follow this activation link:
|
||||
http://{{ site.domain }}{{ subscription.subscribe_activate_url }}
|
||||
|
||||
Kind regards,
|
||||
{{ newsletter.sender }}
|
||||
</body>
|
||||
</html>
|
9
templates/newsletter/message/subscribe.txt
Normal file
9
templates/newsletter/message/subscribe.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
Dear {{ subscription.name|safe }},
|
||||
|
||||
you, or someone in your name requested a subscription to {{ newsletter.title|safe }}.
|
||||
|
||||
If you would like to confirm your subscription, please follow this activation link:
|
||||
http://{{ site.domain }}{{ subscription.subscribe_activate_url }}
|
||||
|
||||
Kind regards,
|
||||
{{ newsletter.sender|safe }}
|
1
templates/newsletter/message/subscribe_subject.txt
Normal file
1
templates/newsletter/message/subscribe_subject.txt
Normal file
|
@ -0,0 +1 @@
|
|||
{{ newsletter.title|safe }} - Confirm subscription
|
20
templates/newsletter/message/unsubscribe.html
Normal file
20
templates/newsletter/message/unsubscribe.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
||||
"http://www.w3.org/TR/html4/strict.dtd">
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Unsubscription from {{ newsletter.title }}</title>
|
||||
</head>
|
||||
<body>
|
||||
Dear {{ subscription.name }},
|
||||
|
||||
you, or someone in your name requested unsubscription from {{ newsletter.title }}.
|
||||
|
||||
If you would like to confirm your unsubscription, please follow this activation link:
|
||||
http://{{ site.domain }}{{ subscription.unsubscribe_activate_url }}
|
||||
|
||||
Kind regards,
|
||||
{{ newsletter.sender }}
|
||||
</body>
|
||||
</html>
|
9
templates/newsletter/message/unsubscribe.txt
Normal file
9
templates/newsletter/message/unsubscribe.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
Dear {{ subscription.name|safe }},
|
||||
|
||||
you, or someone in your name requested unsubscription from {{ newsletter.title|safe }}.
|
||||
|
||||
If you would like to confirm your unsubscription, please follow this activation link:
|
||||
http://{{ site.domain }}{{ subscription.unsubscribe_activate_url }}
|
||||
|
||||
Kind regards,
|
||||
{{ newsletter.sender|safe }}
|
1
templates/newsletter/message/unsubscribe_subject.txt
Normal file
1
templates/newsletter/message/unsubscribe_subject.txt
Normal file
|
@ -0,0 +1 @@
|
|||
{{ newsletter.title|safe }} - Confirm unsubscription
|
20
templates/newsletter/message/update.html
Normal file
20
templates/newsletter/message/update.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
||||
"http://www.w3.org/TR/html4/strict.dtd">
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Update of subscription to {{ newsletter.title }}</title>
|
||||
</head>
|
||||
<body>
|
||||
Dear {{ subscription.name }},
|
||||
|
||||
you, or someone in your name requested updating your personal information for {{ newsletter.title }}.
|
||||
|
||||
To make changes to your information in our database, please follow this activation link:
|
||||
http://{{ site.domain }}{{ subscription.update_activate_url }}
|
||||
|
||||
Kind regards,
|
||||
{{ newsletter.sender }}
|
||||
</body>
|
||||
</html>
|
9
templates/newsletter/message/update.txt
Normal file
9
templates/newsletter/message/update.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
Dear {{ subscription.name|safe }},
|
||||
|
||||
you, or someone in your name requested updating your personal information for {{ newsletter.title|safe }}.
|
||||
|
||||
To make changes to your information in our database, please follow this activation link:
|
||||
http://{{ site.domain }}{{ subscription.update_activate_url }}
|
||||
|
||||
Kind regards,
|
||||
{{ newsletter.sender|safe }}
|
1
templates/newsletter/message/update_subject.txt
Normal file
1
templates/newsletter/message/update_subject.txt
Normal file
|
@ -0,0 +1 @@
|
|||
{{ newsletter.title|safe }} - Update information
|
13
templates/newsletter/newsletter_detail.html
Normal file
13
templates/newsletter/newsletter_detail.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ object.title }} - Newsletter - {{ SITE_LONG_NAME }}{% endblock %}
|
||||
{% block content_title %}Newsletter: {{ object.title }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<ul>
|
||||
<li><a href="{{ url('newsletter_subscribe_request', object.slug) }}">Subscribe</a></li>
|
||||
<li><a href="{{ url('newsletter_unsubscribe_request', object.slug) }}">Unsubscribe</a></li>
|
||||
<li><a href="{{ url('newsletter_archive', object.slug) }}">Archive</a></li>
|
||||
<li><a href="{{ url('newsletter_list') }}">Back to list</a></li>
|
||||
</ul>
|
||||
{% endblock %}
|
42
templates/newsletter/newsletter_list.html
Normal file
42
templates/newsletter/newsletter_list.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Newsletter list - {{ SITE_LONG_NAME }}{% endblock %}
|
||||
{% block content_title %}Newsletter list{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<form method="POST" action="" class="form-area">
|
||||
{% csrf_token %}
|
||||
{{ formset.management_form }}
|
||||
<table>
|
||||
<tr>
|
||||
<th>Newsletter</th>
|
||||
<th>Subscribe</th>
|
||||
</tr>
|
||||
{% for form in formset.forms %}
|
||||
<tr>
|
||||
<td>{{ form.id }}{{ form.newsletter }}
|
||||
<a href="{{ url('newsletter_archive', form.instance.newsletter.slug) }}">
|
||||
{{ form.instance.newsletter.title }}</a>
|
||||
</td>
|
||||
<td>{{ form.subscribed }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<hr>
|
||||
<p>
|
||||
<input id="id_submit" name="submit" value="Update subscriptions" type="submit" style="float: right">
|
||||
</p>
|
||||
</form>
|
||||
{% else %}
|
||||
<table class="form-area">
|
||||
<tr>
|
||||
<th>Newsletter</th>
|
||||
</tr>
|
||||
{% for newsletter in object_list %}
|
||||
<tr>
|
||||
<td><a href="{{ url('newsletter_detail', newsletter.slug) }}">{{ newsletter.title }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endblock %}
|
13
templates/newsletter/submission_archive.html
Normal file
13
templates/newsletter/submission_archive.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}{{ object.title }} - Newsletter Archive - {{ SITE_LONG_NAME }}{% endblock %}
|
||||
{% block content_title %}Newsletter Archive: {{ object.title }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<ul>
|
||||
{% for submission in latest %}
|
||||
<li><a href="{{ submission.get_absolute_url }}">{{ submission }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<hr>
|
||||
<p><a href="{{ url('newsletter_list') }}">Back to list</a></p>
|
||||
{% endblock %}
|
12
templates/newsletter/subscription_activate.html
Normal file
12
templates/newsletter/subscription_activate.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Newsletter {{ newsletter.title }} {{ action }} activate{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>Newsletter {{ newsletter.title }} {{ action }} activate</h1>
|
||||
<form enctype="multipart/form-data" method="post" action=".">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p() }}
|
||||
<p><input id="id_submit" name="submit" type="submit" value="Activate"></p>
|
||||
</form>
|
||||
{% endblock %}
|
19
templates/newsletter/subscription_subscribe.html
Normal file
19
templates/newsletter/subscription_subscribe.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Newsletter subscribe{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>Newsletter subscribe {{ newsletter.title }}</h1>
|
||||
{% if error %}
|
||||
<p>
|
||||
Due to a technical error we were not able to submit your confirmation email.
|
||||
This could be because your email address is invalid.
|
||||
</p>
|
||||
{% else %}
|
||||
<form enctype="multipart/form-data" method="post" action=".">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p() }}
|
||||
<p><input id="id_submit" name="submit" value="Subscribe" type="submit"></p>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Newsletter {{ newsletter.title }} {{ action }} activate{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Newsletter {{ newsletter.title }} {{ action }} activate</h1>
|
||||
<p>Your subscription has successfully been activated.</p>
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Newsletter subscribe{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>Newsletter subscribe {{ newsletter.title }}</h1>
|
||||
<p>Your subscription request was successfully received and an activation email has been sent to you. In that email
|
||||
you will find a link which you need to follow in order to activate your subscription.</p>
|
||||
{% endblock %}
|
22
templates/newsletter/subscription_subscribe_user.html
Normal file
22
templates/newsletter/subscription_subscribe_user.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Newsletter subscribe{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>Newsletter subscribe {{ newsletter.title }}</h1>
|
||||
<p>Welcome, {{ request.user }}!</p>
|
||||
{% if messages %}
|
||||
<ul>
|
||||
{% for message in messages %}
|
||||
<li>{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
Do you want to subscribe to this newsletter?
|
||||
<form enctype="multipart/form-data" method="post"
|
||||
action="{{ url('newsletter_subscribe_confirm', newsletter.slug) }}">
|
||||
{% csrf_token %}
|
||||
<p><input id="id_submit" name="submit" value="Subscribe" type="submit"></p>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
17
templates/newsletter/subscription_unsubscribe.html
Normal file
17
templates/newsletter/subscription_unsubscribe.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Newsletter unsubscribe{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<h1>Newsletter unsubscribe {{ newsletter.title }}</h1>
|
||||
{% if error %}
|
||||
<p>Due to a technical error we were not able to submit your confirmation email.
|
||||
This could be because your email address is invalid.</p>
|
||||
{% else %}
|
||||
<form enctype="multipart/form-data" method="post" action=".">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p() }}
|
||||
<p><input id="id_submit" name="submit" value="Unsubscribe" type="submit"></p>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,9 @@
|
|||
{% extends "newsletter/common.html" %}
|
||||
|
||||
{% block title %}{{ _('Newsletter') }} {{ newsletter.title }} {{ action }} {{ _('activate') }}{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{ _('Newsletter') }} {{ newsletter.title }} {{ action }} {{ _('activate') }}</h1>
|
||||
|
||||
<p>{{ _('You have successfully been unsubscribed.') }}</p>
|
||||
{% endblock body %}
|
|
@ -0,0 +1,9 @@
|
|||
{% extends "newsletter/common.html" %}
|
||||
|
||||
{% block title %}{{ _('Newsletter unsubscribe') }}{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{ _('Newsletter unsubscribe') }} {{ newsletter.title }}</h1>
|
||||
|
||||
<p>{{ _('Your unsubscription request has successfully been received. An email has been sent to you with a link you need to follow in order to confirm your unsubscription.') }}</p>
|
||||
{% endblock body %}
|
25
templates/newsletter/subscription_unsubscribe_user.html
Normal file
25
templates/newsletter/subscription_unsubscribe_user.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
{% extends "newsletter/common.html" %}
|
||||
|
||||
{% block title %}{{ _('Newsletter unsubscribe') }}{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{ _('Newsletter unsubscribe') }} {{ newsletter.title }}</h1>
|
||||
|
||||
<p>Welcome, {{ request.user }}!</p>
|
||||
|
||||
{% if messages %}
|
||||
<ul>
|
||||
{% for message in messages %}
|
||||
<li>{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{{ _('Do you want to unsubscribe from this newsletter?') }}
|
||||
<form enctype="multipart/form-data" method="post"
|
||||
action="{{ url('newsletter_unsubscribe_confirm', newsletter.slug) }}">
|
||||
{% csrf_token %}
|
||||
<p><input id="id_submit" name="submit" value="{{ _('Unsubscribe') }}" type="submit"></p>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% endblock body %}
|
17
templates/newsletter/subscription_update.html
Normal file
17
templates/newsletter/subscription_update.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% extends "newsletter/common.html" %}
|
||||
|
||||
{% block title %}{{ _('Newsletter update') }}{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{ _('Newsletter update') }} {{ newsletter.title }}</h1>
|
||||
|
||||
{% if error %}
|
||||
<p>{{ _('Due to a technical error we were not able to submit your confirmation email. This could be because your email address is invalid.') }}</p>
|
||||
{% else %}
|
||||
<form enctype="multipart/form-data" method="post" action=".">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p() }}
|
||||
<p><input id="id_submit" name="submit" value="{{ _('Update subscription') }}" type="submit"></p>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock body %}
|
9
templates/newsletter/subscription_update_activated.html
Normal file
9
templates/newsletter/subscription_update_activated.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% extends "newsletter/common.html" %}
|
||||
|
||||
{% block title %}{{ _('Newsletter') }} {{ newsletter.title }} {{ action }} {{ _('activate') }}{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{ _('Newsletter') }} {{ newsletter.title }} {{ action }} {{ _('activate') }}</h1>
|
||||
|
||||
<p>{{ _('Your subscription has successfully been updated.') }}</p>
|
||||
{% endblock body %}
|
9
templates/newsletter/subscription_update_email_sent.html
Normal file
9
templates/newsletter/subscription_update_email_sent.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% extends "newsletter/common.html" %}
|
||||
|
||||
{% block title %}{{ _('Newsletter update') }}{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
<h1>{{ _('Newsletter update') }} {{ newsletter.title }}</h1>
|
||||
|
||||
<p>{{ _('Your update request was successfully received and an activation email has been sent to you. In that email you will find a link which you need to follow in order to update your subscription.') }}</p>
|
||||
{% endblock body %}
|
48
templates/organization/edit.html
Normal file
48
templates/organization/edit.html
Normal file
|
@ -0,0 +1,48 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block js_media %}
|
||||
{{ form.media.js }}
|
||||
<script type="text/javascript">
|
||||
window.django = {jQuery: $};
|
||||
|
||||
function pluralidx(count) {
|
||||
return (count == 1) ? 0 : 1;
|
||||
}
|
||||
|
||||
function gettext(msgid) {
|
||||
return msgid;
|
||||
}
|
||||
|
||||
function interpolate(fmt, obj, named) {
|
||||
if (named) {
|
||||
return fmt.replace(/%\(\w+\)s/g, function (match) {
|
||||
return String(obj[match.slice(2, -2)])
|
||||
});
|
||||
} else {
|
||||
return fmt.replace(/%s/g, function (match) {
|
||||
return String(obj.shift())
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block media %}
|
||||
{{ form.media.css }}
|
||||
<link rel="stylesheet" href="{{ static('admin/css/widgets.css') }}" type="text/css">
|
||||
<link rel="stylesheet" href="{{ static('admin/css/pagedown.css') }}" type="text/css">
|
||||
<link rel="stylesheet" href="{{ static('problem_edit.css') }}" type="text/css">
|
||||
<style>
|
||||
#id_about {
|
||||
width: 500px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form action="" method="post" class="form-area">
|
||||
{% csrf_token %}
|
||||
<table border="0" style="text-align:left">{{ form.as_table() }}</table>
|
||||
<button type="submit">{{ _('Update') }}</button>
|
||||
</form>
|
||||
{% endblock %}
|
63
templates/organization/home.html
Normal file
63
templates/organization/home.html
Normal file
|
@ -0,0 +1,63 @@
|
|||
{% extends "common-content.html" %}
|
||||
|
||||
{% block content_js_media %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$('.leave-organization').click(function () {
|
||||
return confirm('{{ _('Are you sure you want to leave this organization?') }}\n' +
|
||||
{% if organization.is_open %}
|
||||
'{{ _('You will have to rejoin to show up on the organization leaderboard.') }}'
|
||||
{% else %}
|
||||
'{{ _('You will have to request membership in order to join again.') }}'
|
||||
{% endif %}
|
||||
);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block info_float %}
|
||||
{% if request.user.is_authenticated %}
|
||||
{% if request.profile in organization %}
|
||||
<form method="post" action="{{ url('leave_organization', organization.id, organization.slug) }}">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="unselectable button full leave-organization" value="{{ _('Leave organization') }}">
|
||||
</form>
|
||||
{% elif organization.is_open %}
|
||||
<form method="post" action="{{ url('join_organization', organization.id, organization.slug) }}">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="unselectable button full" value="{{ _('Join organization') }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<a href="{{ url('request_organization', organization.id, organization.slug) }}"
|
||||
class="unselectable button full">{{ _('Request membership') }}</a>
|
||||
{% endif %}
|
||||
<hr>
|
||||
{% endif %}
|
||||
|
||||
{% if can_edit %}
|
||||
<div><a href="{{ url('edit_organization', organization.id, organization.slug) }}">{{ _('Edit organization') }}</a></div>
|
||||
|
||||
{% if not organization.is_open %}
|
||||
<div>
|
||||
<a href="{{ url('organization_requests_pending', organization.id, organization.slug) }}">{{ _('View requests') }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if perms.judge.change_organization %}
|
||||
<div>
|
||||
<a href="{{ url('admin:judge_organization_change', organization.id) }}">{{ _('Admin organization') }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div>
|
||||
<a href="{{ organization.get_users_url() }}">{{ _('View members') }}</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block description %}
|
||||
{% cache 3600 'organization_html' organization.id MATH_ENGINE %}
|
||||
{{ organization.about|markdown('organization-about', MATH_ENGINE)|reference|str|safe }}
|
||||
{% endcache %}
|
||||
{% endblock %}
|
36
templates/organization/list.html
Normal file
36
templates/organization/list.html
Normal file
|
@ -0,0 +1,36 @@
|
|||
{% extends "base.html" %}
|
||||
{% block js_media %}
|
||||
<script src="{{ static('libs/tablesorter.js') }}" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$("#organization-table").tablesorter();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title_ruler %}{% endblock %}
|
||||
|
||||
{% block title_row %}
|
||||
{% set tab = 'organizations' %}
|
||||
{% set title = _('Organizations') %}
|
||||
{% include "user/user-list-tabs.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<table id="organization-table" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:85%">{{ _('Name') }}</th>
|
||||
<th>{{ _('Members') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for org in organizations %}
|
||||
<tr>
|
||||
<td><a href="{{ org.get_absolute_url() }}">{{ org.name }}</a></td>
|
||||
<td><a href="{{ org.get_users_url() }}">{{ org.member_count }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
12
templates/organization/new.html
Normal file
12
templates/organization/new.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block js_media %}{{ form.media.js }}{% endblock %}
|
||||
{% block media %}{{ form.media.css }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form action="" method="post" class="form-area">
|
||||
{% csrf_token %}
|
||||
<table border="0" style="text-align:left">{{ form.as_table() }}</table>
|
||||
<button type="submit">{{ _('Create') }}</button>
|
||||
</form>
|
||||
{% endblock %}
|
4
templates/organization/preview.html
Normal file
4
templates/organization/preview.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
{{ preview_data|markdown('organization-about', MATH_ENGINE)|reference|str|safe }}
|
||||
{% if REQUIRE_JAX %}
|
||||
<div data-config="{{ static('mathjax_config.js') }}" class="require-mathjax-support"></div>
|
||||
{% endif %}
|
35
templates/organization/requests/detail.html
Normal file
35
templates/organization/requests/detail.html
Normal file
|
@ -0,0 +1,35 @@
|
|||
{% extends "base.html" %}
|
||||
{% block media %}
|
||||
<style>
|
||||
th {
|
||||
text-align: left
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<table>
|
||||
<tr>
|
||||
<th>{{ _('User:') }}</th>
|
||||
<td>{{ link_user(object.user) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{ _('Organization:') }}</th>
|
||||
<td>
|
||||
{% with org=object.organization %}
|
||||
<a href="{{ org.get_absolute_url() }}">{{ org.name }}</a>
|
||||
{% endwith %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{ _('Time:') }}</th>
|
||||
<td>{{ object.time|date(_("N j, Y, g:i a")) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="2">{{ _('Reason:') }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" style="padding-left: 2em">{{ object.reason }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% endblock %}
|
30
templates/organization/requests/log.html
Normal file
30
templates/organization/requests/log.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block body %}
|
||||
{% include "organization/requests/tabs.html" %}
|
||||
|
||||
{% if requests %}
|
||||
<table>
|
||||
<tr>
|
||||
<th>{{ _('User') }}</th>
|
||||
<th>{{ _('Time') }}</th>
|
||||
<th>{{ _('State') }}</th>
|
||||
<th>{{ _('Reason') }}</th>
|
||||
</tr>
|
||||
{% for r in requests %}
|
||||
<tr id="request-{{ r.id }}">
|
||||
<td>{{ link_user(r.user) }}</td>
|
||||
<td>
|
||||
<a href="{{ url('request_organization_detail', object.id, object.slug, r.id) }}">
|
||||
{{- r.time|date(_("N j, Y, H:i")) -}}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ r.state }}</td>
|
||||
<td>{{ r.reason|truncatechars(50) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p>{{ _('There are no requests to approve.') }}</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
39
templates/organization/requests/pending.html
Normal file
39
templates/organization/requests/pending.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
{% extends "base.html" %}
|
||||
{% block body %}
|
||||
{% include "messages.html" %}
|
||||
{% include "organization/requests/tabs.html" %}
|
||||
|
||||
{% if formset.forms %}
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
{{ formset.management_form }}
|
||||
<table>
|
||||
<tr>
|
||||
<th>{{ _('User') }}</th>
|
||||
<th>{{ _('Time') }}</th>
|
||||
<th>{{ _('State') }}</th>
|
||||
<th>{{ _('Reason') }}</th>
|
||||
{% if formset.can_delete %}
|
||||
<th>{{ _('Delete?') }}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% for form in formset %}
|
||||
<tr id="request-{{ form.instance.id }}">
|
||||
<td>{{ form.id }}{{ link_user(form.instance.user) }}</td>
|
||||
<td><a href="{{ url('request_organization_detail', object.id, object.slug, form.instance.id) }}">
|
||||
{{ form.instance.time|date(_("N j, Y, H:i")) }}
|
||||
</a></td>
|
||||
<td>{{ form.state }}</td>
|
||||
<td>{{ form.instance.reason|truncatechars(50) }}</td>
|
||||
{% if formset.can_delete %}
|
||||
<td>{{ form.DELETE }}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<button type="submit">{{ _('Update') }}</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>{{ _('There are no requests to approve.') }}</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
22
templates/organization/requests/request.html
Normal file
22
templates/organization/requests/request.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block js_media %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$('#id_reason').keydown(function (e) {
|
||||
if (e.ctrlKey && (e.keyCode === 13 || e.keyCode === 10)) {
|
||||
$(this).closest('form').submit();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form action="" method="post" class="form-area">
|
||||
{% csrf_token %}
|
||||
<p><label for="{{ form.reason.id_for_label }}"><b>{{ _('Your reason for joining:') }}</b></label></p>
|
||||
<p>{{ form.reason }}</p>
|
||||
<button type="submit">{{ _('Request') }}</button>
|
||||
</form>
|
||||
{% endblock %}
|
16
templates/organization/requests/tabs.html
Normal file
16
templates/organization/requests/tabs.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<div class="tabs">
|
||||
<ul>
|
||||
<li{% if tab == 'pending' %} class="active"{% endif %}>
|
||||
<a href="{{ url('organization_requests_pending', object.id, object.slug) }}">{{ _('Pending') }}</a>
|
||||
</li>
|
||||
<li{% if tab == 'log' %} class="active"{% endif %}>
|
||||
<a href="{{ url('organization_requests_log', object.id, object.slug) }}">{{ _('Log') }}</a>
|
||||
</li>
|
||||
<li{% if tab == 'approved' %} class="active"{% endif %}>
|
||||
<a href="{{ url('organization_requests_approved', object.id, object.slug) }}">{{ _('Approved') }}</a>
|
||||
</li>
|
||||
<li{% if tab == 'rejected' %} class="active"{% endif %}>
|
||||
<a href="{{ url('organization_requests_rejected', object.id, object.slug) }}">{{ _('Rejected') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
19
templates/organization/users-table.html
Normal file
19
templates/organization/users-table.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends "user/users-table.html" %}
|
||||
|
||||
{% block before_point_head %}
|
||||
{% if is_admin %}
|
||||
<th class="header"></th>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block before_point %}
|
||||
{% if is_admin %}
|
||||
<td>
|
||||
<form action="{{ kick_url }}" method="POST" class="kick-form">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="user" value="{{ user.id }}">
|
||||
<a href="#" class="button">{{ _('Kick') }}</a>
|
||||
</form>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endblock %}
|
33
templates/organization/users.html
Normal file
33
templates/organization/users.html
Normal file
|
@ -0,0 +1,33 @@
|
|||
{% extends "user/base-users.html" %}
|
||||
{% block users_media %}
|
||||
<style>
|
||||
#users-table td:nth-child(2), #users-table th:nth-child(2) {
|
||||
border-right: none;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.kick-form .button {
|
||||
margin: -8px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% if is_admin %}
|
||||
<style>#users-table td:nth-child(3), #users-table th:nth-child(3) {
|
||||
border-right: none;
|
||||
}
|
||||
</style>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block users_js_media %}
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$('form.kick-form').find('a.button').click(function () {
|
||||
$(this).parent().submit();
|
||||
return false;
|
||||
})
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block users_table %}{% include "organization/users-table.html" %}{% endblock %}
|
14
templates/pagedown.html
Normal file
14
templates/pagedown.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<div id="{{ id|safe }}-wmd-wrapper" class="wmd-wrapper">
|
||||
<div class="wmd-panel">
|
||||
<div id="{{ id|safe }}_wmd_button_bar"></div>
|
||||
<textarea{{ attrs|safe }}>{{ body }}</textarea>
|
||||
</div>
|
||||
|
||||
{% if show_preview %}
|
||||
<div id="{{ id|safe }}-preview" data-preview-url="{{ preview_url }}" data-textarea-id="{{ id }}"
|
||||
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-content content-description"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
9
templates/pagedown/widgets/default.html
Normal file
9
templates/pagedown/widgets/default.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<div class="wmd-wrapper" id="{{ id }}-wmd-wrapper">
|
||||
<div class="wmd-panel">
|
||||
<div id="{{ id|safe }}_wmd_button_bar"></div>
|
||||
<textarea{{ attrs|safe }}>{{ body }}</textarea>
|
||||
</div>
|
||||
{% if show_preview %}
|
||||
<div id="{{ id|safe }}_wmd_preview" class="wmd-panel wmd-preview content-description"></div>
|
||||
{% endif %}
|
||||
</div>
|
42
templates/problem/clone.html
Normal file
42
templates/problem/clone.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block media %}
|
||||
<style>
|
||||
#problem-clone-panel {
|
||||
position: relative;
|
||||
margin: 5em auto auto -10em;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
#problem-code-container {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
#id_code {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ul.errorlist {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form id="problem-clone-panel" action="" method="post" class="form-area">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
<div id="form-errors">
|
||||
{{ form.code.errors }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div><label class="inline-header grayed">{{ _('Enter a new code for the cloned problem:') }}</label></div>
|
||||
<div id="problem-code-container"><span class="fullwidth">{{ form.code }}</span></div>
|
||||
<hr>
|
||||
<button style="float:right;" type="submit">{{ _('Clone!') }}</button>
|
||||
</form>
|
||||
{% endblock %}
|
451
templates/problem/data.html
Normal file
451
templates/problem/data.html
Normal file
|
@ -0,0 +1,451 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block js_media %}
|
||||
<script type="text/javascript">
|
||||
window.valid_files = {{valid_files_json}};
|
||||
|
||||
$(function () {
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript" src="{{ static('libs/jquery-sortable.js') }}"></script>
|
||||
<script type="text/javascript" src="{{ static('libs/featherlight/featherlight.min.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
function update_select2() {
|
||||
$('tbody:not(.extra-row-body) .type-column select').select2({
|
||||
minimumResultsForSearch: -1
|
||||
});
|
||||
}
|
||||
|
||||
update_select2();
|
||||
|
||||
function autofill_if_exists($select, file) {
|
||||
if (!$select.val() && ~window.valid_files.indexOf(file))
|
||||
$select.val(file).trigger('change');
|
||||
}
|
||||
|
||||
var $table = $('#case-table');
|
||||
$table.on('add-row', function (e, $tr) {
|
||||
update_select2();
|
||||
$tr.find('input').filter('[id$=file]').each(function () {
|
||||
var $select, val = $(this).replaceWith($select = $('<select>').attr({
|
||||
id: $(this).attr('id'),
|
||||
name: $(this).attr('name'),
|
||||
style: 'width: 100%'
|
||||
})).val();
|
||||
$select.select2({
|
||||
data: window.valid_files,
|
||||
allowClear: true,
|
||||
placeholder: ''
|
||||
}).val(val).trigger('change').on('change', function () {
|
||||
var val = $select.val();
|
||||
if (val) {
|
||||
if ($select.attr('id').endsWith('input_file'))
|
||||
autofill_if_exists($tr.find('select[id$=output_file]'), val.replace(/in(?!.*?in)/, 'out'));
|
||||
else
|
||||
autofill_if_exists($tr.find('select[id$=input_file]'), val.replace(/out(?!.*?out)/, 'in'));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var order = 0;
|
||||
|
||||
function handle_table_reorder() {
|
||||
var in_batch = false;
|
||||
$table.find('tbody:first tr').each(function () {
|
||||
switch ($(this).attr('data-type')) {
|
||||
case 'C':
|
||||
$(this).find('input[id$=points], input[id$=pretest]').toggle(!in_batch);
|
||||
break;
|
||||
case 'S':
|
||||
in_batch = true;
|
||||
break;
|
||||
case 'E':
|
||||
in_batch = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function try_parse_json(json) {
|
||||
try {
|
||||
return JSON.parse(json);
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function checker_precision($checker) {
|
||||
var $td = $checker.parent();
|
||||
var $args = $td.find('input');
|
||||
var $precision = $('<input>', {
|
||||
type: 'number',
|
||||
value: try_parse_json($args.val()).precision || 6,
|
||||
title: 'precision (decimal digits)',
|
||||
style: 'width: 4em'
|
||||
}).change(function () {
|
||||
if ($checker.val().startsWith('floats'))
|
||||
$args.val(JSON.stringify({precision: parseInt($(this).val())}));
|
||||
else
|
||||
$args.val('');
|
||||
}).appendTo($td);
|
||||
|
||||
$checker.change(function () {
|
||||
$precision.toggle($checker.val().startsWith('floats')).change();
|
||||
}).change();
|
||||
}
|
||||
|
||||
function swap_row($a, $b) {
|
||||
var $a_order = $a.find('input[id$=order]'), $b_order = $b.find('input[id$=order]');
|
||||
var order = $a_order.val();
|
||||
$a_order.val($b_order.val());
|
||||
$b_order.val(order);
|
||||
$b.after($a);
|
||||
$a.find('span.order').text($a_order.val());
|
||||
$b.find('span.order').text($b_order.val());
|
||||
handle_table_reorder();
|
||||
}
|
||||
|
||||
checker_precision($('#id_problem-data-checker'));
|
||||
|
||||
$table.on('add-row', function (e, $tr) {
|
||||
var $order = $tr.find('input').filter('[id$=order]').attr('type', 'hidden').val(++order);
|
||||
$order.after($('<span>', {'class': 'order'}).text($order.val()))
|
||||
.after($('<i>', {'class': 'fa fa-fw fa-lg fa-ellipsis-v'}));
|
||||
|
||||
var $opts = $tr.find('input').slice(2, 6);
|
||||
var $files = $tr.find('select').slice(1, 3);
|
||||
var $checker = $files.end().last();
|
||||
$tr.find('select[id$=type]').change(function () {
|
||||
var $this = $(this), val = $this.val(), disabled;
|
||||
switch (val) {
|
||||
case 'S':
|
||||
case 'E':
|
||||
disabled = val == 'S';
|
||||
$opts.toggle(val == 'S');
|
||||
$files.siblings('.select2').hide();
|
||||
$checker.toggle(val == 'S');
|
||||
break;
|
||||
default:
|
||||
$opts.toggle(val == 'C');
|
||||
$files.siblings('.select2').toggle(val == 'C');
|
||||
$checker.toggle(val == 'C');
|
||||
var $prevs = $tr.prevAll('tr[data-type=S], tr[data-type=E]');
|
||||
disabled = $prevs.length && $prevs.get(0).getAttribute('data-type') == 'S';
|
||||
$tr.find('input[id$=points], input[id$=pretest]').toggle(val == 'C' && !disabled);
|
||||
}
|
||||
$tr.attr('data-type', val).nextUntil('tr[data-type=S], tr[data-type=E], tr[data-type=""]')
|
||||
.find('input[id$=points], input[id$=pretest]').toggle(!disabled);
|
||||
}).change();
|
||||
|
||||
var tooltip_classes = 'tooltipped tooltipped-s';
|
||||
$tr.find('a.edit-generator-args').mouseover(function () {
|
||||
switch ($tr.attr('data-type')) {
|
||||
case 'C':
|
||||
case 'S':
|
||||
var $this = $(this).addClass(tooltip_classes);
|
||||
$this.attr('aria-label', $this.prev().val() || '(none)');
|
||||
}
|
||||
}).mouseout(function () {
|
||||
$(this).removeClass(tooltip_classes).removeAttr('aria-label');
|
||||
}).featherlight($('.generator-args-editor'), {
|
||||
beforeOpen: function () {
|
||||
switch ($tr.attr('data-type')) {
|
||||
case 'C':
|
||||
case 'S':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
afterOpen: function () {
|
||||
var $input = this.$currentTarget.prev();
|
||||
this.$instance.find('.generator-args-editor')
|
||||
.find('textarea').val($input.val()).end()
|
||||
.find('.button').click(function () {
|
||||
$input.val($(this).prev().val());
|
||||
$.featherlight.current().close();
|
||||
}).end()
|
||||
.show();
|
||||
}
|
||||
});
|
||||
|
||||
checker_precision($tr.find('select[id$=checker]'));
|
||||
}).find('tbody:first').find('tr').each(function () {
|
||||
$table.trigger('add-row', [$(this)]);
|
||||
});
|
||||
|
||||
$('form').submit(function () {
|
||||
$table.find('tbody:first').find('tr').each(function () {
|
||||
var filled = false;
|
||||
$(this).find('input, select').each(function () {
|
||||
var $this = $(this);
|
||||
if (!$this.attr('name'))
|
||||
return;
|
||||
if ($this.attr('type') === 'checkbox')
|
||||
filled |= $this.is(':checked');
|
||||
else if (!$this.attr('name').endsWith('order'))
|
||||
filled |= !!$this.val();
|
||||
});
|
||||
if (!filled)
|
||||
$(this).find('input[id$=order]').val('');
|
||||
});
|
||||
});
|
||||
|
||||
var $total = $('#id_cases-TOTAL_FORMS');
|
||||
|
||||
$('a#add-case-row').click(function () {
|
||||
var $tr;
|
||||
$table.find('tbody:first').append($tr = $($table.find('.extra-row-body').html()
|
||||
.replace(/__prefix__/g, $total.val())));
|
||||
$tr.find('.type-column select option[value="C"]').attr('selected', true);
|
||||
$total.val(parseInt($total.val()) + 1);
|
||||
$table.trigger('add-row', [$tr]);
|
||||
window.scrollBy(0, $tr.height());
|
||||
return false;
|
||||
});
|
||||
|
||||
var oldIndex;
|
||||
$table.sortable({
|
||||
containerSelector: 'table',
|
||||
itemPath: '> tbody:first',
|
||||
itemSelector: 'tr',
|
||||
handle: 'i.fa-ellipsis-v',
|
||||
placeholder: '<tr class="placeholder">',
|
||||
onDragStart: function ($item, container, _super) {
|
||||
oldIndex = $item.index();
|
||||
_super($item, container);
|
||||
},
|
||||
onDrop: function ($item, container, _super) {
|
||||
var newIndex = $item.index();
|
||||
if (newIndex > oldIndex) {
|
||||
var order = parseInt($item.parent().children().slice(oldIndex, newIndex).each(function () {
|
||||
var $order = $(this).find('input[id$=order]');
|
||||
$order.val(parseInt($order.val()) - 1).siblings('span.order').text($order.val());
|
||||
}).last().after($item).find('input[id$=order]').val());
|
||||
$item.find('input[id$=order]').val(order + 1).siblings('span.order').text(order + 1);
|
||||
} else if (newIndex < oldIndex) {
|
||||
var order = parseInt($item.parent().children().slice(newIndex + 1, oldIndex + 1).each(function () {
|
||||
var $order = $(this).find('input[id$=order]');
|
||||
$order.val(parseInt($order.val()) + 1).siblings('span.order').text($order.val());
|
||||
}).first().before($item).find('input[id$=order]').val());
|
||||
$item.find('input[id$=order]').val(order - 1).siblings('span.order').text(order - 1);
|
||||
}
|
||||
if (newIndex != oldIndex)
|
||||
handle_table_reorder();
|
||||
_super($item, container);
|
||||
}
|
||||
});
|
||||
|
||||
var $controls = $('#column-visible');
|
||||
var problem = $controls.attr('data-problem');
|
||||
$controls.find('input').change(function () {
|
||||
var $this = $(this), suffix = $this.attr('data-suffix'), checked = $this.is(':checked');
|
||||
$table.find('.' + suffix.replace(/_/g, '-')).toggle(checked);
|
||||
localStorage.setItem('data-visible:' + problem + ':' + suffix, checked ? '1' : '0')
|
||||
}).each(function () {
|
||||
var $this = $(this), suffix = $this.attr('data-suffix'), filled = false;
|
||||
filled = localStorage.getItem('data-visible:' + problem + ':' + suffix);
|
||||
if (filled !== null)
|
||||
filled = filled == '1';
|
||||
else {
|
||||
filled = false;
|
||||
$table.find('[id$=' + suffix + ']').each(function () {
|
||||
filled |= !!$(this).val();
|
||||
});
|
||||
}
|
||||
$this.prop('checked', filled).trigger('change');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block media %}
|
||||
<style>
|
||||
#case-table .select2 {
|
||||
text-align: initial;
|
||||
}
|
||||
|
||||
.order-column {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
.bad-file input, .bad-file .select2-selection {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
span.order {
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
body.dragging, body.dragging * {
|
||||
cursor: move !important;
|
||||
}
|
||||
|
||||
.dragged {
|
||||
position: absolute;
|
||||
opacity: 0.5;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
tr.placeholder {
|
||||
display: block;
|
||||
background: red;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
tr.placeholder:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 5px solid transparent;
|
||||
border-left-color: red;
|
||||
margin-top: -5px;
|
||||
left: -5px;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
i.fa-ellipsis-v {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.edit-generator-args {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.generator-args-editor textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 0.5em;
|
||||
height: 8em;
|
||||
}
|
||||
|
||||
.generator-args-editor .button {
|
||||
display: block;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#case-table tbody td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.type-column {
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
ul.errorlist {
|
||||
border: 3px red solid;
|
||||
border-radius: 5px;
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
background: #e99;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
{% if data_form.instance.has_yml %}
|
||||
<div class="title-line-action">
|
||||
[<a href="{{ url('problem_data_init', problem.code) }}">{{ _('View YAML') }}</a>]
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% if data_form.instance.feedback %}
|
||||
<ul class="errorlist">
|
||||
<li>{{ data_form.instance.feedback }}</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
<form action="" method="POST" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ cases_formset.management_form }}
|
||||
<table class="table">{{ data_form.as_table() }}</table>
|
||||
<div id="column-visible" data-problem="{{ problem.code }}">
|
||||
<strong>{{ _('Show columns:') }}</strong>
|
||||
<label>
|
||||
<input type="checkbox" data-suffix="output_prefix">
|
||||
{{ _('Output prefix') }}
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" data-suffix="output_limit">
|
||||
{{ _('Output limit') }}
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" data-suffix="checker">
|
||||
{{ _('Checker') }}
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" data-suffix="generator_args">
|
||||
{{ _('Generator args') }}
|
||||
</label>
|
||||
</div>
|
||||
<table id="case-table" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="order-column"></th>
|
||||
<th class="type-column">{{ _('Type') }}</th>
|
||||
<th>{{ _('Input file') }}</th>
|
||||
<th>{{ _('Output file') }}</th>
|
||||
<th>{{ _('Points') }}</th>
|
||||
<th>{{ _('Pretest?') }}</th>
|
||||
<th class="output-prefix">{{ _('Output prefix') }}</th>
|
||||
<th class="output-limit">{{ _('Output limit') }}</th>
|
||||
<th class="checker">{{ _('Checker') }}</th>
|
||||
<th class="generator-args">{{ _('Generator args') }}</th>
|
||||
{% if cases_formset.can_delete %}
|
||||
<th>{{ _('Delete?') }}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for form in all_case_forms %}
|
||||
{% if form.non_field_errors() %}
|
||||
<tr>
|
||||
<td colspan="{{ 9 + cases_formset.can_delete }}">{{ form.non_field_errors() }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if form.prefix and '__prefix__' in form.prefix %}
|
||||
</tbody>
|
||||
<tbody class="extra-row-body" style="display: none">
|
||||
{% endif %}
|
||||
<tr data-type="{{ form['type'].value() }}">
|
||||
<td>{{ form.id }}{{ form.order.errors }}{{ form.order }}</td>
|
||||
<td class="type-column">{{ form.type.errors }}{{ form.type }}</td>
|
||||
<td{% if not (form.empty_permitted or form['type'].value() != 'C' or
|
||||
form['input_file'].value() in valid_files) %} class="bad-file"{% endif %}>
|
||||
{{ form.input_file.errors }}{{ form.input_file }}
|
||||
</td>
|
||||
<td{% if not (form.empty_permitted or form['type'].value() != 'C' or
|
||||
form['output_file'].value() in valid_files) %} class="bad-file"{% endif %}>
|
||||
{{ form.output_file.errors }}{{ form.output_file }}
|
||||
</td>
|
||||
<td>{{ form.points.errors }}{{ form.points }}</td>
|
||||
<td>{{ form.is_pretest.errors }}{{ form.is_pretest }}</td>
|
||||
<td class="output-prefix">{{ form.output_prefix.errors }}{{ form.output_prefix }}</td>
|
||||
<td class="output-limit">{{ form.output_limit.errors }}{{ form.output_limit }}</td>
|
||||
<td class="checker">
|
||||
{{ form.checker.errors }}{{ form.checker }}{{ form.checker_args.errors }}{{ form.checker_args }}
|
||||
</td>
|
||||
<td class="generator-args">{{ form.generator_args.errors }}{{ form.generator_args }}
|
||||
<a href="javascript:void(0)" class="edit-generator-args">
|
||||
<i class="fa fa-pencil"></i>
|
||||
{{ _('Edit') }}
|
||||
</a>
|
||||
</td>
|
||||
{% if cases_formset.can_delete %}
|
||||
<td>{{ form.DELETE }}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<input type="submit" value="{{ _('Submit!') }}" class="button">
|
||||
<a id="add-case-row" href="#"><i class="fa fa-plus"></i> {{ _('Add new case') }}</a>
|
||||
</form>
|
||||
<div style="display: none" class="generator-args-editor"><textarea></textarea><a class="button">Save</a></div>
|
||||
{% endblock %}
|
40
templates/problem/editorial.html
Normal file
40
templates/problem/editorial.html
Normal file
|
@ -0,0 +1,40 @@
|
|||
{% extends "common-content.html" %}
|
||||
|
||||
{% block content_js_media %}
|
||||
{% include "comments/media-js.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content_media %}
|
||||
{% include "comments/media-css.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<div style="float:right">
|
||||
{% if perms.judge.change_problem %}
|
||||
[<a href="{{ url('admin:judge_problem_change', problem.id) }}">{{ _('Edit') }}</a>]
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="content-description">
|
||||
{% if not has_solved_problem %}
|
||||
<div class="alert alert-danger">{{ _('Remember to use this editorial <b>only</b> when stuck, and <b>not to copy-paste code from it</b>. Please be respectful to the problem author and editorialist. <br><br> <b>Submitting an official solution before solving the problem yourself is a bannable offence.</b>') }}</div>
|
||||
{% endif %}
|
||||
{% with authors=solution.authors.all() %}
|
||||
{% if authors %}
|
||||
<p>Authors: {{ link_users(authors) }}</p>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{{ solution.content|markdown('solution', MATH_ENGINE)|reference|str|safe }}
|
||||
</div>
|
||||
<hr>
|
||||
{% include "comments/list.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block bodyend %}
|
||||
{% if REQUIRE_JAX %}
|
||||
{% include "mathjax-load.html" %}
|
||||
{% endif %}
|
||||
{% include "comments/math.html" %}
|
||||
{% endblock %}
|
311
templates/problem/list.html
Normal file
311
templates/problem/list.html
Normal file
|
@ -0,0 +1,311 @@
|
|||
{% extends "common-content.html" %}
|
||||
|
||||
{% block media %}
|
||||
<link rel="stylesheet" href="{{ static('libs/nouislider.min.css') }}">
|
||||
<noscript>
|
||||
<style>#category, #types {
|
||||
visibility: visible;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
{% if not request.in_contest %}
|
||||
<style>
|
||||
#problem-table th {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a.hot-problem-link:hover > .hot-problem-count {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
span.hot-problem-count {
|
||||
color: #555;
|
||||
font-size: 0.75em;
|
||||
vertical-align: super;
|
||||
visibility: hidden;
|
||||
padding-left: 0.25em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ul.problem-list {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block js_media %}
|
||||
<script>
|
||||
window.point_start = {{point_start}};
|
||||
window.point_end = {{point_end}};
|
||||
window.point_values = {{point_values|json|safe}};
|
||||
</script>
|
||||
{% compress js %}
|
||||
<script src="{{ static('libs/nouislider.min.js') }}" type="text/javascript"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
var $form = $('form#filter-form');
|
||||
var $search = $('#search');
|
||||
var $category = $('#category');
|
||||
|
||||
function prep_form() {
|
||||
$search.prop('disabled', !$search.val());
|
||||
$category.prop('disabled', !$category.val());
|
||||
}
|
||||
|
||||
function clean_submit() {
|
||||
prep_form();
|
||||
$form.submit();
|
||||
}
|
||||
|
||||
$category.select2().css({'visibility': 'visible'}).change(clean_submit);
|
||||
$('#types').select2({multiple: 1, placeholder: '{{ _('Filter by type...') }}'})
|
||||
.css({'visibility': 'visible'});
|
||||
|
||||
// This is incredibly nasty to do but it's needed because otherwise the select2 steals the focus
|
||||
$search.keypress(function (e) {
|
||||
if (e.keyCode == 13)
|
||||
$('#go').click();
|
||||
});
|
||||
|
||||
$('#random').click(function (e) {
|
||||
var action = $form.attr('action');
|
||||
$form.attr('action', '{{ url('problem_random') }}').attr('target', '_blank').submit();
|
||||
$form.attr('action', action).attr('target', '');
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$('#go').click(clean_submit);
|
||||
|
||||
$('input#full_text, input#hide_solved, input#show_types').click(function () {
|
||||
prep_form();
|
||||
($('<form>').attr('action', window.location.pathname + '?' + $form.serialize())
|
||||
.append($('<input>').attr('type', 'hidden').attr('name', 'csrfmiddlewaretoken')
|
||||
.attr('value', $.cookie('csrftoken')))
|
||||
.attr('method', 'POST').appendTo($('body')).submit());
|
||||
});
|
||||
|
||||
var info_float = $('.info-float');
|
||||
var container = $('#content-right');
|
||||
if (window.bad_browser) {
|
||||
container.css('float', 'right');
|
||||
} else if (!featureTest('position', 'sticky')) {
|
||||
fix_div(info_float, 55);
|
||||
$(window).resize(function () {
|
||||
info_float.width(container.width());
|
||||
});
|
||||
info_float.width(container.width());
|
||||
}
|
||||
|
||||
var intFormatter = {
|
||||
to: function (value) {
|
||||
return value;
|
||||
},
|
||||
from: function (value) {
|
||||
return +value;
|
||||
}
|
||||
};
|
||||
var $slider = $('#point-slider');
|
||||
if ($slider.length) {
|
||||
var $start = $('#point-start');
|
||||
var $end = $('#point-end');
|
||||
|
||||
noUiSlider.create($slider[0], {
|
||||
start: [point_start, point_end],
|
||||
connect: true,
|
||||
snap: true,
|
||||
tooltips: [intFormatter, intFormatter],
|
||||
range: point_values
|
||||
}).on('change', function (values) {
|
||||
var start = +values[0], end = +values[1];
|
||||
$start.prop('disabled', start === point_values.min).val(start);
|
||||
$end.prop('disabled', end === point_values.max).val(end);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endcompress %}
|
||||
{% if request.in_contest %}
|
||||
{% compress js %}
|
||||
<script src="{{ static('libs/tablesorter.js') }}" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
$.tablesorter.addParser({
|
||||
id: 'solvedsort',
|
||||
is: function (s) {
|
||||
return false;
|
||||
},
|
||||
format: function (s, table, cell, cellIndex) {
|
||||
return $(cell).attr('solved');
|
||||
},
|
||||
type: 'numeric'
|
||||
});
|
||||
|
||||
$('#problem-table').tablesorter({
|
||||
headers: {
|
||||
0: {
|
||||
sorter: 'solvedsort'
|
||||
}
|
||||
},
|
||||
textExtraction: function (node) {
|
||||
node = $(node);
|
||||
var text = node.text().replace(/^\s+|\s+$/g, '');
|
||||
return (node.hasClass('p') ? text.replace(/p$/, '') : text);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endcompress %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block title_ruler %}{% endblock %}
|
||||
|
||||
{% block title_row %}
|
||||
{% set tab = 'list' %}
|
||||
{% set title = 'Problems' %}
|
||||
{% include "problem/problem-list-tabs.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% if page_obj.num_pages > 1 %}
|
||||
<div style="margin-bottom: 7px; margin-top: 11px;">
|
||||
{% include "list-pages.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div id="common-content">
|
||||
{% block before_table %}{% endblock %}
|
||||
{% if not request.in_contest %}
|
||||
<div id="content-right" class="problems">
|
||||
<div class="info-float">
|
||||
{% include "problem/search-form.html" %}
|
||||
{% if hot_problems %}
|
||||
<div class="sidebox">
|
||||
<h3>{{ _('Hot problems') }} <i class="fa fa-fire"></i></h3>
|
||||
<div class="sidebox-content">
|
||||
<ul class="problem-list">{% for problem in hot_problems %}
|
||||
<li><a href="{{ url('problem_detail', problem.code) }}" class="hot-problem-link">
|
||||
{{ problem.name }}
|
||||
</a></li>
|
||||
{% endfor %}</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="content-left" class="problems">
|
||||
<table id="problem-table" class="table striped">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if request.in_contest %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<th class="solved"><i class="fa fa-check"></i></th>
|
||||
{% endif %}
|
||||
<th class="problem">{{ _('Problem') }}</th>
|
||||
<th class="category">{{ _('Category') }}</th>
|
||||
{% if show_types %}
|
||||
<th>{{ _('Types') }}</th>
|
||||
{% endif %}
|
||||
<th class="points">{{ _('Points') }}</th>
|
||||
<th class="users">{{ _('Users') }}</th>
|
||||
{% else %}
|
||||
{% if request.user.is_authenticated %}
|
||||
<th class="solved">
|
||||
<a href="{{ sort_links.solved }}"><i class="fa fa-check"></i>{{ sort_order.solved }}
|
||||
</a>
|
||||
</th>
|
||||
{% endif %}
|
||||
<th class="problem">
|
||||
<a href="{{ sort_links.name }}">{{ _('Problem') }}{{ sort_order.name }}</a>
|
||||
</th>
|
||||
<th class="category">
|
||||
<a href="{{ sort_links.group }}">{{ _('Category') }}{{ sort_order.group }}</a>
|
||||
</th>
|
||||
{% if show_types %}
|
||||
<th>
|
||||
<a href="{{ sort_links.type }}">{{ _('Types') }}{{ sort_order.type }}</a>
|
||||
</th>
|
||||
{% endif %}
|
||||
<th class="points">
|
||||
<a href="{{ sort_links.points }}">{{ _('Points') }}{{ sort_order.points }}</a>
|
||||
</th>
|
||||
<th class="ac-rate">
|
||||
<a href="{{ sort_links.ac_rate }}">{{ _('AC %%') }}{{ sort_order.ac_rate }}</a>
|
||||
</th>
|
||||
<th class="users">
|
||||
<a href="{{ sort_links.user_count }}">{{ _('Users') }}{{ sort_order.user_count }}</a>
|
||||
</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for problem in problems %}
|
||||
<tr>
|
||||
{% if request.user.is_authenticated %}
|
||||
{% if problem.id in completed_problem_ids %}
|
||||
<td solved="1">
|
||||
<a href="{{ url('user_submissions', problem.code, request.user.username) }}">
|
||||
{% if problem.is_public or request.in_contest %}
|
||||
<i class="solved-problem-color fa fa-check-circle"></i>
|
||||
{% else %}
|
||||
<i class="solved-problem-color fa fa-lock"></i>
|
||||
{% endif %}
|
||||
</a>
|
||||
</td>
|
||||
{% elif problem.id in attempted_problems %}
|
||||
<td solved="0">
|
||||
<a href="{{ url('user_submissions', problem.code, request.user.username) }}">
|
||||
{% if problem.is_public or request.in_contest %}
|
||||
<i class="attempted-problem-color fa fa-minus-circle"></i>
|
||||
{% else %}
|
||||
<i class="attempted-problem-color fa fa-lock"></i>
|
||||
{% endif %}
|
||||
</a>
|
||||
</td>
|
||||
{% else %}
|
||||
<td solved="-1">
|
||||
{% if problem.is_public or request.in_contest %}
|
||||
<i class="unsolved-problem-color fa fa-minus-circle"></i>
|
||||
{% else %}
|
||||
<i class="unsolved-problem-color fa fa-lock"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<td class="problem">
|
||||
<a href="{{ url('problem_detail', problem.code) }}">{{ problem.i18n_name }}</a>
|
||||
</td>
|
||||
<td class="category">{{ problem.group.full_name }}</td>
|
||||
{% if show_types %}
|
||||
<td class="types">
|
||||
{% for type in problem.types_list %}
|
||||
<span class="type-tag">{{ type }}</span>{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="p">{{ problem.points|floatformat }}{% if problem.partial %}p{% endif %}</td>
|
||||
{% if not request.in_contest %}
|
||||
<td class="ac-rate">{{ problem.ac_rate|floatformat(1) }}%</td>
|
||||
{% endif %}
|
||||
<td class="users">
|
||||
<a href="{{ url('ranked_submissions', problem.code) }}">
|
||||
{% if not request.in_contest or not hide_contest_scoreboard %}
|
||||
{{ problem.user_count }}
|
||||
{% else %}
|
||||
???
|
||||
{% endif %}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% if page_obj.num_pages > 1 %}
|
||||
<div style="margin-top:10px;">{% include "list-pages.html" %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
{% endblock %}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue