Add organization private pages

This commit is contained in:
cuom1999 2020-12-27 23:45:58 -06:00
parent 4c3441b197
commit 4502dbf6b9
14 changed files with 369 additions and 156 deletions

View file

@ -69,6 +69,7 @@ DMOJ_SUBMISSIONS_REJUDGE_LIMIT = 10
# Maximum number of submissions a single user can queue without the `spam_submission` permission # Maximum number of submissions a single user can queue without the `spam_submission` permission
DMOJ_SUBMISSION_LIMIT = 3 DMOJ_SUBMISSION_LIMIT = 3
DMOJ_BLOG_NEW_PROBLEM_COUNT = 7 DMOJ_BLOG_NEW_PROBLEM_COUNT = 7
DMOJ_BLOG_NEW_CONTEST_COUNT = 7
DMOJ_BLOG_RECENTLY_ATTEMPTED_PROBLEMS_COUNT = 7 DMOJ_BLOG_RECENTLY_ATTEMPTED_PROBLEMS_COUNT = 7
DMOJ_TOTP_TOLERANCE_HALF_MINUTES = 1 DMOJ_TOTP_TOLERANCE_HALF_MINUTES = 1
DMOJ_USER_MAX_ORGANIZATION_COUNT = 3 DMOJ_USER_MAX_ORGANIZATION_COUNT = 3

View file

@ -49,6 +49,8 @@ class BlogPostForm(ModelForm):
class Meta: class Meta:
widgets = { widgets = {
'authors': AdminHeavySelect2MultipleWidget(data_view='profile_select2', attrs={'style': 'width: 100%'}), 'authors': AdminHeavySelect2MultipleWidget(data_view='profile_select2', attrs={'style': 'width: 100%'}),
'organizations': AdminHeavySelect2MultipleWidget(data_view='organization_select2',
attrs={'style': 'width: 100%'}),
} }
if HeavyPreviewAdminPageDownWidget is not None: if HeavyPreviewAdminPageDownWidget is not None:
@ -58,7 +60,8 @@ class BlogPostForm(ModelForm):
class BlogPostAdmin(VersionAdmin): class BlogPostAdmin(VersionAdmin):
fieldsets = ( fieldsets = (
(None, {'fields': ('title', 'slug', 'authors', 'visible', 'sticky', 'publish_on')}), (None, {'fields': ('title', 'slug', 'authors', 'visible', 'sticky', 'publish_on',
'is_organization_private', 'organizations')}),
(_('Content'), {'fields': ('content', 'og_image')}), (_('Content'), {'fields': ('content', 'og_image')}),
(_('Summary'), {'classes': ('collapse',), 'fields': ('summary',)}), (_('Summary'), {'classes': ('collapse',), 'fields': ('summary',)}),
) )

View file

@ -15,7 +15,8 @@ class ProblemFeed(Feed):
description = 'The latest problems added on the %s website' % settings.SITE_LONG_NAME description = 'The latest problems added on the %s website' % settings.SITE_LONG_NAME
def items(self): def items(self):
return Problem.objects.filter(is_public=True, is_organization_private=False).order_by('-date', '-id')[:25] return BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now(), is_organization_private=False) \
.order_by('-sticky', '-publish_on')
def item_title(self, problem): def item_title(self, problem):
return problem.name return problem.name

View file

@ -0,0 +1,23 @@
# Generated by Django 2.2.17 on 2020-12-28 03:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('judge', '0113_auto_20201228_0911'),
]
operations = [
migrations.AddField(
model_name='blogpost',
name='is_organization_private',
field=models.BooleanField(default=False, verbose_name='private to organizations'),
),
migrations.AddField(
model_name='blogpost',
name='organizations',
field=models.ManyToManyField(blank=True, help_text='If private, only these organizations may see the blog post.', to='judge.Organization', verbose_name='organizations'),
),
]

View file

@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _
from mptt.fields import TreeForeignKey from mptt.fields import TreeForeignKey
from mptt.models import MPTTModel from mptt.models import MPTTModel
from judge.models.profile import Profile from judge.models.profile import Organization, Profile
__all__ = ['MiscConfig', 'validate_regex', 'NavigationBar', 'BlogPost'] __all__ = ['MiscConfig', 'validate_regex', 'NavigationBar', 'BlogPost']
@ -72,6 +72,9 @@ class BlogPost(models.Model):
content = models.TextField(verbose_name=_('post content')) content = models.TextField(verbose_name=_('post content'))
summary = models.TextField(verbose_name=_('post summary'), blank=True) summary = models.TextField(verbose_name=_('post summary'), blank=True)
og_image = models.CharField(verbose_name=_('openGraph image'), default='', max_length=150, blank=True) og_image = models.CharField(verbose_name=_('openGraph image'), default='', max_length=150, blank=True)
organizations = models.ManyToManyField(Organization, blank=True, verbose_name=_('organizations'),
help_text=_('If private, only these organizations may see the blog post.'))
is_organization_private = models.BooleanField(verbose_name=_('private to organizations'), default=False)
def __str__(self): def __str__(self):
return self.title return self.title
@ -81,11 +84,22 @@ class BlogPost(models.Model):
def can_see(self, user): def can_see(self, user):
if self.visible and self.publish_on <= timezone.now(): if self.visible and self.publish_on <= timezone.now():
return True if not self.is_organization_private:
return True
if user.is_authenticated and \
self.organizations.filter(id__in=user.profile.organizations.all()).exists():
return True
if user.has_perm('judge.edit_all_post'): if user.has_perm('judge.edit_all_post'):
return True return True
return user.is_authenticated and self.authors.filter(id=user.profile.id).exists() return user.is_authenticated and self.authors.filter(id=user.profile.id).exists()
def is_editable_by(self, user):
if not user.is_authenticated:
return False
if user.has_perm('judge.edit_all_post'):
return True
return user.has_perm('judge.change_blogpost') and self.authors.filter(id=user.profile.id).exists()
class Meta: class Meta:
permissions = ( permissions = (
('edit_all_post', _('Edit all posts')), ('edit_all_post', _('Edit all posts')),

View file

@ -56,8 +56,9 @@ class BlogPostSitemap(Sitemap):
priority = 0.7 priority = 0.7
def items(self): def items(self):
return BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now()).values_list('id', 'slug') return (BlogPost.objects.filter(visible=True, is_organization_private=False, publish_on__lte=timezone.now())
.values_list('id', 'slug'))
def location(self, obj): def location(self, obj):
return reverse('blog_post', args=obj) return reverse('blog_post', args=obj)

View file

@ -30,8 +30,15 @@ class PostList(ListView):
orphans=orphans, allow_empty_first_page=allow_empty_first_page, **kwargs) orphans=orphans, allow_empty_first_page=allow_empty_first_page, **kwargs)
def get_queryset(self): def get_queryset(self):
return (BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now()).order_by('-sticky', '-publish_on') queryset = BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now()) \
.prefetch_related('authors__user')) .order_by('-sticky', '-publish_on') \
.prefetch_related('authors__user', 'organizations')
if not self.request.user.has_perm('judge.edit_all_post'):
filter = Q(is_organization_private=False)
if self.request.user.is_authenticated:
filter |= Q(organizations__in=self.request.profile.organizations.all())
queryset = queryset.filter(filter)
return queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(PostList, self).get_context_data(**kwargs) context = super(PostList, self).get_context_data(**kwargs)
@ -107,7 +114,7 @@ class PostView(TitleMixin, CommentedDetailView):
model = BlogPost model = BlogPost
pk_url_kwarg = 'id' pk_url_kwarg = 'id'
context_object_name = 'post' context_object_name = 'post'
template_name = 'blog/content.html' template_name = 'blog/blog.html'
def get_title(self): def get_title(self):
return self.object.title return self.object.title

View file

@ -6,17 +6,18 @@ from django.core.cache import cache
from django.core.cache.utils import make_template_fragment_key from django.core.cache.utils import make_template_fragment_key
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db import transaction from django.db import transaction
from django.db.models import Count, Q from django.db.models import Count, Q, Value, BooleanField
from django.forms import Form, modelformset_factory from django.forms import Form, modelformset_factory
from django.http import Http404, HttpResponsePermanentRedirect, HttpResponseRedirect from django.http import Http404, HttpResponsePermanentRedirect, HttpResponseRedirect
from django.urls import reverse from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext as _, gettext_lazy, ungettext from django.utils.translation import gettext as _, gettext_lazy, ungettext
from django.views.generic import DetailView, FormView, ListView, UpdateView, View from django.views.generic import DetailView, FormView, ListView, UpdateView, View
from django.views.generic.detail import SingleObjectMixin, SingleObjectTemplateResponseMixin from django.views.generic.detail import SingleObjectMixin, SingleObjectTemplateResponseMixin
from reversion import revisions from reversion import revisions
from judge.forms import EditOrganizationForm from judge.forms import EditOrganizationForm
from judge.models import Organization, OrganizationRequest, Profile from judge.models import BlogPost, Comment, Organization, OrganizationRequest, Problem, Profile, Contest
from judge.utils.ranker import ranker from judge.utils.ranker import ranker
from judge.utils.views import TitleMixin, generic_message from judge.utils.views import TitleMixin, generic_message
@ -25,8 +26,21 @@ __all__ = ['OrganizationList', 'OrganizationHome', 'OrganizationUsers', 'Organiz
'OrganizationRequestDetail', 'OrganizationRequestView', 'OrganizationRequestLog', 'OrganizationRequestDetail', 'OrganizationRequestView', 'OrganizationRequestLog',
'KickUserWidgetView'] 'KickUserWidgetView']
class OrganizationBase(object):
def can_edit_organization(self, org=None):
if org is None:
org = self.object
if not self.request.user.is_authenticated:
return False
profile_id = self.request.profile.id
return org.admins.filter(id=profile_id).exists() or org.registrant_id == profile_id
class OrganizationMixin(object): def is_member(self, org=None):
if org is None:
org = self.object
return self.request.profile in org if self.request.user.is_authenticated else False
class OrganizationMixin(OrganizationBase):
context_object_name = 'organization' context_object_name = 'organization'
model = Organization model = Organization
@ -46,15 +60,7 @@ class OrganizationMixin(object):
else: else:
return generic_message(request, _('No such organization'), return generic_message(request, _('No such organization'),
_('Could not find such organization.')) _('Could not find such organization.'))
def can_edit_organization(self, org=None):
if org is None:
org = self.object
if not self.request.user.is_authenticated:
return False
profile_id = self.request.profile.id
return org.admins.filter(id=profile_id).exists() or org.registrant_id == profile_id
class OrganizationDetailView(OrganizationMixin, DetailView): class OrganizationDetailView(OrganizationMixin, DetailView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -65,7 +71,7 @@ class OrganizationDetailView(OrganizationMixin, DetailView):
return self.render_to_response(context) return self.render_to_response(context)
class OrganizationList(TitleMixin, ListView): class OrganizationList(TitleMixin, ListView, OrganizationBase):
model = Organization model = Organization
context_object_name = 'organizations' context_object_name = 'organizations'
template_name = 'organization/list.html' template_name = 'organization/list.html'
@ -74,6 +80,15 @@ class OrganizationList(TitleMixin, ListView):
def get_queryset(self): def get_queryset(self):
return super(OrganizationList, self).get_queryset().annotate(member_count=Count('member')) return super(OrganizationList, self).get_queryset().annotate(member_count=Count('member'))
def get_context_data(self, **kwargs):
context = super(OrganizationList, self).get_context_data(**kwargs)
context['my_organizations'] = set()
for organization in context['organizations']:
if self.can_edit_organization(organization) or self.is_member(organization):
context['my_organizations'].add(organization)
return context
class OrganizationHome(OrganizationDetailView): class OrganizationHome(OrganizationDetailView):
template_name = 'organization/home.html' template_name = 'organization/home.html'
@ -82,6 +97,23 @@ class OrganizationHome(OrganizationDetailView):
context = super(OrganizationHome, self).get_context_data(**kwargs) context = super(OrganizationHome, self).get_context_data(**kwargs)
context['title'] = self.object.name context['title'] = self.object.name
context['can_edit'] = self.can_edit_organization() context['can_edit'] = self.can_edit_organization()
context['is_member'] = self.is_member()
context['new_problems'] = Problem.objects.filter(is_public=True, is_organization_private=True,
organizations=self.object) \
.order_by('-date', '-id')[:settings.DMOJ_BLOG_NEW_PROBLEM_COUNT]
context['new_contests'] = Contest.objects.filter(is_visible=True, is_organization_private=True,
organizations=self.object) \
.order_by('-end_time', '-id')[:settings.DMOJ_BLOG_NEW_CONTEST_COUNT]
context['posts'] = BlogPost.objects.filter(visible=True, publish_on__lte=timezone.now(),
is_organization_private=True, organizations=self.object) \
.order_by('-sticky', '-publish_on') \
.prefetch_related('authors__user', 'organizations')
context['post_comment_counts'] = {
int(page[2:]): count for page, count in
Comment.objects.filter(page__in=['b:%d' % post.id for post in context['posts']], hidden=False)
.values_list('page').annotate(count=Count('page')).order_by()
}
return context return context

View file

@ -76,6 +76,18 @@
} }
} }
.blog-comment-count {
font-size: 12px;
}
.blog-comment-icon {
padding: 0.1em 0.2em 0 0.5em;
}
.blog-comment-count-link {
color: #555;
}
@media (min-width: 800px) { @media (min-width: 800px) {
.blog-content, .blog-sidebar { .blog-content, .blog-sidebar {
display: block !important; display: block !important;

52
templates/blog/blog.html Normal file
View file

@ -0,0 +1,52 @@
{% extends "base.html" %}
{% block js_media %}
{% include "comments/media-js.html" %}
{% endblock %}
{% block media %}
{% include "comments/media-css.html" %}
{% endblock %}
{% block header %}
{% if post.is_editable_by(request.user) %}
<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 %}

View file

@ -1,53 +1,40 @@
{% extends "base.html" %} <section class="{% if post.sticky %}sticky {% endif %}post">
<span style="float:right">
{% block js_media %} <span class="time">
{% include "comments/media-js.html" %} {%- if post.sticky %}<i title="Sticky" class="fa fa-star fa-fw"></i>{% endif -%}
{% 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() %} {% with authors=post.authors.all() %}
{% if authors %} {%- if authors -%}
<span class="post-authors">{{ link_users(authors) }}</span> <span class="post-authors">{{ link_users(authors) }}</span>
{% endif %} {%- endif -%}
{% endwith %} {% endwith %}
<span class="post-time"> {{ relative_time(post.publish_on, abs=_('posted on {time}'), rel=_('posted {time}')) -}}
{% trans time=post.publish_on|date(_("N j, Y, g:i a")) %} </span>
posted on {{ time }} <span>
{% endtrans %} <a href="{{ url('blog_post', post.id, post.slug) }}#comments" class="blog-comment-count-link">
</span> <i class="fa fa-comments blog-comment-icon"></i>
</div> <span class="blog-comment-count">
<div class="body content-description"> {{- post_comment_counts[post.id] or 0 -}}
{% cache 86400 'post_content' post.id MATH_ENGINE %} </span>
{{ post.content|markdown('blog', MATH_ENGINE)|reference|str|safe}} </a>
{% endcache %} </span>
</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> </span>
{% include "comments/list.html" %} <h2 class="title">
{% endblock %} <a href="{{ url('blog_post', post.id, post.slug) }}">{{ post.title }}</a>
</h2>
{% block bodyend %} {% if post.is_organization_private and show_organization_private_icon %}
{{ super() }} <div class="organization-tags">
{% if REQUIRE_JAX %} {% for org in post.organizations.all() %}
{% include "mathjax-load.html" %} <span class="organization-tag">
<a href="{{ org.get_absolute_url() }}">
<i class="fa fa-lock"></i> {{ org.name }}
</a>
</span>
{% endfor %}
</div>
{% endif %} {% endif %}
{% include "comments/math.html" %} <div class="summary content-description">
{% endblock %} {% cache 86400 'post_summary' post.id %}
{{ post.summary|default(post.content, true)|markdown('blog', 'svg', lazy_load=True)|reference|str|safe }}
{% endcache %}
</div>
</section>

View file

@ -27,18 +27,6 @@
margin-top: 0.6em; 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 { .own-open-tickets .title a, .open-tickets .title a {
display: block; display: block;
} }
@ -103,35 +91,9 @@
<div class="blog-content sidebox"> <div class="blog-content sidebox">
<h3>{{ _('News') }} <i class="fa fa-terminal"></i></h3> <h3>{{ _('News') }} <i class="fa fa-terminal"></i></h3>
<div class="sidebox-content"> <div class="sidebox-content">
{% set show_organization_private_icon=True %}
{% for post in posts %} {% for post in posts %}
<section class="{% if post.sticky %}sticky {% endif %}post"> {% include "blog/content.html" %}
<span style="float:right;">
<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>
</span>
<h2 class="title">
<a href="{{ url('blog_post', post.id, post.slug) }}">{{ post.title }}</a>
</h2>
<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 %} {% endfor %}
</div> </div>
{% if page_obj.num_pages > 1 %} {% if page_obj.num_pages > 1 %}

View file

@ -1,6 +1,19 @@
{% extends "common-content.html" %} {% extends "base.html" %}
{% block title_row %}{% endblock %}
{% block title_ruler %}{% endblock %}
{% block content_js_media %} <!-- {% block media %}
<style>
.post {
margin: 0 1.4em;
}
.post:first-child {
margin-top: 0.6em;
}
</style>
{% endblock %} -->
{% block js_media %}
<script type="text/javascript"> <script type="text/javascript">
$(function () { $(function () {
$('.leave-organization').click(function () { $('.leave-organization').click(function () {
@ -12,52 +25,133 @@
{% endif %} {% endif %}
); );
}); });
$(document).ready(function () {
$('.blog-sidebar').hide();
$('#info-tab').find('a').click(function (e) {
e.preventDefault();
$('#info-tab').addClass('active');
$('#detail-tab').removeClass('active');
$('.blog-content').show();
$('.blog-sidebar').hide();
});
$('#detail-tab').find('a').click(function (e) {
e.preventDefault();
$('#detail-tab').addClass('active');
$('#info-tab').removeClass('active');
$('.blog-content').hide();
$('.blog-sidebar').show();
});
});
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block info_float %} {% block body %}
{% if request.user.is_authenticated %} {% block before_posts %}{% endblock %}
{% if request.profile in organization %} <div id="mobile" class="tabs">
<form method="post" action="{{ url('leave_organization', organization.id, organization.slug) }}"> <ul>
{% csrf_token %} <li id="info-tab" class="tab active"><a href="#">
<input type="submit" class="unselectable button full leave-organization" value="{{ _('Leave organization') }}"> <i class="tab-icon fa fa-info-circle"></i> {{ _('Info') }}
</form> </a></li>
{% elif organization.is_open %} <li id="detail-tab" class="tab"><a href="#"><i class="tab-icon fa fa-rss"></i> {{ _('Details') }}</a></li>
<form method="post" action="{{ url('join_organization', organization.id, organization.slug) }}"> </ul>
{% 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> </div>
{% endblock %} <div id="blog-container">
<div class="blog-content sidebox">
<h3>{{ _('About') }} {{ organization.name }} <i class="fa fa-info-circle"></i></h3>
<div class="sidebox-content">
<div style="margin: 1.4em;">
{% cache 3600 'organization_html' organization.id MATH_ENGINE %}
{{ organization.about|markdown('organization-about', MATH_ENGINE)|reference|str|safe }}
{% endcache %}
</div>
</div>
{% if is_member or can_edit %}
<br>
<h3>{{ _('Organization news') }} <i class="fa fa-terminal"></i></h3>
<div class="sidebox-content">
{% for post in posts %}
{% include "blog/content.html" %}
{% else %}
<div style="margin: 1.4em;">
<i>{{ _('There is no news at this time.') }}</i>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% block description %} <div class="blog-sidebar">
{% cache 3600 'organization_html' organization.id MATH_ENGINE %} <div class="blog-sidebox sidebox">
{{ organization.about|markdown('organization-about', MATH_ENGINE)|reference|str|safe }} <h3>{{ _('Controls') }} <i class="fa fa-cog"></i></h3>
{% endcache %} <div class="sidebox-content" style="padding: 1em;">
{% endblock %} {% if request.user.is_authenticated %}
{% if is_member %}
<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 or can_edit %}
<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 %}
<br>
{% 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>
</div>
</div>
{% if (is_member or can_edit) %}
{% if new_contests %}
<div class="blog-sidebox sidebox">
<h3>{{ _('New private contests') }} <i class="fa fa-trophy"></i>
</h3>
<div class="sidebox-content">
<ul class="problem-list">
{% for contest in new_contests %}
<li><a href="{{ url('contest_view', contest.key) }}">{{ contest.name }}</a></li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if new_problems %}
<div class="blog-sidebox sidebox">
<h3>{{ _('New private 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>
</div>
</div>
{% endif %}
{% endif %}
</div>
</div>
{% block after_posts %}{% endblock %}
{% endblock %}

View file

@ -4,8 +4,25 @@
<script type="text/javascript"> <script type="text/javascript">
$(function () { $(function () {
$("#organization-table").tablesorter(); $("#organization-table").tablesorter();
{% if request.user.is_authenticated %}
$('#show-my-org-checkbox').click(function() {
let checked = $('#show-my-org-checkbox').is(':checked');
if (checked) {
$('.other-organization').hide();
$('.my-organization').last().find('td').css({'border-bottom-width':
'1px', 'border-color': '#ccc'});
}
else {
$('.other-organization').show();
$('.my-organization').last().find('td').css({'border-bottom-width':
'', 'border-color': ''});
}
})
$('#show-my-org-checkbox').click()
{% endif %}
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block title_ruler %}{% endblock %} {% block title_ruler %}{% endblock %}
@ -17,6 +34,13 @@
{% endblock %} {% endblock %}
{% block body %} {% block body %}
{% if request.user.is_authenticated %}
<div style="margin-bottom: 0.5em">
<input id="show-my-org-checkbox" type="checkbox" style="vertical-align: bottom;">
<label for="show-my-org-checkbox" style="vertical-align: bottom; margin-right: 1em;">{{ _('Show my organizations only') }}</label>
</div>
{% endif %}
<table id="organization-table" class="table"> <table id="organization-table" class="table">
<thead> <thead>
<tr> <tr>
@ -26,7 +50,7 @@
</thead> </thead>
<tbody> <tbody>
{% for org in organizations %} {% for org in organizations %}
<tr> <tr class="{{ 'my-organization' if org in my_organizations else 'other-organization'}}">
<td><a href="{{ org.get_absolute_url() }}">{{ org.name }}</a></td> <td><a href="{{ org.get_absolute_url() }}">{{ org.name }}</a></td>
<td><a href="{{ org.get_users_url() }}">{{ org.member_count }}</a></td> <td><a href="{{ org.get_users_url() }}">{{ org.member_count }}</a></td>
</tr> </tr>