New home UI

This commit is contained in:
cuom1999 2022-03-21 16:09:16 -05:00
parent e8ee2ac4aa
commit d10173df5d
14 changed files with 478 additions and 222 deletions

View file

@ -110,13 +110,17 @@ urlpatterns = [
url(r'^accounts/', include(register_patterns)), url(r'^accounts/', include(register_patterns)),
url(r'^', include('social_django.urls')), url(r'^', include('social_django.urls')),
url(r'^feed/', include([
url(r'^problems/$', problem.ProblemFeed.as_view(), name='problem_feed'),
url(r'^tickets/$', blog.TicketFeed.as_view(), name='ticket_feed'),
url(r'^comments/$', blog.CommentFeed.as_view(), name='comment_feed'),
])),
url(r'^problems/$', problem.ProblemList.as_view(), name='problem_list'), url(r'^problems/$', problem.ProblemList.as_view(), name='problem_list'),
url(r'^problems/random/$', problem.RandomProblem.as_view(), name='problem_random'), url(r'^problems/random/$', problem.RandomProblem.as_view(), name='problem_random'),
url(r'^problem/(?P<problem>[^/]+)', include([ url(r'^problem/(?P<problem>[^/]+)', include([
url(r'^$', problem.ProblemDetail.as_view(), name='problem_detail'), url(r'^$', problem.ProblemDetail.as_view(), name='problem_detail'),
url(r'^/editorial$', problem.ProblemSolution.as_view(), name='problem_editorial'), url(r'^/editorial$', problem.ProblemSolution.as_view(), name='problem_editorial'),
url(r'^/comments$', problem.ProblemComments.as_view(), name='problem_comments'),
url(r'^/raw$', problem.ProblemRaw.as_view(), name='problem_raw'), url(r'^/raw$', problem.ProblemRaw.as_view(), name='problem_raw'),
url(r'^/pdf$', problem.ProblemPdfView.as_view(), name='problem_pdf'), url(r'^/pdf$', problem.ProblemPdfView.as_view(), name='problem_pdf'),
url(r'^/pdf/(?P<language>[a-z-]+)$', problem.ProblemPdfView.as_view(), name='problem_pdf'), url(r'^/pdf/(?P<language>[a-z-]+)$', problem.ProblemPdfView.as_view(), name='problem_pdf'),

View file

@ -66,6 +66,8 @@ class Comment(MPTTModel):
contest_access = CacheDict(lambda key: Contest.objects.get(key=key).is_accessible_by(user)) contest_access = CacheDict(lambda key: Contest.objects.get(key=key).is_accessible_by(user))
blog_access = CacheDict(lambda id: BlogPost.objects.get(id=id).can_see(user)) blog_access = CacheDict(lambda id: BlogPost.objects.get(id=id).can_see(user))
if n == -1:
n = len(queryset)
if user.is_superuser: if user.is_superuser:
return queryset[:n] return queryset[:n]
if batch is None: if batch is None:
@ -105,7 +107,7 @@ class Comment(MPTTModel):
try: try:
link = None link = None
if self.page.startswith('p:'): if self.page.startswith('p:'):
link = reverse('problem_comments', args=(self.page[2:],)) link = reverse('problem_detail', args=(self.page[2:],))
elif self.page.startswith('c:'): elif self.page.startswith('c:'):
link = reverse('contest_view', args=(self.page[2:],)) link = reverse('contest_view', args=(self.page[2:],))
elif self.page.startswith('b:'): elif self.page.startswith('b:'):

View file

@ -17,10 +17,8 @@ from judge.utils.tickets import filter_visible_tickets
from judge.utils.views import TitleMixin from judge.utils.views import TitleMixin
class PostList(ListView): # General view for all content list on home feed
model = BlogPost class FeedView(ListView):
paginate_by = 10
context_object_name = 'posts'
template_name = 'blog/list.html' template_name = 'blog/list.html'
title = None title = None
@ -29,6 +27,56 @@ class PostList(ListView):
return DiggPaginator(queryset, per_page, body=6, padding=2, return DiggPaginator(queryset, per_page, body=6, padding=2,
orphans=orphans, allow_empty_first_page=allow_empty_first_page, **kwargs) orphans=orphans, allow_empty_first_page=allow_empty_first_page, **kwargs)
def get_context_data(self, **kwargs):
context = super(FeedView, self).get_context_data(**kwargs)
context['has_clarifications'] = False
if self.request.user.is_authenticated:
participation = self.request.profile.current_contest
if participation:
clarifications = ProblemClarification.objects.filter(problem__in=participation.contest.problems.all())
context['has_clarifications'] = clarifications.count() > 0
context['clarifications'] = clarifications.order_by('-date')
if participation.contest.is_editable_by(self.request.user):
context['can_edit_contest'] = True
context['page_titles'] = CacheDict(lambda page: Comment.get_page_title(page))
context['user_count'] = lazy(Profile.objects.count, int, int)
context['problem_count'] = lazy(Problem.objects.filter(is_public=True).count, int, int)
context['submission_count'] = lazy(Submission.objects.count, int, int)
context['language_count'] = lazy(Language.objects.count, int, int)
now = timezone.now()
# Dashboard stuff
# if self.request.user.is_authenticated:
# user = self.request.profile
# context['recently_attempted_problems'] = (Submission.objects.filter(user=user)
# .exclude(problem__in=user_completed_ids(user))
# .values_list('problem__code', 'problem__name', 'problem__points')
# .annotate(points=Max('points'), latest=Max('date'))
# .order_by('-latest')
# [:settings.DMOJ_BLOG_RECENTLY_ATTEMPTED_PROBLEMS_COUNT])
visible_contests = Contest.get_visible_contests(self.request.user).filter(is_visible=True) \
.order_by('start_time')
context['current_contests'] = visible_contests.filter(start_time__lte=now, end_time__gt=now)
context['future_contests'] = visible_contests.filter(start_time__gt=now)
visible_contests = Contest.get_visible_contests(self.request.user).filter(is_visible=True)
context['top_rated'] = Profile.objects.order_by('-rating')[:10]
context['top_scorer'] = Profile.objects.order_by('-performance_points')[:10]
return context
class PostList(FeedView):
model = BlogPost
paginate_by = 10
context_object_name = 'posts'
def get_queryset(self): def get_queryset(self):
queryset = BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now()) \ queryset = BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now()) \
.order_by('-sticky', '-publish_on') \ .order_by('-sticky', '-publish_on') \
@ -45,25 +93,7 @@ class PostList(ListView):
context['title'] = self.title or _('Page %d of Posts') % context['page_obj'].number context['title'] = self.title or _('Page %d of Posts') % context['page_obj'].number
context['first_page_href'] = reverse('home') context['first_page_href'] = reverse('home')
context['page_prefix'] = reverse('blog_post_list') context['page_prefix'] = reverse('blog_post_list')
context['comments'] = Comment.most_recent(self.request.user, 25) context['feed_type'] = 'blog'
context['new_problems'] = Problem.objects.filter(is_public=True, is_organization_private=False) \
.order_by('-date', '-id')[:settings.DMOJ_BLOG_NEW_PROBLEM_COUNT]
context['page_titles'] = CacheDict(lambda page: Comment.get_page_title(page))
context['has_clarifications'] = False
if self.request.user.is_authenticated:
participation = self.request.profile.current_contest
if participation:
clarifications = ProblemClarification.objects.filter(problem__in=participation.contest.problems.all())
context['has_clarifications'] = clarifications.count() > 0
context['clarifications'] = clarifications.order_by('-date')
if participation.contest.is_editable_by(self.request.user):
context['can_edit_contest'] = True
context['user_count'] = lazy(Profile.objects.count, int, int)
context['problem_count'] = lazy(Problem.objects.filter(is_public=True).count, int, int)
context['submission_count'] = lazy(Submission.objects.count, int, int)
context['language_count'] = lazy(Language.objects.count, int, int)
context['post_comment_counts'] = { context['post_comment_counts'] = {
int(page[2:]): count for page, count in int(page[2:]): count for page, count in
Comment.objects Comment.objects
@ -71,40 +101,55 @@ class PostList(ListView):
.values_list('page').annotate(count=Count('page')).order_by() .values_list('page').annotate(count=Count('page')).order_by()
} }
now = timezone.now() return context
# Dashboard stuff
if self.request.user.is_authenticated:
user = self.request.profile
context['recently_attempted_problems'] = (Submission.objects.filter(user=user)
.exclude(problem__in=user_completed_ids(user))
.values_list('problem__code', 'problem__name', 'problem__points')
.annotate(points=Max('points'), latest=Max('date'))
.order_by('-latest')
[:settings.DMOJ_BLOG_RECENTLY_ATTEMPTED_PROBLEMS_COUNT])
visible_contests = Contest.get_visible_contests(self.request.user).filter(is_visible=True) \ class TicketFeed(FeedView):
.order_by('start_time') model = Ticket
context_object_name = 'tickets'
paginate_by = 30
context['current_contests'] = visible_contests.filter(start_time__lte=now, end_time__gt=now) def get_queryset(self, is_own=False):
context['future_contests'] = visible_contests.filter(start_time__gt=now) profile = self.request.profile
if is_own:
visible_contests = Contest.get_visible_contests(self.request.user).filter(is_visible=True) if self.request.user.is_authenticated:
if self.request.user.is_authenticated: return (Ticket.objects.filter(Q(user=profile) | Q(assignees__in=[profile]), is_open=True).order_by('-id')
profile = self.request.profile .prefetch_related('linked_item').select_related('user__user'))
context['own_open_tickets'] = (Ticket.objects.filter(Q(user=profile) | Q(assignees__in=[profile]), is_open=True).order_by('-id') else:
.prefetch_related('linked_item').select_related('user__user')) return []
else: else:
profile = None # Superusers better be staffs, not the spell-casting kind either.
context['own_open_tickets'] = [] if self.request.user.is_staff:
tickets = (Ticket.objects.order_by('-id').filter(is_open=True).prefetch_related('linked_item')
.select_related('user__user'))
return filter_visible_tickets(tickets, self.request.user, profile)
else:
return []
# Superusers better be staffs, not the spell-casting kind either. def get_context_data(self, **kwargs):
if self.request.user.is_staff: context = super(TicketFeed, self).get_context_data(**kwargs)
tickets = (Ticket.objects.order_by('-id').filter(is_open=True).prefetch_related('linked_item') context['feed_type'] = 'ticket'
.select_related('user__user')) context['first_page_href'] = self.request.path
context['open_tickets'] = filter_visible_tickets(tickets, self.request.user, profile)[:10] context['page_prefix'] = '?page='
else: context['title'] = _('Ticket feed')
context['open_tickets'] = []
return context
class CommentFeed(FeedView):
model = Comment
context_object_name = 'comments'
paginate_by = 50
def get_queryset(self):
return Comment.most_recent(self.request.user, 1000)
def get_context_data(self, **kwargs):
context = super(CommentFeed, self).get_context_data(**kwargs)
context['feed_type'] = 'comment'
context['first_page_href'] = self.request.path
context['page_prefix'] = '?page='
context['title'] = _('Comment feed')
return context return context

View file

@ -21,7 +21,7 @@ from django.utils.functional import cached_property
from django.utils.html import escape, format_html from django.utils.html import escape, format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _, gettext_lazy from django.utils.translation import gettext as _, gettext_lazy
from django.views.generic import DetailView, ListView, View from django.views.generic import ListView, View
from django.views.generic.base import TemplateResponseMixin from django.views.generic.base import TemplateResponseMixin
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
@ -38,6 +38,7 @@ from judge.utils.problems import contest_attempted_ids, contest_completed_ids, h
from judge.utils.strings import safe_float_or_none, safe_int_or_none from judge.utils.strings import safe_float_or_none, safe_int_or_none
from judge.utils.tickets import own_ticket_filter from judge.utils.tickets import own_ticket_filter
from judge.utils.views import QueryStringSortMixin, SingleObjectFormView, TitleMixin, generic_message from judge.utils.views import QueryStringSortMixin, SingleObjectFormView, TitleMixin, generic_message
from judge.views.blog import FeedView
def get_contest_problem(problem, profile): def get_contest_problem(problem, profile):
@ -155,10 +156,13 @@ class ProblemRaw(ProblemMixin, TitleMixin, TemplateResponseMixin, SingleObjectMi
)) ))
class ProblemDetail(ProblemMixin, SolvedProblemMixin, DetailView): class ProblemDetail(ProblemMixin, SolvedProblemMixin, CommentedDetailView):
context_object_name = 'problem' context_object_name = 'problem'
template_name = 'problem/problem.html' template_name = 'problem/problem.html'
def get_comment_page(self):
return 'p:%s' % self.object.code
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(ProblemDetail, self).get_context_data(**kwargs) context = super(ProblemDetail, self).get_context_data(**kwargs)
user = self.request.user user = self.request.user
@ -235,6 +239,7 @@ class ProblemDetail(ProblemMixin, SolvedProblemMixin, DetailView):
context['min_possible_vote'] = 100 context['min_possible_vote'] = 100
return context return context
class DeleteVote(ProblemMixin, SingleObjectMixin, View): class DeleteVote(ProblemMixin, SingleObjectMixin, View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
return HttpResponseForbidden(status=405, content_type='text/plain') return HttpResponseForbidden(status=405, content_type='text/plain')
@ -273,21 +278,6 @@ class Vote(ProblemMixin, SingleObjectMixin, View):
return JsonResponse(form.errors, status=400) return JsonResponse(form.errors, status=400)
class ProblemComments(ProblemMixin, TitleMixin, CommentedDetailView):
context_object_name = 'problem'
template_name = 'problem/comments.html'
def get_title(self):
return _('Disscuss {0}').format(self.object.name)
def get_content_title(self):
return format_html(_(u'Discuss <a href="{1}">{0}</a>'), self.object.name,
reverse('problem_detail', args=[self.object.code]))
def get_comment_page(self):
return 'p:%s' % self.object.code
class LatexError(Exception): class LatexError(Exception):
pass pass
@ -592,6 +582,49 @@ class ProblemList(QueryStringSortMixin, TitleMixin, SolvedProblemMixin, ListView
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
class ProblemFeed(FeedView):
model = Problem
context_object_name = 'problems'
paginate_by = 50
title = _('Problem feed')
@cached_property
def profile(self):
if not self.request.user.is_authenticated:
return None
return self.request.profile
def get_unsolved_queryset(self):
filter = Q(is_public=True)
if self.profile is not None:
filter |= Q(authors=self.profile)
filter |= Q(curators=self.profile)
filter |= Q(testers=self.profile)
queryset = Problem.objects.filter(filter).select_related('group').defer('description')
if not self.request.user.has_perm('see_organization_problem'):
filter = Q(is_organization_private=False)
if self.profile is not None:
filter |= Q(organizations__in=self.profile.organizations.all())
queryset = queryset.filter(filter)
if self.profile is not None:
queryset = queryset.exclude(id__in=Submission.objects.filter(user=self.profile, points=F('problem__points'))
.values_list('problem__id', flat=True))
return queryset.distinct()
def get_queryset(self):
queryset = self.get_unsolved_queryset()
return queryset.order_by('?')
def get_context_data(self, **kwargs):
context = super(ProblemFeed, self).get_context_data(**kwargs)
context['first_page_href'] = self.request.path
context['page_prefix'] = '?page='
context['feed_type'] = 'problem'
context['title'] = self.title
return context
class LanguageTemplateAjax(View): class LanguageTemplateAjax(View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
try: try:

View file

@ -232,7 +232,7 @@ header {
} }
#navigation { #navigation {
position: relative; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
@ -382,7 +382,7 @@ hr {
} }
#content { #content {
margin: 1em auto auto; margin: 4.5em auto 1em auto;
// Header // Header
width: 90%; width: 90%;

View file

@ -2,9 +2,9 @@
.blog-content { .blog-content {
padding-right: 0em; padding-right: 0em;
flex: 73.5%;
vertical-align: top; vertical-align: top;
margin-right: 0; margin-right: 0;
width: 100%;
.post { .post {
border: 1px dotted grey; border: 1px dotted grey;
@ -33,7 +33,8 @@
} }
.blog-sidebar { .blog-sidebar {
flex: 26.5%; width: 100%;
margin-left: auto;
} }
.blog-sidebox { .blog-sidebox {
@ -88,6 +89,19 @@
color: #555; color: #555;
} }
@media (max-width: 799px) {
.left-sidebar-header {
display: none;
}
.left-sidebar-item {
display: inline-block;
}
.blog-left-sidebar {
text-align: right;
padding-right: 1em;
margin-bottom: 1em;
}
}
@media (min-width: 800px) { @media (min-width: 800px) {
.blog-content, .blog-sidebar { .blog-content, .blog-sidebar {
display: block !important; display: block !important;
@ -104,6 +118,28 @@
#blog-container { #blog-container {
display: flex; display: flex;
} }
.blog-content {
max-width: 71.5%;
margin-left: 10%;
}
.blog-sidebar {
width: 18%;
}
.blog-left-sidebar {
width: 8%;
margin-right: 1em;
position: fixed;
height: 100%;
margin-top: -4em;
padding-top: 4em;
}
.feed-table {
font-size: small;
}
} }
#mobile.tabs { #mobile.tabs {
@ -135,3 +171,63 @@
} }
} }
} }
.blog-box {
border-bottom: 1px solid black;
width: 90%;
margin-bottom: 2.5em;
padding: 0.5em 1.25em;
background-color: white;
margin-left: auto;
margin-right: auto;
}
.blog-description {
max-height: 20em;
overflow: hidden;
overflow-wrap: anywhere;
padding-bottom: 1em;
}
.problem-feed-name {
display: inline;
font-weight: bold;
}
.problem-feed-name a {
color: #0645ad;
}
.problem-feed-info-entry {
display: inline;
float: right;
}
.problem-feed-types {
color: gray;
}
.blog-left-sidebar {
background-color: #f0f1f3;
color: #616161;
}
.left-sidebar-item {
padding: 1em 0.5em;
text-align: center;
}
.left-sidebar-item:hover {
background-color: lightgray;
cursor: pointer;
}
.sidebar-icon {
font-size: x-large;
margin-bottom: 0.1em;
color: black;
}
.left-sidebar-header {
text-align: center;
padding-bottom: 1em;
border-bottom: 1px solid black;
color: black;
border-radius: 0;
}
.feed-table {
margin: 0;
}

View file

@ -1,18 +1,16 @@
<section class="{% if post.sticky %}sticky {% endif %}post"> <section class="{% if post.sticky %}sticky {% endif %}blog-box">
<h2 class="title"> <div style="margin-bottom: 0.5em">
<a href="{{ url('blog_post', post.id, post.slug) }}">{{ post.title }}</a>
</h2>
<span style="float: right">
<span class="time"> <span class="time">
{%- if post.sticky %}<i title="Sticky" class="fa fa-star fa-fw"></i>{% endif -%}
{% with authors=post.authors.all() %} {% with authors=post.authors.all() %}
{%- if authors -%} {%- if authors -%}
<img src="{{gravatar(authors[0])}}" style="width: 1.5em; border-radius: 50%; margin-bottom: -0.3em">
<span class="post-authors">{{ link_users(authors) }}</span> <span class="post-authors">{{ link_users(authors) }}</span>
{%- endif -%} {%- endif -%}
{% endwith %} {% endwith %}
{{_('posted')}} {{ relative_time(post.publish_on, abs=_('on {time}'), rel=_('{time}')) -}} &#8226; {{ relative_time(post.publish_on, abs=_('on {time}'), rel=_('{time}')) -}}
{%- if post.sticky %} &#8226; <i title="Sticky" class="fa fa-star fa-fw"></i>{% endif -%}
</span> </span>
<span> <span style="float: right">
<a href="{{ url('blog_post', post.id, post.slug) }}#comments" class="blog-comment-count-link"> <a href="{{ url('blog_post', post.id, post.slug) }}#comments" class="blog-comment-count-link">
<i class="fa fa-comments blog-comment-icon"></i> <i class="fa fa-comments blog-comment-icon"></i>
<span class="blog-comment-count"> <span class="blog-comment-count">
@ -20,8 +18,10 @@
</span> </span>
</a> </a>
</span> </span>
</span> </div>
<div style="clear:both"></div> <h2 class="title">
<a href="{{ url('blog_post', post.id, post.slug) }}">{{ post.title }}</a>
</h2>
{% if post.is_organization_private and show_organization_private_icon %} {% if post.is_organization_private and show_organization_private_icon %}
<div class="organization-tags"> <div class="organization-tags">
{% for org in post.organizations.all() %} {% for org in post.organizations.all() %}
@ -33,7 +33,7 @@
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
<div class="summary content-description"> <div class="summary content-description blog-description">
{% cache 86400 'post_summary' post.id %} {% cache 86400 'post_summary' post.id %}
{{ post.summary|default(post.content, true)|markdown('blog', 'svg', lazy_load=True)|reference|str|safe }} {{ post.summary|default(post.content, true)|markdown('blog', 'svg', lazy_load=True)|reference|str|safe }}
{% endcache %} {% endcache %}

View file

@ -16,30 +16,10 @@
clear: both; clear: both;
} }
} }
.post {
margin: 0 2%;
}
.time { .time {
margin-left: 0; margin-left: 0;
} }
.post:first-child {
margin-top: 0.6em;
}
.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 { .no-clarifications-message {
font-style: italic; font-style: italic;
text-align: center; text-align: center;
@ -56,6 +36,11 @@
#add-clarification:hover { #add-clarification:hover {
color: cyan; color: cyan;
} }
#content {
width: 99%;
margin-left: 0;
}
</style> </style>
{% endblock %} {% endblock %}
@ -81,6 +66,35 @@
$('.blog-content').hide(); $('.blog-content').hide();
$('.blog-sidebar').show(); $('.blog-sidebar').show();
}); });
$('.blog-description').on('click', function() {
var max_height = $(this).css('max-height');
if (max_height !== 'fit-content') {
$(this).css('max-height', 'fit-content');
$(this).parent().css('background-image', 'inherit')
.css('padding-bottom', '0.5em');
$(this).css('cursor', 'auto');
}
})
$('.blog-description').each(function() {
if ($(this).prop('scrollHeight') > $(this).height() ) {
$(this).parent().css('background-image', '-webkit-linear-gradient(bottom, lightgray, lightgray 3%, transparent 8%, transparent 100%)');
$(this).parent().css('padding-bottom', '0');
$(this).css('cursor', 'pointer');
}
});
$('.left-sidebar-item').on('click', function() {
var url = $(this).attr('data-href');
window.location.replace(url);
});
{% if feed_type == 'blog' %}
$('#news-icon').css('color', 'green');
{% elif feed_type == 'problem' %}
$('#problems-icon').css('color', 'green');
{% elif feed_type == 'ticket' %}
$('#tickets-icon').css('color', 'green');
{% elif feed_type == 'comment' %}
$('#comments-icon').css('color', 'green');
{% endif %}
}); });
</script> </script>
{% endblock %} {% endblock %}
@ -90,20 +104,50 @@
<div id="mobile" class="tabs"> <div id="mobile" class="tabs">
<ul> <ul>
<li id="blog-tab" class="tab active"><a href="#"> <li id="blog-tab" class="tab active"><a href="#">
<i class="tab-icon fa fa-info-circle"></i> {{ _('Blog') }} <i class="tab-icon fa fa-info-circle"></i> {{ _('Feed') }}
</a></li> </a></li>
<li id="event-tab" class="tab"><a href="#"><i class="tab-icon fa fa-rss"></i> {{ _('Events') }}</a></li> <li id="event-tab" class="tab"><a href="#"><i class="tab-icon fa fa-rss"></i> {{ _('Events') }}</a></li>
</ul> </ul>
</div> </div>
<div id="blog-container"> <div id="blog-container">
<div class="blog-content sidebox"> <div class="blog-left-sidebar">
<h3>{{ _('News') }} <i class="fa fa-terminal"></i></h3> <h3 class="left-sidebar-header">{{_('Content')}}</h3>
<div class="sidebox-content"> <div class="left-sidebar-item" data-href="{{url('home')}}">
{% set show_organization_private_icon=True %} <div class="sidebar-icon" id="news-icon"><i class="fa fa-rss"></i></div>
{{_('News')}}
</div>
<div class="left-sidebar-item" data-href="{{url('problem_feed')}}">
<div class="sidebar-icon" id="problems-icon"><i class="fa fa-tasks"></i></div>
{{_('Problems')}}
</div>
<div class="left-sidebar-item" data-href="{{url('comment_feed')}}">
<div class="sidebar-icon" id="comments-icon"><i class="fa fa-comments"></i></div>
{{_('Comments')}}
</div>
<div class="left-sidebar-item" data-href="{{url('ticket_feed')}}">
<div class="sidebar-icon" id="tickets-icon"><i class="fa fa-question-circle"></i></div>
{{_('Tickets')}}
</div>
</div>
<div class="blog-content">
{% set show_organization_private_icon=True %}
{% if feed_type == 'blog' %}
{% for post in posts %} {% for post in posts %}
{% include "blog/content.html" %} {% include "blog/content.html" %}
{% endfor %} {% endfor %}
</div> {% elif feed_type == 'problem' %}
{% for problem in problems %}
{% include "problem/feed.html" %}
{% endfor %}
{% elif feed_type == 'ticket' %}
{% for ticket in tickets %}
{% include "ticket/feed.html" %}
{% endfor %}
{% elif feed_type == 'comment' %}
{% for comment in comments %}
{% include "comments/feed.html" %}
{% endfor %}
{% endif %}
{% if page_obj.num_pages > 1 %} {% if page_obj.num_pages > 1 %}
<div style="margin-bottom:10px;margin-top:10px">{% include "list-pages.html" %}</div> <div style="margin-bottom:10px;margin-top:10px">{% include "list-pages.html" %}</div>
{% endif %} {% endif %}
@ -178,85 +222,36 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% if 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>
<div>{{ link_user(ticket.user) }}</div>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if 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 class="blog-sidebox sidebox"> <div class="blog-sidebox sidebox">
<h3>{{ _('New problems') }} <i class="fa fa-puzzle-piece"></i> <h3>{{ _('Top Rating') }} <i class="fa fa-trophy"></i></h3>
</h3> <div class="sidebox-content" style="padding: 0; border: 0">
<div class="sidebox-content"> <table class="table feed-table">
<ul class="problem-list"> <tbody>
{% for problem in new_problems %} {% for user in top_rated %}
<li><a href="{{ url('problem_detail', problem.code) }}">{{ problem.name }}</a></li> <tr>
{% endfor %} <td style="padding: 7px 2px"><b>{{loop.index}}</b></td>
</ul> <td>{{link_user(user)}}</td>
<span class="rssatom"> <td>{{user.rating}}</td>
<a href="{{ url('problem_rss') }}"><span><i class="fa fa-rss"></i></span> RSS</a> </tr>
/ {% endfor %}
<a href="{{ url('problem_atom') }}">Atom</a> </tbody>
</span> </table>
</div> </div>
</div> </div>
<div class="blog-sidebox sidebox"> <div class="blog-sidebox sidebox">
<h3>{{ _('Comment stream') }} <i class="fa fa-comments"></i></h3> <h3>{{ _('Top Score') }} <i class="fa fa-trophy"></i></h3>
<div class="sidebox-content"> <div class="sidebox-content" style="padding: 0; border: 0">
<ul> <table class="table feed-table">
{% for comment in comments %} <tbody>
<li> {% for user in top_scorer %}
<span style="padding-left:0.25em" class="poster"> <tr>
{{ link_user(comment.author) }} <td style="padding: 7px 2px"><b>{{loop.index}}</b></td>
</span> &rarr; <td>{{link_user(user)}}</td>
<a href="{{ comment.link }}#comment-{{ comment.id }}">{{ page_titles[comment.page] }}</a> <td>{{ user.performance_points|floatformat(0) }}</td>
</li>{% endfor %} </tr>
</ul> {% endfor %}
<span class="rssatom"> </tbody>
<a href="{{ url('comment_rss') }}"><span><i class="fa fa-rss"></i></span> RSS</a> </table>
/
<a href="{{ url('comment_atom') }}">Atom</a>
</span>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,6 +1,6 @@
<style> <style>
#content { #content {
margin: -1em 1em 0 0; margin: 2.5em 1em 0 0;
} }
::-webkit-scrollbar { ::-webkit-scrollbar {

View file

@ -0,0 +1,18 @@
<div class="blog-box">
<h3 class="problem-feed-name">
<a href="{{ comment.link }}#comment-{{ comment.id }}">
{{ page_titles[comment.page] }}
</a>
</h3>
{% with author=comment.author %}
{% if author %}
<div class="problem-feed-info-entry">
<i class="fa fa-pencil-square-o fa-fw"></i>
<span class="pi-value">{{ link_user(author) }}</span>
</div>
{% endif %}
{% endwith %}
<div class='blog-description content-description'>
{{ comment.body |markdown("comment", MATH_ENGINE)|reference|str|safe }}
</div>
</div>

View file

@ -1,28 +0,0 @@
{% extends "common-content.html" %}
{% block content_js_media %}
{% include "comments/media-js.html" %}
{% endblock %}
{% block content_media %}
{% include "comments/media-css.html" %}
<style>
#comment-header {
display: none;
}
.no-comments-message {
margin-top: 0;
}
</style>
{% endblock %}
{% block body %}
{% include "comments/list.html" %}
{% endblock %}
{% block bodyend %}
{% if REQUIRE_JAX %}
{% include "mathjax-load.html" %}
{% endif %}
{% include "comments/math.html" %}
{% endblock %}

View file

@ -0,0 +1,28 @@
<div class="blog-box">
<h3 class="problem-feed-name">
<a href="{{ url('problem_detail', problem.code) }}">
{{ problem.name }}
</a>
</h3>
{% with authors=problem.authors.all() %}
{% 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 %}
{% endwith %}
{% if true %}
<div class="problem-feed-types">
<i class="fa fa-tag"></i>
{% for type in problem.types_list %}
<span class="type-tag">{{ type }}</span>{% if not loop.last %}, {% endif %}
{% endfor %}
</div>
{% endif %}
<div class='blog-description content-description'>
{% cache 86400 'problem_html' problem.id MATH_ENGINE LANGUAGE_CODE %}
{{ problem.description|markdown("problem", MATH_ENGINE)|reference|str|safe }}
{% endcache %}
</div>
</div>

View file

@ -1,5 +1,6 @@
{% extends "common-content.html" %} {% extends "common-content.html" %}
{% block content_media %} {% block content_media %}
{% include "comments/media-css.html" %}
<style> <style>
.title-state { .title-state {
font-size: 2em; font-size: 2em;
@ -54,10 +55,30 @@
#clarification_header:hover { #clarification_header:hover {
color: orange; color: orange;
} }
#comment-announcement {
margin-top: 1em;
background-color: lightgray;
border-radius: 30px;
padding: 0.5em;
text-align: center;
cursor: pointer;
color: dimgrey;
font-weight: bold;
}
#comment-announcement:hover {
background-color: gray;
}
#comment-section {
display: none;
}
</style> </style>
{% endblock %} {% endblock %}
{% block content_js_media %} {% block content_js_media %}
{% include "comments/media-js.html" %}
{% if request.in_contest_mode %} {% if request.in_contest_mode %}
<script type="text/javascript"> <script type="text/javascript">
window.register_contest_notification("{{url('contest_clarification_ajax', request.participation.contest.key)}}"); window.register_contest_notification("{{url('contest_clarification_ajax', request.participation.contest.key)}}");
@ -79,6 +100,14 @@
$('#clarification_header_container').hide(); $('#clarification_header_container').hide();
window.scrollTo(0, document.body.scrollHeight); window.scrollTo(0, document.body.scrollHeight);
}) })
$('#comment-announcement').on('click', function() {
$('#comment-section').show();
$('#comment-announcement').hide();
})
if (window.location.href.includes('#comment')) {
$('#comment-announcement').click();
}
}); });
</script> </script>
{% endblock %} {% endblock %}
@ -164,13 +193,9 @@
{% endif %} {% endif %}
<div><a href="{{ url('chronological_submissions', problem.code) }}">{{ _('All submissions') }}</a></div> <div><a href="{{ url('chronological_submissions', problem.code) }}">{{ _('All submissions') }}</a></div>
<div><a href="{{ url('ranked_submissions', problem.code) }}">{{ _('Best submissions') }}</a></div> <div><a href="{{ url('ranked_submissions', problem.code) }}">{{ _('Best submissions') }}</a></div>
{% if not contest_problem or not contest_problem.contest.use_clarifications %}
<hr>
<div><a href="{{ url('problem_comments', problem.code) }}">{{ _('Discuss') }}</a></div>
{% endif %}
{% if editorial and editorial.is_public and {% if editorial and editorial.is_public and
not (request.user.is_authenticated and request.profile.current_contest) %} not (request.user.is_authenticated and request.profile.current_contest) %}
<hr>
<div><a href="{{ url('problem_editorial', problem.code) }}">{{ _('Read editorial') }}</a></div> <div><a href="{{ url('problem_editorial', problem.code) }}">{{ _('Read editorial') }}</a></div>
{% endif %} {% endif %}
{% if can_edit_problem %} {% if can_edit_problem %}
@ -362,6 +387,17 @@
{%- endif -%} {%- endif -%}
</a> </a>
{% endif %} {% endif %}
{% if not (contest_problem and contest_problem.contest.use_clarifications) %}
<div id="comment-announcement-container">
<div id="comment-announcement">
{% if has_comments %}
{{_('View comments')}} ({{comment_list.count()}})
{% else %}
{{_('Be the first to comment')}}
{% endif %}
</div>
</div>
{% endif %}
{% endblock %} {% endblock %}
{% block comments %} {% block comments %}
@ -381,6 +417,10 @@
<p class="no-comments-message">{{ _('No clarifications have been made at this time.') }}</p> <p class="no-comments-message">{{ _('No clarifications have been made at this time.') }}</p>
{% endif %} {% endif %}
</div> </div>
{% else %}
<div id="comment-section">
{% include "comments/list.html" %}
</div>
{% endif %} {% endif %}
<iframe name="raw_problem" id="raw_problem"></iframe> <iframe name="raw_problem" id="raw_problem"></iframe>
{% endblock %} {% endblock %}

View file

@ -0,0 +1,23 @@
<div class="blog-box">
<h3 class="problem-feed-name">
<a href="{{ url('ticket', ticket.id) }}">
{{ ticket.title }}
</a>
</h3>
{% with author=ticket.user %}
{% if author %}
<div class="problem-feed-info-entry">
<i class="fa fa-pencil-square-o fa-fw"></i>
<span class="pi-value">{{ link_user(author) }}</span>
</div>
{% endif %}
{% endwith %}
<div class="problem-feed-types">
<i class="fa fa-tag"></i>
<a href="{{ ticket.linked_item.get_absolute_url() }}">
{{ ticket.linked_item|item_title }}</a>
</div>
<div class='blog-description content-description'>
{{ ticket.messages.last().body |markdown("ticket", MATH_ENGINE)|reference|str|safe }}
</div>
</div>