Redesign Bookmark (#112)
This commit is contained in:
parent
829e6a802d
commit
c6acfa5e05
13 changed files with 398 additions and 243 deletions
|
@ -704,6 +704,10 @@ class Solution(models.Model, PageVotable, Bookmarkable):
|
|||
else:
|
||||
return reverse("problem_editorial", args=[problem.code])
|
||||
|
||||
@cache_wrapper(prefix="Sga", expected_type=models.query.QuerySet)
|
||||
def get_authors(self):
|
||||
return self.authors.only("id")
|
||||
|
||||
def __str__(self):
|
||||
return _("Editorial for %s") % self.problem.name
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ from .models import (
|
|||
Profile,
|
||||
Submission,
|
||||
NavigationBar,
|
||||
Solution,
|
||||
)
|
||||
|
||||
|
||||
|
@ -120,14 +121,7 @@ def comment_update(sender, instance, **kwargs):
|
|||
|
||||
@receiver(post_save, sender=BlogPost)
|
||||
def post_update(sender, instance, **kwargs):
|
||||
cache.delete_many(
|
||||
[
|
||||
make_template_fragment_key("post_summary", (instance.id,)),
|
||||
"blog_slug:%d" % instance.id,
|
||||
"blog_feed:%d" % instance.id,
|
||||
]
|
||||
+ [make_template_fragment_key("post_content", (instance.id,))]
|
||||
)
|
||||
cache.delete(make_template_fragment_key("post_content", (instance.id,)))
|
||||
BlogPost.get_authors.dirty(instance)
|
||||
|
||||
|
||||
|
@ -175,3 +169,8 @@ def contest_submission_update(sender, instance, **kwargs):
|
|||
@receiver(post_save, sender=NavigationBar)
|
||||
def navbar_update(sender, instance, **kwargs):
|
||||
judge.template_context._nav_bar.dirty()
|
||||
|
||||
|
||||
@receiver(post_save, sender=Solution)
|
||||
def solution_update(sender, instance, **kwargs):
|
||||
cache.delete(make_template_fragment_key("solution_content", (instance.id,)))
|
||||
|
|
|
@ -22,6 +22,7 @@ from django.db.models import (
|
|||
Q,
|
||||
When,
|
||||
IntegerField,
|
||||
Sum,
|
||||
)
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.db.utils import ProgrammingError
|
||||
|
@ -644,6 +645,16 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
|
|||
queryset = queryset.filter(points__gte=self.point_start)
|
||||
if self.point_end is not None:
|
||||
queryset = queryset.filter(points__lte=self.point_end)
|
||||
|
||||
queryset = queryset.annotate(
|
||||
has_public_editorial=Sum(
|
||||
Case(
|
||||
When(solution__is_public=True, then=1),
|
||||
default=0,
|
||||
output_field=IntegerField(),
|
||||
)
|
||||
)
|
||||
)
|
||||
return queryset.distinct()
|
||||
|
||||
def get_queryset(self):
|
||||
|
|
|
@ -36,7 +36,17 @@ from django.template.loader import render_to_string
|
|||
from reversion import revisions
|
||||
|
||||
from judge.forms import UserForm, ProfileForm, ProfileInfoForm
|
||||
from judge.models import Profile, Rating, Submission, Friend, ProfileInfo
|
||||
from judge.models import (
|
||||
Profile,
|
||||
Rating,
|
||||
Submission,
|
||||
Friend,
|
||||
ProfileInfo,
|
||||
BlogPost,
|
||||
Problem,
|
||||
Contest,
|
||||
Solution,
|
||||
)
|
||||
from judge.performance_points import get_pp_breakdown
|
||||
from judge.ratings import rating_class, rating_progress
|
||||
from judge.tasks import import_users
|
||||
|
@ -54,10 +64,13 @@ from judge.utils.views import (
|
|||
TitleMixin,
|
||||
generic_message,
|
||||
SingleObjectFormView,
|
||||
DiggPaginatorMixin,
|
||||
)
|
||||
from judge.utils.infinite_paginator import InfinitePaginationMixin
|
||||
from judge.views.problem import ProblemList
|
||||
from .contests import ContestRanking
|
||||
|
||||
|
||||
__all__ = [
|
||||
"UserPage",
|
||||
"UserAboutPage",
|
||||
|
@ -305,17 +318,49 @@ class UserProblemsPage(UserPage):
|
|||
return context
|
||||
|
||||
|
||||
class UserBookMarkPage(UserPage):
|
||||
class UserBookMarkPage(DiggPaginatorMixin, ListView, UserPage):
|
||||
template_name = "user/user-bookmarks.html"
|
||||
context_object_name = "bookmarks"
|
||||
paginate_by = 10
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.current_tab = self.request.GET.get("tab", "problems")
|
||||
self.user = self.object = self.get_object()
|
||||
return super(UserBookMarkPage, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
model = None
|
||||
if self.current_tab == "posts":
|
||||
model = BlogPost
|
||||
elif self.current_tab == "contests":
|
||||
model = Contest
|
||||
elif self.current_tab == "editorials":
|
||||
model = Solution
|
||||
else:
|
||||
model = Problem
|
||||
|
||||
q = MakeBookMark.objects.filter(user=self.user).select_related("bookmark")
|
||||
q = q.filter(bookmark__content_type=ContentType.objects.get_for_model(model))
|
||||
object_ids = q.values_list("bookmark__object_id", flat=True)
|
||||
|
||||
res = model.objects.filter(id__in=object_ids)
|
||||
if self.current_tab == "contests":
|
||||
res = res.prefetch_related("organizations", "tags")
|
||||
elif self.current_tab == "editorials":
|
||||
res = res.select_related("problem")
|
||||
|
||||
return res
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserBookMarkPage, self).get_context_data(**kwargs)
|
||||
|
||||
bookmark_list = MakeBookMark.objects.filter(user=self.object)
|
||||
context["blogs"] = bookmark_list.filter(bookmark__page__startswith="b")
|
||||
context["problems"] = bookmark_list.filter(bookmark__page__startswith="p")
|
||||
context["contests"] = bookmark_list.filter(bookmark__page__startswith="c")
|
||||
context["solutions"] = bookmark_list.filter(bookmark__page__startswith="s")
|
||||
context["current_tab"] = self.current_tab
|
||||
context["user"] = self.user
|
||||
|
||||
context["page_prefix"] = (
|
||||
self.request.path + "?tab=" + self.current_tab + "&page="
|
||||
)
|
||||
context["first_page_href"] = self.request.path
|
||||
|
||||
return context
|
||||
|
||||
|
|
|
@ -358,6 +358,33 @@ function register_copy_clipboard($elements, callback) {
|
|||
});
|
||||
}
|
||||
|
||||
function activateBlogBoxOnClick() {
|
||||
$('.blog-box').on('click', function () {
|
||||
var $description = $(this).children('.blog-description');
|
||||
var max_height = $description.css('max-height');
|
||||
if (max_height !== 'fit-content') {
|
||||
$description.css('max-height', 'fit-content');
|
||||
$(this).css('cursor', 'auto');
|
||||
$(this).removeClass('pre-expand-blog');
|
||||
$(this).children().children('.show-more').hide();
|
||||
}
|
||||
});
|
||||
|
||||
$('.blog-box').each(function () {
|
||||
var $precontent = $(this).children('.blog-description').height();
|
||||
var $content = $(this).children().children('.content-description').height();
|
||||
if ($content == undefined) {
|
||||
$content = $(this).children().children('.md-typeset').height()
|
||||
}
|
||||
if ($content > $precontent - 30) {
|
||||
$(this).addClass('pre-expand-blog');
|
||||
$(this).css('cursor', 'pointer');
|
||||
} else {
|
||||
$(this).children().children('.show-more').hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onWindowReady() {
|
||||
// http://stackoverflow.com/a/1060034/1090657
|
||||
var hidden = 'hidden';
|
||||
|
@ -464,6 +491,7 @@ function onWindowReady() {
|
|||
errorList.nextAll('input, select, textarea').first().after(errorList);
|
||||
});
|
||||
register_all_toggles();
|
||||
activateBlogBoxOnClick();
|
||||
}
|
||||
|
||||
$(function() {
|
||||
|
|
|
@ -36,8 +36,8 @@
|
|||
</h2>
|
||||
<div class="blog-description">
|
||||
<div class="summary content-description">
|
||||
{% cache 86400 'post_summary' post.id %}
|
||||
{{ post.summary|default(post.content, true)|markdown(lazy_load=True)|reference|str|safe }}
|
||||
{% cache 86400 'post_content' post.id %}
|
||||
{{ post.content|markdown(lazy_load=True)|reference|str|safe }}
|
||||
{% endcache %}
|
||||
</div>
|
||||
<div class="show-more"> {{_("...More")}} </div>
|
||||
|
|
|
@ -134,112 +134,7 @@
|
|||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% macro contest_head(contest) %}
|
||||
<a href="{{ url('contest_view', contest.key) }}" class="contest-list-title" style="margin-right: 5px;">
|
||||
{{contest.name}}
|
||||
</a>
|
||||
<div class="contest-tags">
|
||||
{% if not contest.is_visible %}
|
||||
<span class="contest-tag contest-tag-hidden">
|
||||
<i class="fa fa-eye-slash"></i> {{ _('hidden') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if contest.is_editable %}
|
||||
<span class="contest-tag contest-tag-edit">
|
||||
<a href="{{ url('organization_contest_edit', organization.id, organization.slug, contest.key) }}" class="white">
|
||||
<i class="fa fa-edit"></i> {{ _('Edit') }}
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if contest.is_private %}
|
||||
<span class="contest-tag contest-tag-private">
|
||||
<i class="fa fa-lock"></i> {{ _('private') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if not hide_contest_orgs %}
|
||||
{% if contest.is_organization_private %}
|
||||
{% for org in contest.organizations.all() %}
|
||||
{% include "organization/tag.html" %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if contest.is_rated %}
|
||||
<span class="contest-tag contest-tag-rated">
|
||||
<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 %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro time_left(contest) %}
|
||||
<div class="time-left">
|
||||
{% if contest.time_limit %}
|
||||
<div>
|
||||
<b>{{_("Start")}}</b>: {{ contest.start_time|date(_("H:i d/m/Y")) }}
|
||||
</div>
|
||||
<div>
|
||||
<b>{{_("End")}}</b>: {{ contest.end_time|date(_("H:i d/m/Y")) }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div>
|
||||
<b>{{_("Start")}}</b>: {{ contest.start_time|date(_("H:i d/m/Y")) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<b>{{_("Length")}}</b>:
|
||||
{% if contest.time_limit %}
|
||||
{% trans time_limit=contest.time_limit|timedelta('localized-no-seconds') %}{{ time_limit }}{% endtrans %}
|
||||
{% else %}
|
||||
{% trans duration=contest.contest_window_length|timedelta('localized-no-seconds') %}{{ duration }}{% endtrans %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro user_count(contest, user) %}
|
||||
{% if contest.can_see_own_scoreboard(user) %}
|
||||
<a href="{{ url('contest_ranking', contest.key) }}"><i class="fa fa-users"></i> {{ contest.user_count }}</a>
|
||||
{% else %}
|
||||
<i class="fa fa-users"></i>{{ contest.user_count }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro contest_join(contest, request) %}
|
||||
{% if request.in_contest and request.participation.contest == contest %}
|
||||
<button class="small" disabled>{{ _('In contest') }}</button>
|
||||
{% elif request.profile.id in contest.editor_ids or request.profile.id in contest.tester_ids %}
|
||||
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="unselectable button full small"
|
||||
value="{{ _('Spectate') }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="unselectable button full small join-warning"
|
||||
value="{{ _('Join') }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro contest_format_user(contest, show_user=True) %}
|
||||
<div style="display: flex; flex-direction: column; height: 100%;">
|
||||
<div class="contest-title"> {{ _('Format') }} </div>
|
||||
<div style="flex-grow: 1">{{ contest.format.name }}</div>
|
||||
{% if show_user %}
|
||||
<div class="contest-title">{{ user_count(contest, request.user) }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% from "contest/macros.html" import contest_head, time_left, user_count, contest_join, contest_format_user %}
|
||||
|
||||
{% block middle_content %}
|
||||
<div class="tabs tabs-no-flex" style="width: 100%;margin-left: auto;margin-right: auto;">
|
||||
|
@ -298,7 +193,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="info-contest" style="flex: 0.5;">
|
||||
{{ contest_format_user(contest) }}
|
||||
{{ contest_format_user(contest, request) }}
|
||||
</div>
|
||||
<div class="participate-button">
|
||||
{{ contest_join(contest, request) }}
|
||||
|
@ -336,7 +231,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="info-contest" style="flex: 0.5;">
|
||||
{{ contest_format_user(contest) }}
|
||||
{{ contest_format_user(contest, request) }}
|
||||
</div>
|
||||
<div class="participate-button">
|
||||
{{ contest_join(contest, request) }}
|
||||
|
@ -373,7 +268,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="info-contest" style="flex: 0.5;">
|
||||
{{ contest_format_user(contest, show_user=False) }}
|
||||
{{ contest_format_user(contest, request, show_user=False) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
@ -402,7 +297,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="info-contest" style="flex: 0.5;">
|
||||
{{ contest_format_user(contest) }}
|
||||
{{ contest_format_user(contest, request) }}
|
||||
</div>
|
||||
<div class="participate-button">
|
||||
{% if request.in_contest and request.participation.contest == contest %}
|
||||
|
|
106
templates/contest/macros.html
Normal file
106
templates/contest/macros.html
Normal file
|
@ -0,0 +1,106 @@
|
|||
{% macro contest_head(contest) %}
|
||||
<a href="{{ url('contest_view', contest.key) }}" class="contest-list-title" style="margin-right: 5px;">
|
||||
{{contest.name}}
|
||||
</a>
|
||||
<div class="contest-tags">
|
||||
{% if not contest.is_visible %}
|
||||
<span class="contest-tag contest-tag-hidden">
|
||||
<i class="fa fa-eye-slash"></i> {{ _('hidden') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if contest.is_editable %}
|
||||
<span class="contest-tag contest-tag-edit">
|
||||
<a href="{{ url('organization_contest_edit', organization.id, organization.slug, contest.key) }}" class="white">
|
||||
<i class="fa fa-edit"></i> {{ _('Edit') }}
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if contest.is_private %}
|
||||
<span class="contest-tag contest-tag-private">
|
||||
<i class="fa fa-lock"></i> {{ _('private') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if not hide_contest_orgs %}
|
||||
{% if contest.is_organization_private %}
|
||||
{% for org in contest.organizations.all() %}
|
||||
{% include "organization/tag.html" %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if contest.is_rated %}
|
||||
<span class="contest-tag contest-tag-rated">
|
||||
<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 %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro time_left(contest) %}
|
||||
<div class="time-left">
|
||||
{% if contest.time_limit %}
|
||||
<div>
|
||||
<b>{{_("Start")}}</b>: {{ contest.start_time|date(_("H:i d/m/Y")) }}
|
||||
</div>
|
||||
<div>
|
||||
<b>{{_("End")}}</b>: {{ contest.end_time|date(_("H:i d/m/Y")) }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div>
|
||||
<b>{{_("Start")}}</b>: {{ contest.start_time|date(_("H:i d/m/Y")) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<b>{{_("Length")}}</b>:
|
||||
{% if contest.time_limit %}
|
||||
{% trans time_limit=contest.time_limit|timedelta('localized-no-seconds') %}{{ time_limit }}{% endtrans %}
|
||||
{% else %}
|
||||
{% trans duration=contest.contest_window_length|timedelta('localized-no-seconds') %}{{ duration }}{% endtrans %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro user_count(contest, user) %}
|
||||
{% if contest.can_see_own_scoreboard(user) %}
|
||||
<a href="{{ url('contest_ranking', contest.key) }}"><i class="fa fa-users"></i> {{ contest.user_count }}</a>
|
||||
{% else %}
|
||||
<i class="fa fa-users"></i>{{ contest.user_count }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro contest_join(contest, request) %}
|
||||
{% if request.in_contest and request.participation.contest == contest %}
|
||||
<button class="small" disabled>{{ _('In contest') }}</button>
|
||||
{% elif request.profile.id in contest.editor_ids or request.profile.id in contest.tester_ids %}
|
||||
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="unselectable button full small"
|
||||
value="{{ _('Spectate') }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="{{ url('contest_join', contest.key) }}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="submit" class="unselectable button full small join-warning"
|
||||
value="{{ _('Join') }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro contest_format_user(contest, request, show_user=True) %}
|
||||
<div style="display: flex; flex-direction: column; height: 100%;">
|
||||
<div class="contest-title"> {{ _('Format') }} </div>
|
||||
<div style="flex-grow: 1">{{ contest.format.name }}</div>
|
||||
{% if show_user %}
|
||||
<div class="contest-title">{{ user_count(contest, request.user) }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
|
@ -32,7 +32,6 @@
|
|||
window.page++;
|
||||
renderKatex($('.middle-content')[0]);
|
||||
onWindowReady();
|
||||
activateBlogBoxOnClick();
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
<p>Authors: {{ link_users(authors) }}</p>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{{ solution.content|markdown|reference|str|safe }}
|
||||
{% cache 86400 'solution_content' solution.id %}
|
||||
{{ solution.content|markdown(lazy_load=True)|reference|str|safe }}
|
||||
{% endcache %}
|
||||
</div>
|
||||
<hr>
|
||||
{% include "actionbar/list.html" %}
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
|
||||
$('#go').click(clean_submit);
|
||||
|
||||
$('input#full_text, input#hide_solved, input#show_types, input#have_editorial, input#show_solved_only').click(function () {
|
||||
$('input#full_text, input#hide_solved, input#show_types, input#have_editorial, input#show_solved_only, input#show_editorial').click(function () {
|
||||
prep_form();
|
||||
($('<form>').attr('action', window.location.pathname + '?' + form_serialize())
|
||||
.append($('<input>').attr('type', 'hidden').attr('name', 'csrfmiddlewaretoken')
|
||||
|
|
|
@ -30,33 +30,6 @@
|
|||
<script type="text/javascript">
|
||||
let loadingPage;
|
||||
|
||||
function activateBlogBoxOnClick() {
|
||||
$('.blog-box').on('click', function () {
|
||||
var $description = $(this).children('.blog-description');
|
||||
var max_height = $description.css('max-height');
|
||||
if (max_height !== 'fit-content') {
|
||||
$description.css('max-height', 'fit-content');
|
||||
$(this).css('cursor', 'auto');
|
||||
$(this).removeClass('pre-expand-blog');
|
||||
$(this).children().children('.show-more').hide();
|
||||
}
|
||||
});
|
||||
|
||||
$('.blog-box').each(function () {
|
||||
var $precontent = $(this).children('.blog-description').height();
|
||||
var $content = $(this).children().children('.content-description').height();
|
||||
if ($content == undefined) {
|
||||
$content = $(this).children().children('.md-typeset').height()
|
||||
}
|
||||
if ($content > $precontent - 30) {
|
||||
$(this).addClass('pre-expand-blog');
|
||||
$(this).css('cursor', 'pointer');
|
||||
} else {
|
||||
$(this).children().children('.show-more').hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function navigateTo($elem, update_sidebar = false) {
|
||||
var url = $elem.attr('href');
|
||||
|
||||
|
@ -86,7 +59,6 @@
|
|||
$(document).prop('title', $(data).filter('title').text());
|
||||
renderKatex($('.middle-right-content')[0]);
|
||||
onWindowReady();
|
||||
activateBlogBoxOnClick();
|
||||
$('.xdsoft_datetimepicker').hide();
|
||||
registerNavigation();
|
||||
}
|
||||
|
@ -114,7 +86,6 @@
|
|||
window.addEventListener('popstate', (e) => {
|
||||
window.location.href = e.currentTarget.location.href;
|
||||
});
|
||||
activateBlogBoxOnClick();
|
||||
|
||||
$('.left-sidebar-item').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
|
|
@ -7,92 +7,187 @@
|
|||
{% include "user/user-tabs.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% from "contest/macros.html" import contest_head, time_left, user_count, contest_format_user %}
|
||||
|
||||
{% block user_content %}
|
||||
{% if blogs %}
|
||||
<div class="bookmark-group">
|
||||
<h3 class="unselectable toggle closed">
|
||||
<span class="fa fa-chevron-right fa-fw"></span>{{ _('Posts') }} ({{ blogs|length }})
|
||||
</h3>
|
||||
<table style="display: none" class="table toggled">
|
||||
<tbody>
|
||||
{% for post in blogs %}
|
||||
{% set object = post.bookmark.linked_object %}
|
||||
{% if object %}
|
||||
<tr>
|
||||
<td class="bookmark-name">
|
||||
<a href="{{ url('blog_post', object.id, object.slug) }}">{{ object.title}} </a>
|
||||
</td>
|
||||
</tr>
|
||||
<div class="tabs tabs-no-flex" style="width: 100%;margin-left: auto;margin-right: auto;">
|
||||
<ul>
|
||||
<li class="{{'active' if current_tab=='problems'}}">
|
||||
<a href="?tab=problems">{{ _('Problems') }}</a>
|
||||
</li>
|
||||
<li class="{{'active' if current_tab=='contests'}}">
|
||||
<a href="?tab=contests">{{ _('Contests') }}</a>
|
||||
</li>
|
||||
<li class="{{'active' if current_tab=='editorials'}}">
|
||||
<a href="?tab=editorials">{{ _('Editorials') }}</a>
|
||||
</li>
|
||||
<li class="{{'active' if current_tab=='posts'}}">
|
||||
<a href="?tab=posts">{{ _('Posts') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% if current_tab == 'problems' %}
|
||||
{% if bookmarks %}
|
||||
{% for problem in bookmarks %}
|
||||
<div class="blog-box">
|
||||
<h3 class="problem-feed-name">
|
||||
<a href="{{ url('problem_detail', problem.code) }}">
|
||||
{{ problem.name }}
|
||||
</a>
|
||||
</h3>
|
||||
{% with authors=problem.get_authors() %}
|
||||
{% if authors %}
|
||||
<div class="problem-feed-info-entry">
|
||||
<i class="fa fa-pencil-square-o fa-fw"></i>
|
||||
<span class="pi-value">{{ link_users(authors) }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if problems %}
|
||||
<div class="bookmark-group">
|
||||
<h3 class="unselectable toggle closed">
|
||||
<span class="fa fa-chevron-right fa-fw"></span>{{ _('Problems') }} ({{ problems|length }})
|
||||
</h3>
|
||||
<table style="display: none" class="table toggled">
|
||||
<tbody>
|
||||
{% for problem in problems %}
|
||||
{% set object = problem.bookmark.linked_object %}
|
||||
{% if object %}
|
||||
<tr>
|
||||
<td class="bookmark-name">
|
||||
<a href="{{ url('problem_detail', object.code) }}">{{ object.name}} </a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if contests %}
|
||||
<div class="bookmark-group">
|
||||
<h3 class="unselectable toggle closed">
|
||||
<span class="fa fa-chevron-right fa-fw"></span>{{ _('Contests') }} ({{ contests|length }})
|
||||
</h3>
|
||||
<table style="display: none" class="table toggled">
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for contest in contests %}
|
||||
{% set object = contest.bookmark.linked_object %}
|
||||
{% if object %}
|
||||
<tr>
|
||||
<td class="bookmark-name">
|
||||
<a href="{{ url('contest_view', object.key) }}">{{ object.name}} </a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if solutions %}
|
||||
<div class="bookmark-group">
|
||||
<h3 class="unselectable toggle closed">
|
||||
<span class="fa fa-chevron-right fa-fw"></span>{{ _('Editorials') }} ({{ solutions|length }})
|
||||
</h3>
|
||||
<table style="display: none" class="table toggled">
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for solution in solutions %}
|
||||
{% set object = solution.bookmark.linked_object %}
|
||||
{% if object %}
|
||||
<tr>
|
||||
<td class="bookmark-name">
|
||||
<a href="{{ url('problem_editorial', object.problem.code) }}">{{ object.problem.name}} </a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endwith %}
|
||||
<div class="problem-feed-types">
|
||||
<i class="fa fa-tag"></i>
|
||||
*{{problem.points | int}}
|
||||
</div>
|
||||
<div class="blog-description">
|
||||
<div class='content-description'>
|
||||
{% cache 86400 'problem_html' problem.id LANGUAGE_CODE %}
|
||||
{{ problem.description|markdown(lazy_load=True)|reference|str|safe }}
|
||||
{% endcache %}
|
||||
{% if problem.pdf_description %}
|
||||
<embed src="{{url('problem_pdf_description', problem.code)}}" width="100%" height="500" type="application/pdf"
|
||||
style="margin-top: 0.5em">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="show-more"> {{_("...More")}} </div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if page_obj and page_obj.num_pages > 1 %}
|
||||
<div style="margin-top: 10px;">
|
||||
{% include "list-pages.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<i> {{ _('There is no saved problem.') }} </i>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if current_tab == 'contests' %}
|
||||
{% if bookmarks %}
|
||||
{% for contest in bookmarks %}
|
||||
<div class="list-contest">
|
||||
<div class="info-contest">
|
||||
<div class="contest-title"> {{ _('Contests') }} </div>
|
||||
{{ contest_head(contest) }}
|
||||
</div>
|
||||
<div class="info-contest">
|
||||
<div class="contest-title"> {{ _('Time') }} </div>
|
||||
<div class="contest-block">
|
||||
{{ time_left(contest) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-contest" style="flex: 0.5;">
|
||||
{{ contest_format_user(contest, request) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if page_obj and page_obj.num_pages > 1 %}
|
||||
<div style="margin-top: 10px;">
|
||||
{% include "list-pages.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<i> {{ _('There is no saved contest.') }} </i>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if current_tab == 'editorials' %}
|
||||
{% if bookmarks %}
|
||||
{% for solution in bookmarks %}
|
||||
<section class="blog-box">
|
||||
<div style="margin-bottom: 0.5em">
|
||||
<span class="post-content-header time">
|
||||
{% with authors=solution.get_authors() %}
|
||||
{%- if authors -%}
|
||||
<span class="user-img" style="width: 1.5em; height: 1.5em">
|
||||
<img src="{{gravatar(authors[0])}}" loading="lazy">
|
||||
</span>
|
||||
<span class="post-authors">{{ link_users(authors) }}</span>
|
||||
{%- endif -%}
|
||||
{% endwith %}
|
||||
•
|
||||
{{ relative_time(solution.publish_on) }}
|
||||
</span>
|
||||
</div>
|
||||
<h2 class="title">
|
||||
<a href="{{ url('problem_editorial', solution.problem.code) }}">{{ solution.problem.name }}</a>
|
||||
</h2>
|
||||
<div class="blog-description">
|
||||
<div class="summary content-description">
|
||||
{% cache 86400 'solution_content' solution.id %}
|
||||
{{ solution.content|markdown(lazy_load=True)|reference|str|safe }}
|
||||
{% endcache %}
|
||||
</div>
|
||||
<div class="show-more"> {{_("...More")}} </div>
|
||||
</div>
|
||||
</section>
|
||||
{% endfor %}
|
||||
{% if page_obj and page_obj.num_pages > 1 %}
|
||||
<div style="margin-top: 10px;">
|
||||
{% include "list-pages.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<i> {{ _('There is no saved editorial.') }} </i>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if current_tab == 'posts' %}
|
||||
{% if bookmarks %}
|
||||
{% for post in bookmarks %}
|
||||
<section class="blog-box">
|
||||
<div style="margin-bottom: 0.5em">
|
||||
<span class="post-content-header time">
|
||||
{% with authors=post.get_authors() %}
|
||||
{%- if authors -%}
|
||||
<span class="user-img" style="width: 1.5em; height: 1.5em">
|
||||
<img src="{{gravatar(authors[0])}}" loading="lazy">
|
||||
</span>
|
||||
<span class="post-authors">{{ link_users(authors) }}</span>
|
||||
{%- endif -%}
|
||||
{% endwith %}
|
||||
•
|
||||
{{ relative_time(post.publish_on) }}
|
||||
</span>
|
||||
<span style="float: right">
|
||||
<a href="{{ url('blog_post', post.id, post.slug) }}#comments" class="blog-comment-count-link">
|
||||
<i class="fa fa-comments blog-comment-icon"></i>
|
||||
<span class="blog-comment-count">
|
||||
{{ comment_count(post) }}
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<h2 class="title">
|
||||
<a href="{{ url('blog_post', post.id, post.slug) }}">{{ post.title }}</a>
|
||||
</h2>
|
||||
<div class="blog-description">
|
||||
<div class="summary content-description">
|
||||
{% cache 86400 'post_content' post.id %}
|
||||
{{ post.content|markdown(lazy_load=True)|reference|str|safe }}
|
||||
{% endcache %}
|
||||
</div>
|
||||
<div class="show-more"> {{_("...More")}} </div>
|
||||
</div>
|
||||
</section>
|
||||
{% endfor %}
|
||||
{% if page_obj and page_obj.num_pages > 1 %}
|
||||
<div style="margin-top: 10px;">
|
||||
{% include "list-pages.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<i> {{ _('There is no saved post.') }} </i>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in a new issue