Skip to content

Commit

Permalink
Support access organization by subdomain (#344)
Browse files Browse the repository at this point in the history
  • Loading branch information
leduythuccs authored Oct 17, 2023
1 parent 8bd959a commit b676c30
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 33 deletions.
3 changes: 3 additions & 0 deletions dmoj/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@

VNOJ_TAG_PROBLEM_MIN_RATING = 1900 # Minimum rating to be able to tag a problem

# List of subdomain that will be ignored in organization subdomain middleware
VNOJ_IGNORED_ORGANIZATION_SUBDOMAINS = ['oj', 'www', 'localhost']

# Some problems have a lot of testcases, and each testcase
# has about 5~6 fields, so we need to raise this
DATA_UPLOAD_MAX_NUMBER_FIELDS = 3000
Expand Down
26 changes: 25 additions & 1 deletion judge/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
from django.contrib.sites.shortcuts import get_current_site
from django.core.cache import cache
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.urls import Resolver404, resolve, reverse
from django.utils.encoding import force_bytes
from requests.exceptions import HTTPError

from judge.models import MiscConfig
from judge.models import MiscConfig, Organization

try:
import uwsgi
Expand Down Expand Up @@ -181,3 +182,26 @@ def __call__(self, request):
domain = get_current_site(request).domain
request.misc_config = MiscConfigDict(language=request.LANGUAGE_CODE, domain=domain)
return self.get_response(request)


class OrganizationSubdomainMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
subdomain: str = request.get_host().split('.')[0]
if subdomain.isnumeric() or subdomain in settings.VNOJ_IGNORED_ORGANIZATION_SUBDOMAINS:
return self.get_response(request)

request.organization = get_object_or_404(Organization, slug=subdomain)
# if the user is trying to access the home page, redirect to the organization's home page
if request.path == '/':
return HttpResponseRedirect(request.organization.get_absolute_url())

return self.get_response(request)

def process_template_response(self, request, response):
if hasattr(request, 'organization') and 'logo_override_image' not in response.context_data:
# inject the logo override image into the template context
response.context_data['logo_override_image'] = request.organization.logo_override_image
return response
28 changes: 22 additions & 6 deletions judge/views/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from judge.models import BlogPost, Comment, Contest, Language, Organization, OrganizationRequest, \
Problem, Profile
from judge.tasks import on_new_problem
from judge.utils.infinite_paginator import InfinitePaginationMixin
from judge.utils.ranker import ranker
from judge.utils.views import DiggPaginatorMixin, QueryStringSortMixin, TitleMixin, generic_message
from judge.views.blog import BlogPostCreate, PostListBase
Expand Down Expand Up @@ -57,6 +58,12 @@ def dispatch(self, request, *args, **kwargs):

try:
self.object = self.organization

# block the user from viewing other orgs in the subdomain
if self.is_in_organization_subdomain() and self.organization.pk != self.request.organization.pk:
return generic_message(request, _('Cannot view other organizations'),
_('You cannot view other organizations'), status=403)

return super(OrganizationMixin, self).dispatch(request, *args, **kwargs)
except Http404:
slug = kwargs.get('slug', None)
Expand All @@ -74,6 +81,9 @@ def can_edit_organization(self, org=None):
return False
return org.is_admin(self.request.profile) or self.request.user.has_perm('judge.edit_all_organization')

def is_in_organization_subdomain(self):
return hasattr(self.request, 'organization')


# Use this mixin to mark a view is public for all users, including non-members
class PublicOrganizationMixin(OrganizationMixin):
Expand Down Expand Up @@ -151,7 +161,10 @@ def get_queryset(self):

def get_context_data(self, **kwargs):
context = super(OrganizationUsers, self).get_context_data(**kwargs)
context['title'] = self.object.name
if not self.is_in_organization_subdomain():
context['title'] = self.organization.name
else:
context['title'] = _('Members')
context['users'] = ranker(context['users'])
context['partial'] = True
context['is_admin'] = self.can_edit_organization()
Expand Down Expand Up @@ -521,7 +534,8 @@ def get_hot_problems(self):

def get_context_data(self, **kwargs):
context = super(ProblemListOrganization, self).get_context_data(**kwargs)
context['title'] = self.organization.name
if not self.is_in_organization_subdomain():
context['title'] = self.organization.name
return context

def get_filter(self):
Expand Down Expand Up @@ -562,11 +576,12 @@ def _get_queryset(self):

def get_context_data(self, **kwargs):
context = super(ContestListOrganization, self).get_context_data(**kwargs)
context['title'] = self.organization.name
if not self.is_in_organization_subdomain():
context['title'] = self.organization.name
return context


class SubmissionListOrganization(PrivateOrganizationMixin, SubmissionsListBase):
class SubmissionListOrganization(InfinitePaginationMixin, PrivateOrganizationMixin, SubmissionsListBase):
template_name = 'organization/submission-list.html'
permission_bypass = ['judge.view_all_submission']

Expand All @@ -577,8 +592,9 @@ def _get_queryset(self):

def get_context_data(self, **kwargs):
context = super(SubmissionListOrganization, self).get_context_data(**kwargs)
context['title'] = self.organization.name
context['content_title'] = self.organization.name
if not self.is_in_organization_subdomain():
context['title'] = self.organization.name
context['content_title'] = self.organization.name
return context


Expand Down
49 changes: 33 additions & 16 deletions templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -233,22 +233,39 @@
<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 %}">
{{ _(node.label) }}
{% if not node.is_leaf_node() %}
<div class="nav-expand">&gt;</div>
{% endif %}
</a>
{% with children=node.get_children() %}
{% if children %}<ul>{{ loop(children) }}</ul>{% endif %}
{% endwith %}
</li>
{% endfor %}
{% if request.organization is not defined %}
<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 %}">
{{ _(node.label) }}
{% if not node.is_leaf_node() %}
<div class="nav-expand">&gt;</div>
{% endif %}
</a>
{% with children=node.get_children() %}
{% if children %}<ul>{{ loop(children) }}</ul>{% endif %}
{% endwith %}
</li>
{% endfor %}
{% else %}
{% macro make_navbar_item(key, url, text) %}
<li>
<a href="{{ url }}" class="nav-{{ key }}"><span>
{{ text }}
</a>
</li>
{% endmacro %}
{% set org = request.organization %}
<li class="home-nav-element"><a href="{{ url('organization_home', org.slug) }}">{% include "site-logo-fragment.html" %}</a></li>
<li class="home-nav-element"><span class="nav-divider"></span></li>
{{ make_navbar_item('problem-list', url('problem_list_organization', org.slug), _('PRoblems')) }}
{{ make_navbar_item('submission-list', url('submission_list_organization', org.slug), _('Submissions')) }}
{{ make_navbar_item('contest-list', url('contest_list_organization', org.slug), _('Contests')) }}
{{ make_navbar_item('users',org.get_users_url(), _('Users')) }}
{% endif %}
</ul>

<span id="user-links">
Expand Down
25 changes: 15 additions & 10 deletions templates/organization/tabs.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@
{% endmacro %}

<div class="page-title">
<h2>{{ content_title or title }}</h2>
<br>
<div class="tabs">
<ul>
{{ make_tab('home', 'fa-info-circle', url('organization_home', organization.slug), _('Home')) }}
{{ make_tab('users', 'fa-university', organization.get_users_url(), _('Users')) }}
{{ make_tab('problem-list', 'fa-puzzle-piece', url('problem_list_organization', organization.slug), _('Problems list')) }}
{{ make_tab('contest-list', 'fa-trophy', url('contest_list_organization', organization.slug), _('Contests list')) }}
{{ make_tab('submission-list', 'fa-list', url('submission_list_organization', organization.slug), _('Submissions')) }}
</ul>
{% if request.organization is not defined %}
<h2>{{ content_title or title }}</h2>
<br>
<div class="tabs">
<ul>
{{ make_tab('home', 'fa-info-circle', url('organization_home', organization.slug), _('Home')) }}
{{ make_tab('users', 'fa-university', organization.get_users_url(), _('Users')) }}
{{ make_tab('problem-list', 'fa-puzzle-piece', url('problem_list_organization', organization.slug), _('Problems list')) }}
{{ make_tab('contest-list', 'fa-trophy', url('contest_list_organization', organization.slug), _('Contests list')) }}
{{ make_tab('submission-list', 'fa-list', url('submission_list_organization', organization.slug), _('Submissions')) }}
</ul>
{% else %}
<div class="tabs">
<h2>{{ content_title or title }}</h2>
{% endif %}
<span class="spacer"></span>
<ul>
{% if organization.is_admin(request.profile) %}
Expand Down

0 comments on commit b676c30

Please sign in to comment.