mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-09-21 08:23:20 +00:00
Merge branch 'dev' into virusdefender-dev
* dev: (27 commits) 统一get参数格式 修改一些错误 这个是刚才api地方的,忘了add上了 修改较多,涉及到小组管理员对比赛的管理,小组管理员可以看到他管理的小组的其他管理员创建的比赛,但看不到别人的题目,但是可以从前台看到比赛的题目,可以在比赛开始前测试题目 修改小组列表模板,适应数据库的修改 添加提升小组管理员的api,调整小组管理权限的认证方式 修改后台小组管理功能添加设为管理员按钮,方便添加多管理员 修改group的models添加小组管理员的多对多字段,把原来的管理员字段重命名为创建者 修改学校判断和自动统一队形的方法 统一格式 注释掉了用户主页里还没有后端配套的submission部分,添加学校显示,修复了settings里codeforces用户名无法编辑的问题,原来是html里边拼错了 统一userprofile字段的处理方式,都判断是否为none,修复typo 修改settings中语言为新版本的'zh-hans' 针对添加学号字段对页面的一些修改,注册是学校为青岛大学则显示学号字段,在user settings页面显示学号,并提供修改 在UserProfile中添加学号字段 吧if 。。。or...改成 if in 修改错误 验证小组邀请赛密码 contest list 添加小组邀请赛和私有小组赛 添加小组邀请赛 ...
This commit is contained in:
commit
75ae8e1656
20
account/migrations/0015_userprofile_student_id.py
Normal file
20
account/migrations/0015_userprofile_student_id.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9 on 2015-12-08 06:22
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('account', '0014_auto_20151110_1037'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userprofile',
|
||||||
|
name='student_id',
|
||||||
|
field=models.CharField(blank=True, max_length=15, null=True),
|
||||||
|
),
|
||||||
|
]
|
@ -70,6 +70,7 @@ class UserProfile(models.Model):
|
|||||||
problems_status = JSONField(default={})
|
problems_status = JSONField(default={})
|
||||||
phone_number = models.CharField(max_length=15, blank=True, null=True)
|
phone_number = models.CharField(max_length=15, blank=True, null=True)
|
||||||
school = models.CharField(max_length=200, blank=True, null=True)
|
school = models.CharField(max_length=200, blank=True, null=True)
|
||||||
|
student_id = models.CharField(max_length=15, blank=True, null=True)
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -25,6 +25,7 @@ class UserRegisterSerializer(serializers.Serializer):
|
|||||||
password = serializers.CharField(max_length=30, min_length=6)
|
password = serializers.CharField(max_length=30, min_length=6)
|
||||||
email = serializers.EmailField(max_length=254)
|
email = serializers.EmailField(max_length=254)
|
||||||
captcha = serializers.CharField(max_length=4, min_length=4)
|
captcha = serializers.CharField(max_length=4, min_length=4)
|
||||||
|
student_id = serializers.CharField(max_length=15, required=False, default=None)
|
||||||
|
|
||||||
|
|
||||||
class UserChangePasswordSerializer(serializers.Serializer):
|
class UserChangePasswordSerializer(serializers.Serializer):
|
||||||
@ -74,6 +75,7 @@ class EditUserProfileSerializer(serializers.Serializer):
|
|||||||
codeforces_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='')
|
codeforces_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='')
|
||||||
school = serializers.CharField(max_length=200, required=False, allow_blank=True, default='')
|
school = serializers.CharField(max_length=200, required=False, allow_blank=True, default='')
|
||||||
phone_number = serializers.CharField(max_length=15, required=False, allow_blank=True, default='')
|
phone_number = serializers.CharField(max_length=15, required=False, allow_blank=True, default='')
|
||||||
|
student_id = serializers.CharField(max_length=15, required=False, default="")
|
||||||
|
|
||||||
|
|
||||||
class UserProfileSerializer(serializers.ModelSerializer):
|
class UserProfileSerializer(serializers.ModelSerializer):
|
||||||
@ -81,4 +83,4 @@ class UserProfileSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = UserProfile
|
model = UserProfile
|
||||||
fields = ["avatar", "blog", "mood", "hduoj_username", "bestcoder_username", "codeforces_username",
|
fields = ["avatar", "blog", "mood", "hduoj_username", "bestcoder_username", "codeforces_username",
|
||||||
"rank", "accepted_number", "submissions_number", "problems_status", "phone_number", "school"]
|
"rank", "accepted_number", "submissions_number", "problems_status", "phone_number", "school", "student_id"]
|
||||||
|
@ -97,7 +97,7 @@ class UserRegisterAPIView(APIView):
|
|||||||
email=data["email"])
|
email=data["email"])
|
||||||
user.set_password(data["password"])
|
user.set_password(data["password"])
|
||||||
user.save()
|
user.save()
|
||||||
UserProfile.objects.create(user=user, school=data["school"])
|
UserProfile.objects.create(user=user, school=data["school"], student_id=data["student_id"])
|
||||||
return success_response(u"注册成功!")
|
return success_response(u"注册成功!")
|
||||||
else:
|
else:
|
||||||
return serializer_invalid_response(serializer)
|
return serializer_invalid_response(serializer)
|
||||||
@ -262,6 +262,7 @@ class UserProfileAPIView(APIView):
|
|||||||
user_profile.codeforces_username = data["codeforces_username"]
|
user_profile.codeforces_username = data["codeforces_username"]
|
||||||
user_profile.blog = data["blog"]
|
user_profile.blog = data["blog"]
|
||||||
user_profile.school = data["school"]
|
user_profile.school = data["school"]
|
||||||
|
user_profile.student_id = data["student_id"]
|
||||||
user_profile.phone_number = data["phone_number"]
|
user_profile.phone_number = data["phone_number"]
|
||||||
user_profile.save()
|
user_profile.save()
|
||||||
return success_response(u"修改成功")
|
return success_response(u"修改成功")
|
||||||
|
@ -8,8 +8,8 @@ from django.core.urlresolvers import reverse
|
|||||||
|
|
||||||
from utils.shortcuts import error_response, error_page
|
from utils.shortcuts import error_response, error_page
|
||||||
|
|
||||||
from account.models import SUPER_ADMIN
|
from account.models import SUPER_ADMIN, ADMIN
|
||||||
from .models import (Contest, PASSWORD_PROTECTED_CONTEST, PUBLIC_CONTEST, GROUP_CONTEST,
|
from .models import (Contest, PASSWORD_PROTECTED_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST, PUBLIC_CONTEST, GROUP_CONTEST,
|
||||||
CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY)
|
CONTEST_ENDED, CONTEST_NOT_START, CONTEST_UNDERWAY)
|
||||||
|
|
||||||
|
|
||||||
@ -57,7 +57,10 @@ def check_user_contest_permission(func):
|
|||||||
|
|
||||||
if request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by:
|
if request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
if request.user.admin_type == ADMIN:
|
||||||
|
contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all())
|
||||||
|
if contest in contest_set:
|
||||||
|
return func(*args, **kwargs)
|
||||||
# 管理员可见隐藏的比赛,已经先判断了身份
|
# 管理员可见隐藏的比赛,已经先判断了身份
|
||||||
if not contest.visible:
|
if not contest.visible:
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
@ -84,6 +87,15 @@ def check_user_contest_permission(func):
|
|||||||
return render(request, "oj/contest/no_contest_permission.html",
|
return render(request, "oj/contest/no_contest_permission.html",
|
||||||
{"reason": "group_limited", "show_tab": False, "contest": contest})
|
{"reason": "group_limited", "show_tab": False, "contest": contest})
|
||||||
|
|
||||||
|
if contest.contest_type == PASSWORD_PROTECTED_GROUP_CONTEST:
|
||||||
|
if not contest.groups.filter(id__in=request.user.group_set.all()).exists():
|
||||||
|
if contest.id not in request.session.get("contests", []):
|
||||||
|
if request.is_ajax():
|
||||||
|
return error_response(u"请先输入密码")
|
||||||
|
else:
|
||||||
|
return render(request, "oj/contest/no_contest_permission.html",
|
||||||
|
{"reason": "password_protect", "show_tab": False, "contest": contest})
|
||||||
|
|
||||||
# 比赛没有开始
|
# 比赛没有开始
|
||||||
if contest.status == CONTEST_NOT_START:
|
if contest.status == CONTEST_NOT_START:
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
|
@ -13,6 +13,7 @@ from judge.judger.result import result
|
|||||||
GROUP_CONTEST = 0
|
GROUP_CONTEST = 0
|
||||||
PUBLIC_CONTEST = 1
|
PUBLIC_CONTEST = 1
|
||||||
PASSWORD_PROTECTED_CONTEST = 2
|
PASSWORD_PROTECTED_CONTEST = 2
|
||||||
|
PASSWORD_PROTECTED_GROUP_CONTEST = 3
|
||||||
|
|
||||||
CONTEST_NOT_START = 1
|
CONTEST_NOT_START = 1
|
||||||
CONTEST_ENDED = -1
|
CONTEST_ENDED = -1
|
||||||
|
@ -17,13 +17,13 @@ from utils.shortcuts import (serializer_invalid_response, error_response,
|
|||||||
success_response, paginate, error_page, paginate_data)
|
success_response, paginate, error_page, paginate_data)
|
||||||
from account.models import SUPER_ADMIN, User
|
from account.models import SUPER_ADMIN, User
|
||||||
from account.decorators import login_required, super_admin_required
|
from account.decorators import login_required, super_admin_required
|
||||||
from group.models import Group
|
from group.models import Group, AdminGroupRelation, UserGroupRelation
|
||||||
from utils.cache import get_cache_redis
|
from utils.cache import get_cache_redis
|
||||||
from submission.models import Submission
|
from submission.models import Submission
|
||||||
from problem.models import Problem
|
from problem.models import Problem
|
||||||
from .models import (Contest, ContestProblem, CONTEST_ENDED,
|
from .models import (Contest, ContestProblem, CONTEST_ENDED,
|
||||||
CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank)
|
CONTEST_NOT_START, CONTEST_UNDERWAY, ContestRank)
|
||||||
from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST
|
from .models import GROUP_CONTEST, PUBLIC_CONTEST, PASSWORD_PROTECTED_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST
|
||||||
from .decorators import check_user_contest_permission
|
from .decorators import check_user_contest_permission
|
||||||
from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer,
|
from .serializers import (CreateContestSerializer, ContestSerializer, EditContestSerializer,
|
||||||
CreateContestProblemSerializer, ContestProblemSerializer,
|
CreateContestProblemSerializer, ContestProblemSerializer,
|
||||||
@ -50,11 +50,11 @@ class ContestAdminAPIView(APIView):
|
|||||||
if request.user.admin_type != SUPER_ADMIN:
|
if request.user.admin_type != SUPER_ADMIN:
|
||||||
return error_response(u"只有超级管理员才可创建公开赛")
|
return error_response(u"只有超级管理员才可创建公开赛")
|
||||||
|
|
||||||
if data["contest_type"] == PASSWORD_PROTECTED_CONTEST:
|
if data["contest_type"] in [PASSWORD_PROTECTED_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST]:
|
||||||
if not data["password"]:
|
if not data["password"]:
|
||||||
return error_response(u"此比赛为有密码的公开赛,密码不可为空")
|
return error_response(u"此比赛为有密码的比赛,密码不可为空")
|
||||||
# 没有密码的公开赛 没有密码的小组赛
|
# 没有密码的公开赛 没有密码的小组赛
|
||||||
elif data["contest_type"] == GROUP_CONTEST:
|
if data["contest_type"] == GROUP_CONTEST or data["contest_type"] == PASSWORD_PROTECTED_GROUP_CONTEST:
|
||||||
if request.user.admin_type == SUPER_ADMIN:
|
if request.user.admin_type == SUPER_ADMIN:
|
||||||
groups = Group.objects.filter(id__in=data["groups"])
|
groups = Group.objects.filter(id__in=data["groups"])
|
||||||
else:
|
else:
|
||||||
@ -91,8 +91,10 @@ class ContestAdminAPIView(APIView):
|
|||||||
try:
|
try:
|
||||||
# 超级管理员可以编辑所有的
|
# 超级管理员可以编辑所有的
|
||||||
contest = Contest.objects.get(id=data["id"])
|
contest = Contest.objects.get(id=data["id"])
|
||||||
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
|
if request.user.admin_type != SUPER_ADMIN:
|
||||||
return error_response(u"无权访问!")
|
contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all())
|
||||||
|
if contest not in contest_set:
|
||||||
|
return error_response(u"无权访问!")
|
||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return error_response(u"该比赛不存在!")
|
return error_response(u"该比赛不存在!")
|
||||||
try:
|
try:
|
||||||
@ -107,7 +109,7 @@ class ContestAdminAPIView(APIView):
|
|||||||
if data["contest_type"] == PASSWORD_PROTECTED_CONTEST:
|
if data["contest_type"] == PASSWORD_PROTECTED_CONTEST:
|
||||||
if not data["password"]:
|
if not data["password"]:
|
||||||
return error_response(u"此比赛为有密码的公开赛,密码不可为空")
|
return error_response(u"此比赛为有密码的公开赛,密码不可为空")
|
||||||
elif data["contest_type"] == GROUP_CONTEST:
|
elif data["contest_type"] in [GROUP_CONTEST, PASSWORD_PROTECTED_GROUP_CONTEST]:
|
||||||
if request.user.admin_type == SUPER_ADMIN:
|
if request.user.admin_type == SUPER_ADMIN:
|
||||||
groups = Group.objects.filter(id__in=data["groups"])
|
groups = Group.objects.filter(id__in=data["groups"])
|
||||||
else:
|
else:
|
||||||
@ -151,16 +153,18 @@ class ContestAdminAPIView(APIView):
|
|||||||
# 普通管理员只能获取自己创建的题目
|
# 普通管理员只能获取自己创建的题目
|
||||||
# 超级管理员可以获取全部的题目
|
# 超级管理员可以获取全部的题目
|
||||||
contest = Contest.objects.get(id=contest_id)
|
contest = Contest.objects.get(id=contest_id)
|
||||||
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
|
if request.user.admin_type != SUPER_ADMIN:
|
||||||
return error_response(u"题目不存在")
|
contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all())
|
||||||
|
if contest not in contest_set:
|
||||||
|
return error_response(u"比赛不存在")
|
||||||
return success_response(ContestSerializer(contest).data)
|
return success_response(ContestSerializer(contest).data)
|
||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return error_response(u"题目不存在")
|
return error_response(u"比赛不存在")
|
||||||
|
|
||||||
if request.user.admin_type == SUPER_ADMIN:
|
if request.user.admin_type == SUPER_ADMIN:
|
||||||
contest = Contest.objects.all().order_by("-create_time")
|
contest = Contest.objects.all().order_by("-create_time")
|
||||||
else:
|
else:
|
||||||
contest = Contest.objects.filter(created_by=request.user).order_by("-create_time")
|
contest = Contest.objects.filter(groups__in=request.user.managed_groups.all()).distinct().order_by("-create_time")
|
||||||
visible = request.GET.get("visible", None)
|
visible = request.GET.get("visible", None)
|
||||||
if visible:
|
if visible:
|
||||||
contest = contest.filter(visible=(visible == "true"))
|
contest = contest.filter(visible=(visible == "true"))
|
||||||
@ -184,8 +188,10 @@ class ContestProblemAdminAPIView(APIView):
|
|||||||
data = serializer.data
|
data = serializer.data
|
||||||
try:
|
try:
|
||||||
contest = Contest.objects.get(id=data["contest_id"])
|
contest = Contest.objects.get(id=data["contest_id"])
|
||||||
if request.user.admin_type != SUPER_ADMIN and contest.created_by != request.user:
|
if request.user.admin_type != SUPER_ADMIN:
|
||||||
return error_response(u"比赛不存在")
|
contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all())
|
||||||
|
if contest not in contest_set:
|
||||||
|
return error_response(u"比赛不存在")
|
||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return error_response(u"比赛不存在")
|
return error_response(u"比赛不存在")
|
||||||
contest_problem = ContestProblem.objects.create(title=data["title"],
|
contest_problem = ContestProblem.objects.create(title=data["title"],
|
||||||
@ -362,7 +368,10 @@ def contest_problem_page(request, contest_id, contest_problem_id):
|
|||||||
request.user.admin_type == SUPER_ADMIN or \
|
request.user.admin_type == SUPER_ADMIN or \
|
||||||
request.user == contest.created_by:
|
request.user == contest.created_by:
|
||||||
show_submit_code_area = True
|
show_submit_code_area = True
|
||||||
|
else:
|
||||||
|
contest_set = Contest.objects.filter(groups__in=request.user.managed_groups.all())
|
||||||
|
if contest in contest_set:
|
||||||
|
show_submit_code_area = True
|
||||||
return render(request, "oj/problem/contest_problem.html", {"problem": problem,
|
return render(request, "oj/problem/contest_problem.html", {"problem": problem,
|
||||||
"contest": contest,
|
"contest": contest,
|
||||||
"samples": json.loads(problem.samples),
|
"samples": json.loads(problem.samples),
|
||||||
|
20
group/migrations/0006_auto_20151209_1834.py
Normal file
20
group/migrations/0006_auto_20151209_1834.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9 on 2015-12-09 10:34
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('group', '0005_joingrouprequest_accepted'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='group',
|
||||||
|
old_name='admin',
|
||||||
|
new_name='created_by',
|
||||||
|
),
|
||||||
|
]
|
38
group/migrations/0007_auto_20151209_1836.py
Normal file
38
group/migrations/0007_auto_20151209_1836.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9 on 2015-12-09 10:36
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('group', '0006_auto_20151209_1834'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AdminGroupRelation',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='group.Group')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'admin_group_relation',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='group',
|
||||||
|
name='admin',
|
||||||
|
field=models.ManyToManyField(related_name='managed_groups', through='group.AdminGroupRelation', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='admingrouprelation',
|
||||||
|
unique_together=set([('user', 'group')]),
|
||||||
|
),
|
||||||
|
]
|
@ -8,10 +8,11 @@ class Group(models.Model):
|
|||||||
name = models.CharField(max_length=30, unique=True)
|
name = models.CharField(max_length=30, unique=True)
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
create_time = models.DateTimeField(auto_now_add=True)
|
create_time = models.DateTimeField(auto_now_add=True)
|
||||||
admin = models.ForeignKey(User, related_name="my_groups")
|
created_by = models.ForeignKey(User, related_name="my_groups")
|
||||||
# 0是公开 1是需要申请后加入 2是不允许任何人加入
|
# 0是公开 1是需要申请后加入 2是不允许任何人加入
|
||||||
join_group_setting = models.IntegerField(default=1)
|
join_group_setting = models.IntegerField(default=1)
|
||||||
members = models.ManyToManyField(User, through="UserGroupRelation")
|
members = models.ManyToManyField(User, through="UserGroupRelation")
|
||||||
|
admin = models.ManyToManyField(User, through="AdminGroupRelation", related_name="managed_groups")
|
||||||
# 解散小组后,这一项改为False
|
# 解散小组后,这一项改为False
|
||||||
visible = models.BooleanField(default=True)
|
visible = models.BooleanField(default=True)
|
||||||
|
|
||||||
@ -29,6 +30,16 @@ class UserGroupRelation(models.Model):
|
|||||||
unique_together = ("group", "user")
|
unique_together = ("group", "user")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AdminGroupRelation(models.Model):
|
||||||
|
user = models.ForeignKey(User)
|
||||||
|
group = models.ForeignKey(Group)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "admin_group_relation"
|
||||||
|
unique_together = ("user", "group")
|
||||||
|
|
||||||
|
|
||||||
class JoinGroupRequest(models.Model):
|
class JoinGroupRequest(models.Model):
|
||||||
group = models.ForeignKey(Group)
|
group = models.ForeignKey(Group)
|
||||||
user = models.ForeignKey(User, related_name="my_join_group_requests")
|
user = models.ForeignKey(User, related_name="my_join_group_requests")
|
||||||
|
@ -74,3 +74,7 @@ class EditGroupMemberSerializer(serializers.Serializer):
|
|||||||
class PutJoinGroupRequestSerializer(serializers.Serializer):
|
class PutJoinGroupRequestSerializer(serializers.Serializer):
|
||||||
request_id = serializers.IntegerField()
|
request_id = serializers.IntegerField()
|
||||||
status = serializers.BooleanField()
|
status = serializers.BooleanField()
|
||||||
|
|
||||||
|
class GroupPromoteAdminSerializer(serializers.Serializer):
|
||||||
|
user_id = serializers.IntegerField()
|
||||||
|
group_id = serializers.IntegerField()
|
||||||
|
@ -5,14 +5,14 @@ from django.db import IntegrityError
|
|||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from utils.shortcuts import error_response, serializer_invalid_response, success_response, paginate, error_page
|
from utils.shortcuts import error_response, serializer_invalid_response, success_response, paginate, error_page
|
||||||
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN
|
from account.models import REGULAR_USER, ADMIN, SUPER_ADMIN, User
|
||||||
from account.decorators import login_required
|
from account.decorators import login_required
|
||||||
|
|
||||||
from .models import Group, JoinGroupRequest, UserGroupRelation
|
from .models import Group, JoinGroupRequest, UserGroupRelation, AdminGroupRelation
|
||||||
from .serializers import (CreateGroupSerializer, EditGroupSerializer,
|
from .serializers import (CreateGroupSerializer, EditGroupSerializer,
|
||||||
CreateJoinGroupRequestSerializer, GroupSerializer,
|
CreateJoinGroupRequestSerializer, GroupSerializer,
|
||||||
GroupMemberSerializer, EditGroupMemberSerializer,
|
GroupMemberSerializer, EditGroupMemberSerializer,
|
||||||
JoinGroupRequestSerializer, PutJoinGroupRequestSerializer)
|
JoinGroupRequestSerializer, PutJoinGroupRequestSerializer, GroupPromoteAdminSerializer)
|
||||||
from announcement.models import Announcement
|
from announcement.models import Announcement
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
@ -57,9 +57,10 @@ class GroupAdminAPIView(APIView, GroupAPIViewBase):
|
|||||||
group = Group.objects.create(name=data["name"],
|
group = Group.objects.create(name=data["name"],
|
||||||
description=data["description"],
|
description=data["description"],
|
||||||
join_group_setting=data["join_group_setting"],
|
join_group_setting=data["join_group_setting"],
|
||||||
admin=request.user)
|
created_by=request.user)
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
return error_response(u"小组名已经存在")
|
return error_response(u"小组名已经存在")
|
||||||
|
AdminGroupRelation.objects.create(group=group, user=request.user)
|
||||||
return success_response(GroupSerializer(group).data)
|
return success_response(GroupSerializer(group).data)
|
||||||
else:
|
else:
|
||||||
return serializer_invalid_response(serializer)
|
return serializer_invalid_response(serializer)
|
||||||
@ -132,8 +133,13 @@ class GroupMemberAdminAPIView(APIView, GroupAPIViewBase):
|
|||||||
group = self.get_group(request, group_id)
|
group = self.get_group(request, group_id)
|
||||||
except Group.DoesNotExist:
|
except Group.DoesNotExist:
|
||||||
return error_response(u"小组不存在")
|
return error_response(u"小组不存在")
|
||||||
|
admin_only = request.GET.get("admin_only", None)
|
||||||
|
if admin_only:
|
||||||
|
members = AdminGroupRelation.objects.filter(group=group)
|
||||||
|
else:
|
||||||
|
members = UserGroupRelation.objects.filter(group=group)
|
||||||
|
|
||||||
return paginate(request, UserGroupRelation.objects.filter(group=group), GroupMemberSerializer)
|
return paginate(request, members, GroupMemberSerializer)
|
||||||
|
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
"""
|
"""
|
||||||
@ -217,7 +223,7 @@ class JoinGroupRequestAdminAPIView(APIView, GroupAPIViewBase):
|
|||||||
---
|
---
|
||||||
response_serializer: JoinGroupRequestSerializer
|
response_serializer: JoinGroupRequestSerializer
|
||||||
"""
|
"""
|
||||||
requests = JoinGroupRequest.objects.filter(group=Group.objects.filter(admin=request.user, visible=True),
|
requests = JoinGroupRequest.objects.filter(group__in=Group.objects.filter(admin=request.user, visible=True),
|
||||||
status=False)
|
status=False)
|
||||||
return paginate(request, requests, JoinGroupRequestSerializer)
|
return paginate(request, requests, JoinGroupRequestSerializer)
|
||||||
|
|
||||||
@ -314,3 +320,30 @@ def application_page(request, request_id):
|
|||||||
return error_page(request, u"申请不存在")
|
return error_page(request, u"申请不存在")
|
||||||
return render(request, "oj/group/my_application.html",
|
return render(request, "oj/group/my_application.html",
|
||||||
{"application": application})
|
{"application": application})
|
||||||
|
|
||||||
|
|
||||||
|
class GroupPrometAdminAPIView(APIView):
|
||||||
|
def post(self, request):
|
||||||
|
"""
|
||||||
|
创建小组管理员的api
|
||||||
|
---
|
||||||
|
request_serializer: GroupPromoteAdminSerializer
|
||||||
|
"""
|
||||||
|
serializer = GroupPromoteAdminSerializer(data=request.data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
data = serializer.data
|
||||||
|
try:
|
||||||
|
group = Group.objects.get(id=data["group_id"])
|
||||||
|
except Group.DoesNotExist:
|
||||||
|
return error_response(u"小组不存在")
|
||||||
|
try:
|
||||||
|
user = User.objects.get(id=data["user_id"])
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return error_response(u"用户不存在")
|
||||||
|
try:
|
||||||
|
AdminGroupRelation.objects.create(user=user, group=group)
|
||||||
|
except IntegrityError:
|
||||||
|
return error_response(u"该用户已经是管理员了")
|
||||||
|
return success_response(u"操作成功")
|
||||||
|
else:
|
||||||
|
return serializer_invalid_response(serializer)
|
||||||
|
@ -97,7 +97,7 @@ WSGI_APPLICATION = 'oj.wsgi.application'
|
|||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
||||||
|
|
||||||
LANGUAGE_CODE = 'zh-cn'
|
LANGUAGE_CODE = 'zh-hans'
|
||||||
|
|
||||||
TIME_ZONE = 'Asia/Shanghai'
|
TIME_ZONE = 'Asia/Shanghai'
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from contest.views import (ContestAdminAPIView, ContestProblemAdminAPIView,
|
|||||||
MakeContestProblemPublicAPIView)
|
MakeContestProblemPublicAPIView)
|
||||||
|
|
||||||
from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView,
|
from group.views import (GroupAdminAPIView, GroupMemberAdminAPIView,
|
||||||
JoinGroupAPIView, JoinGroupRequestAdminAPIView)
|
JoinGroupAPIView, JoinGroupRequestAdminAPIView, GroupPrometAdminAPIView)
|
||||||
|
|
||||||
from admin.views import AdminTemplateView
|
from admin.views import AdminTemplateView
|
||||||
|
|
||||||
@ -56,12 +56,15 @@ urlpatterns = [
|
|||||||
url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"),
|
url(r'^api/submission/$', SubmissionAPIView.as_view(), name="submission_api"),
|
||||||
url(r'^api/group_join/$', JoinGroupAPIView.as_view(), name="group_join_api"),
|
url(r'^api/group_join/$', JoinGroupAPIView.as_view(), name="group_join_api"),
|
||||||
|
|
||||||
|
|
||||||
url(r'^api/admin/upload_image/$', SimditorImageUploadAPIView.as_view(), name="simditor_upload_image"),
|
url(r'^api/admin/upload_image/$', SimditorImageUploadAPIView.as_view(), name="simditor_upload_image"),
|
||||||
url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"),
|
url(r'^api/admin/announcement/$', AnnouncementAdminAPIView.as_view(), name="announcement_admin_api"),
|
||||||
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
|
url(r'^api/admin/contest/$', ContestAdminAPIView.as_view(), name="contest_admin_api"),
|
||||||
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
|
url(r'^api/admin/user/$', UserAdminAPIView.as_view(), name="user_admin_api"),
|
||||||
url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"),
|
url(r'^api/admin/group/$', GroupAdminAPIView.as_view(), name="group_admin_api"),
|
||||||
url(r'^api/admin/group_member/$', GroupMemberAdminAPIView.as_view(), name="group_member_admin_api"),
|
url(r'^api/admin/group_member/$', GroupMemberAdminAPIView.as_view(), name="group_member_admin_api"),
|
||||||
|
url(r'^api/admin/group/promot_as_admin/$', GroupPrometAdminAPIView.as_view(), name="group_promote_admin_api"),
|
||||||
|
|
||||||
|
|
||||||
url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"),
|
url(r'^api/admin/problem/$', ProblemAdminAPIView.as_view(), name="problem_admin_api"),
|
||||||
url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"),
|
url(r'^api/admin/contest_problem/$', ContestProblemAdminAPIView.as_view(), name="contest_problem_admin_api"),
|
||||||
|
@ -103,14 +103,7 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
vm.$watch("showGroupDetailPage", function (groupId) {
|
|
||||||
vm.groupId = groupId;
|
|
||||||
vm.template_url = "template/group/group_detail.html";
|
|
||||||
});
|
|
||||||
|
|
||||||
vm.$watch("showGroupListPage", function () {
|
|
||||||
vm.template_url = "template/group/group.html";
|
|
||||||
});
|
|
||||||
|
|
||||||
avalon.scan();
|
avalon.scan();
|
||||||
|
|
||||||
|
@ -22,6 +22,10 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
selectedGroups.push(vm.allGroups[i].id);
|
selectedGroups.push(vm.allGroups[i].id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (vm.password) {
|
||||||
|
ajaxData.password = vm.password;
|
||||||
|
ajaxData.contest_type = 3;
|
||||||
|
}
|
||||||
ajaxData.groups = selectedGroups;
|
ajaxData.groups = selectedGroups;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -23,6 +23,10 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
selectedGroups.push(vm.allGroups[i].id);
|
selectedGroups.push(vm.allGroups[i].id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (vm.password) {
|
||||||
|
ajaxData.password = vm.password;
|
||||||
|
ajaxData.contest_type = 3;
|
||||||
|
}
|
||||||
ajaxData.groups = selectedGroups;
|
ajaxData.groups = selectedGroups;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -131,7 +135,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
|
|||||||
vm.startTime = contest.start_time.substring(0, 16).replace("T", " ");
|
vm.startTime = contest.start_time.substring(0, 16).replace("T", " ");
|
||||||
vm.endTime = contest.end_time.substring(0, 16).replace("T", " ");
|
vm.endTime = contest.end_time.substring(0, 16).replace("T", " ");
|
||||||
vm.password = contest.password;
|
vm.password = contest.password;
|
||||||
if (contest.contest_type == 0) { //contest_type == 0, 小组内比赛
|
if (contest.contest_type == 0 || contest.contest_type == 3) { //contest_type == 0, 小组内比赛
|
||||||
vm.isGlobal = false;
|
vm.isGlobal = false;
|
||||||
for (var i = 0; i < vm.allGroups.length; i++) {
|
for (var i = 0; i < vm.allGroups.length; i++) {
|
||||||
vm.allGroups[i].isSelected = false;
|
vm.allGroups[i].isSelected = false;
|
||||||
|
@ -1,7 +1,30 @@
|
|||||||
require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfTokenHeader, bsAlert) {
|
require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($, avalon, csrfTokenHeader, bsAlert) {
|
||||||
|
|
||||||
avalon.ready(function () {
|
avalon.ready(function () {
|
||||||
//avalon.vmodels.group = null;
|
$('#add-group-form').validator().on('submit', function (e) {
|
||||||
|
if (!e.isDefaultPrevented()) {
|
||||||
|
var name = vm.name;
|
||||||
|
var description = vm.description;
|
||||||
|
var join_group_setting = vm.group_type;
|
||||||
|
$.ajax({
|
||||||
|
beforeSend: csrfTokenHeader,
|
||||||
|
url: "/api/admin/group/",
|
||||||
|
method: "post",
|
||||||
|
data: {name: name, description: description, join_group_setting: join_group_setting},
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data) {
|
||||||
|
if (!data.code) {
|
||||||
|
getPageData(1);
|
||||||
|
bsAlert("添加成功");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bsAlert(data.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
if (avalon.vmodels.group) {
|
if (avalon.vmodels.group) {
|
||||||
var vm = avalon.vmodels.group;
|
var vm = avalon.vmodels.group;
|
||||||
}
|
}
|
||||||
@ -16,7 +39,9 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
|
|||||||
page: 1, // 当前页数
|
page: 1, // 当前页数
|
||||||
totalPage: 1, // 总页数
|
totalPage: 1, // 总页数
|
||||||
keyword: "",
|
keyword: "",
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
group_type: 0,
|
||||||
getNext: function () {
|
getNext: function () {
|
||||||
if (!vm.nextPage)
|
if (!vm.nextPage)
|
||||||
return;
|
return;
|
||||||
@ -42,8 +67,10 @@ require(["jquery", "avalon", "csrfToken", "bsAlert"], function ($, avalon, csrfT
|
|||||||
getGroupSettingString: function (setting) {
|
getGroupSettingString: function (setting) {
|
||||||
return {0: "允许任何人加入", 1: "提交请求后管理员审核", 2: "不允许任何人加入"}[setting]
|
return {0: "允许任何人加入", 1: "提交请求后管理员审核", 2: "不允许任何人加入"}[setting]
|
||||||
},
|
},
|
||||||
|
|
||||||
showGroupDetailPage: function (groupId) {
|
showGroupDetailPage: function (groupId) {
|
||||||
vm.$fire("up!showGroupDetailPage", groupId);
|
avalon.vmodels.admin.groupId = groupId;
|
||||||
|
avalon.vmodels.admin.template_url = "template/group/group_detail.html";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($, avalon, csrfTokenHeader, bsAlert) {
|
require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($, avalon, csrfTokenHeader, bsAlert) {
|
||||||
|
|
||||||
|
|
||||||
// avalon:定义模式 group_list
|
// avalon:定义模式 group_list
|
||||||
avalon.ready(function () {
|
avalon.ready(function () {
|
||||||
|
|
||||||
@ -53,7 +52,20 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "validator"], function ($,
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
showGroupListPage: function () {
|
showGroupListPage: function () {
|
||||||
|
avalon.vmodels.admin.template_url = "template/group/group.html";
|
||||||
vm.$fire("up!showGroupListPage");
|
vm.$fire("up!showGroupListPage");
|
||||||
|
},
|
||||||
|
promotAsAdmin: function (relation) {
|
||||||
|
$.ajax({
|
||||||
|
beforeSend: csrfTokenHeader,
|
||||||
|
url: "/api/admin/group/promot_as_admin/",
|
||||||
|
method: "post",
|
||||||
|
data: JSON.stringify({group_id: relation.group, user_id: relation.user.id}),
|
||||||
|
contentType: "application/json;charset=UTF-8",
|
||||||
|
success: function (data) {
|
||||||
|
bsAlert(data.data);
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
|
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
|
||||||
|
$("#stu_id").hide();
|
||||||
$('form').validator().on('submit', function (e) {
|
$('form').validator().on('submit', function (e) {
|
||||||
if (!e.isDefaultPrevented()) {
|
if (!e.isDefaultPrevented()) {
|
||||||
var username = $("#username").val();
|
var username = $("#username").val();
|
||||||
var realName = $("#real_name").val();
|
var realName = $("#real_name").val();
|
||||||
var school = $('#school').val();
|
var school = $('#school').val();
|
||||||
|
var student_id = $('#student_id').val();
|
||||||
var password = $("#password").val();
|
var password = $("#password").val();
|
||||||
var email = $("#email").val();
|
var email = $("#email").val();
|
||||||
var captcha = $("#captcha").val();
|
var captcha = $("#captcha").val();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
beforeSend: csrfTokenHeader,
|
beforeSend: csrfTokenHeader,
|
||||||
url: "/api/register/",
|
url: "/api/register/",
|
||||||
data: {username: username, school: school, real_name: realName, password: password, email: email, captcha:captcha},
|
data: {username: username, school: school, student_id: student_id, real_name: realName, password: password, email: email, captcha: captcha},
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
method: "post",
|
method: "post",
|
||||||
success: function (data) {
|
success: function (data) {
|
||||||
@ -29,6 +31,14 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#school").blur(function () {
|
||||||
|
var school = $("#school").val().trim(school).toLowerCase();
|
||||||
|
if (school == "青岛大学" || school == "qdu" || school == "青大") {
|
||||||
|
$("#stu_id").show();
|
||||||
|
$("#school").val("青岛大学");
|
||||||
|
}
|
||||||
|
});
|
||||||
function refresh_captcha() {
|
function refresh_captcha() {
|
||||||
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
|
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
|
||||||
$("#captcha")[0].value = "";
|
$("#captcha")[0].value = "";
|
||||||
|
@ -8,7 +8,7 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
|
|||||||
var blog = $("#blog").val();
|
var blog = $("#blog").val();
|
||||||
var mood = $("#mood").val();
|
var mood = $("#mood").val();
|
||||||
var school = $("#school").val();
|
var school = $("#school").val();
|
||||||
|
var student_id = $("#student_id").val();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
beforeSend: csrfTokenHeader,
|
beforeSend: csrfTokenHeader,
|
||||||
url: "/api/account/userprofile/",
|
url: "/api/account/userprofile/",
|
||||||
@ -19,7 +19,8 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
|
|||||||
codeforces_username: codeforces_username,
|
codeforces_username: codeforces_username,
|
||||||
blog: blog,
|
blog: blog,
|
||||||
mood: mood,
|
mood: mood,
|
||||||
school: school
|
school: school,
|
||||||
|
student_id: student_id
|
||||||
},
|
},
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
method: "put",
|
method: "put",
|
||||||
|
@ -3,9 +3,10 @@ require(["jquery", "csrfToken", "bsAlert"], function ($, csrfTokenHeader, bsAler
|
|||||||
var message;
|
var message;
|
||||||
if ($("#applyMessage").length) {
|
if ($("#applyMessage").length) {
|
||||||
message = $("#applyMessage").val();
|
message = $("#applyMessage").val();
|
||||||
if (!message)
|
if (!message) {
|
||||||
bsAlert("提交失败,请填写申请信息!");
|
bsAlert("提交失败,请填写申请信息!");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var groupId = window.location.pathname.split("/")[2];
|
var groupId = window.location.pathname.split("/")[2];
|
||||||
|
@ -46,13 +46,13 @@
|
|||||||
<div>
|
<div>
|
||||||
<span ms-if="showGlobalViewRadio">
|
<span ms-if="showGlobalViewRadio">
|
||||||
<label>
|
<label>
|
||||||
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">全局可见
|
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">公开赛
|
||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<label>
|
<label>
|
||||||
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组内可见
|
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组赛
|
||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
@ -60,10 +60,18 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6" ms-visible="isGlobal">
|
<div class="col-md-6" >
|
||||||
<label>密码保护</label>
|
<div ms-visible="isGlobal">
|
||||||
<div class="form-group">
|
<label>密码保护</label>
|
||||||
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ms-visible="!isGlobal">
|
||||||
|
<label>邀请密码</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="password" placeholder="留空则只有小组内可以参加" ms-duplex="password">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
||||||
|
@ -55,13 +55,13 @@
|
|||||||
<div>
|
<div>
|
||||||
<span ms-if="showGlobalViewRadio">
|
<span ms-if="showGlobalViewRadio">
|
||||||
<label>
|
<label>
|
||||||
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">全局可见
|
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">公开赛
|
||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<label>
|
<label>
|
||||||
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组内可见
|
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组赛
|
||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
</span>
|
</span>
|
||||||
@ -69,11 +69,19 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6" ms-visible="isGlobal">
|
<div class="col-md-6">
|
||||||
<label>密码保护</label>
|
<div ms-visible="isGlobal">
|
||||||
|
<label>密码保护</label>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
|
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ms-visible="!isGlobal">
|
||||||
|
<label>邀请密码</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="password" placeholder="留空则只有小组内可以参加" ms-duplex="password">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
<div class="form-group col-md-12" ms-visible="!isGlobal">
|
||||||
|
@ -38,6 +38,35 @@
|
|||||||
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
|
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
|
||||||
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
|
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
|
||||||
</div>
|
</div>
|
||||||
|
<h2>创建小组</h2>
|
||||||
|
<form id="add-group-form">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group"><label>小组名</label>
|
||||||
|
<input type="text" name="name" class="form-control" ms-duplex="name"
|
||||||
|
data-error="请填写小组名(名称不能超过20字)" maxlength="20" required>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group"><label>描述</label>
|
||||||
|
<textarea rows="3" name="description" class="form-control" ms-duplex="description"
|
||||||
|
data-error="请填写描述" maxlength="300" required>
|
||||||
|
</textarea>
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>加入小组设置</label>
|
||||||
|
<input type="radio" name="join_group_setting" value="0" ms-duplex-string="group_type">允许任何人加入
|
||||||
|
<input type="radio" name="join_group_setting" value="1" ms-duplex-string="group_type">提交请求后管理员审核
|
||||||
|
<input type="radio" name="join_group_setting" value="2" ms-duplex-string="group_type">不允许任何人加入
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<input type="submit" class="btn btn-success btn-lg" value="创建小组" id="submitBtn">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script src="/static/js/app/admin/group/group.js"></script>
|
<script src="/static/js/app/admin/group/group.js"></script>
|
@ -5,7 +5,7 @@
|
|||||||
aria-hidden="true">←</span> 返回</a></li>
|
aria-hidden="true">←</span> 返回</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<h1>小组成员管理</h1>
|
<h2>小组成员管理</h2>
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
@ -21,17 +21,17 @@
|
|||||||
<td>{{ el.join_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
|
<td>{{ el.join_time|date("yyyy-MM-dd HH:mm:ss")}}</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
|
<button class="btn-sm btn-info" ms-click="promotAsAdmin(el)">设为管理员</button>
|
||||||
<button class="btn-sm btn-danger" ms-click="removeMember(el)">移除</button>
|
<button class="btn-sm btn-danger" ms-click="removeMember(el)">移除</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
页数:{{ page }}/{{ totalPage }}
|
页数:{{ page }}/{{ totalPage }}
|
||||||
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
|
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
|
||||||
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
|
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
|
||||||
</div>
|
</div>
|
||||||
<h1>修改小组信息</h1>
|
<h2>修改小组信息</h2>
|
||||||
|
|
||||||
<form id="edit_group_form">
|
<form id="edit_group_form">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
|
@ -19,10 +19,14 @@
|
|||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="real_name">学校</label>
|
<label for="school">学校</label>
|
||||||
<input type="text" class="form-control input-lg" id="school" name="school" placeholder="学校" data-error="请填写学校" required>
|
<input type="text" class="form-control input-lg" id="school" name="school" placeholder="学校" data-error="请填写学校" required>
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" id="stu_id">
|
||||||
|
<label for="student_id">学号</label>
|
||||||
|
<input type="number" class="form-control input-lg" id="student_id" name="student_id" placeholder="非必填,如果你需要使用课程功能,请填写该字段">
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email">邮箱地址</label>
|
<label for="email">邮箱地址</label>
|
||||||
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址" data-remote="/api/email_check/" data-remote-error="该邮箱已被注册!" data-error="请填写正确的邮箱地址" required>
|
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址" data-remote="/api/email_check/" data-remote-error="该邮箱已被注册!" data-error="请填写正确的邮箱地址" required>
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
value="{{ request.user.email }}" readonly>
|
value="{{ request.user.email }}" readonly>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6"><label>手机</label>
|
<div class="form-group col-md-6"><label>手机</label>
|
||||||
<input name="phone" type="text" maxlength="11" id="phone"
|
<input name="phone_number" type="text" maxlength="11" id="phone"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
value="{% if request.user.userprofile.phone_number %}{{ request.user.userprofile.phone_number }}{% endif %}">
|
value="{% if request.user.userprofile.phone_number %}{{ request.user.userprofile.phone_number }}{% endif %}">
|
||||||
</div>
|
</div>
|
||||||
@ -50,28 +50,35 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label>hduoj 用户名</label>
|
<label>hduoj 用户名</label>
|
||||||
<input name="hduoj" type="text" class="form-control" id="hduoj_username"
|
<input name="hduoj_username" type="text" class="form-control" id="hduoj_username"
|
||||||
value="{{ request.user.userprofile.hduoj_username }}">
|
value="{% if request.user.userprofile.hduoj_username %}{{ request.user.userprofile.hduoj_username }}{% endif %}">
|
||||||
|
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label>BestCoder 用户名</label>
|
<label>BestCoder 用户名</label>
|
||||||
<input name="bestcoder" type="text" class="form-control" id="bestcoder_username"
|
<input name="bestcoder_username" type="text" class="form-control" id="bestcoder_username"
|
||||||
value="{{ request.user.userprofile.bestcoder_username }}">
|
value="{% if request.user.userprofile.bestcoder_username %}{{ request.user.userprofile.bestcoder_username }}{% endif %}">
|
||||||
|
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-6">
|
<div class="form-group col-md-6">
|
||||||
<label>Codeforces 用户名</label>
|
<label>Codeforces 用户名</label>
|
||||||
<input name="codeforces" type="text" class="form-control" id="codeforce_username"
|
<input name="codeforces_username" type="text" class="form-control" id="codeforces_username"
|
||||||
value="{{ request.user.userprofile.bestcoder_username }}">
|
value="{% if request.user.userprofile.codeforce_username %}{{ request.user.userprofile.codeforces_username }}{% endif %}">
|
||||||
|
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group col-md-12"><label>学号</label>
|
||||||
|
<input name="student_id" type="number" class="form-control" id="student_id"
|
||||||
|
value="{% if request.user.userprofile.student_id %}{{ request.user.userprofile.student_id }}{% endif %}">
|
||||||
|
|
||||||
|
<div class="help-block with-errors"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
<div class="form-group col-md-12"><label>blog</label>
|
<div class="form-group col-md-12"><label>blog</label>
|
||||||
<input name="blog" type="url" class="form-control" id="blog"
|
<input name="blog" type="url" class="form-control" id="blog"
|
||||||
value="{{ request.user.userprofile.blog }}">
|
value="{% if request.user.userprofile.blog %}{{ request.user.userprofile.blog }}{% endif %}">
|
||||||
|
|
||||||
<div class="help-block with-errors"></div>
|
<div class="help-block with-errors"></div>
|
||||||
|
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
{% if user.userprofile.mood %}
|
{% if user.userprofile.mood %}
|
||||||
<p id="user-mood">{{ user.userprofile.mood }}</p>
|
<p id="user-mood">{{ user.userprofile.mood }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if user.userprofile.school %}
|
||||||
|
<p id="user-mood">{{ user.userprofile.school }}</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="list-group col-lg-9">
|
<div class="list-group col-lg-9">
|
||||||
|
|
||||||
@ -68,7 +71,7 @@
|
|||||||
<strong id="user-data-number">{{ user.userprofile.rank }}</strong>
|
<strong id="user-data-number">{{ user.userprofile.rank }}</strong>
|
||||||
<span id="user-data-text">Rank</span>
|
<span id="user-data-text">Rank</span>
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
<div class="col-lg-6 text-center">
|
<div class="col-lg-6 text-center">
|
||||||
<strong id="user-data-number">{{ user.userprofile.accepted_number }}</strong>
|
<strong id="user-data-number">{{ user.userprofile.accepted_number }}</strong>
|
||||||
<span id="user-data-text">AC</span>
|
<span id="user-data-text">AC</span>
|
||||||
@ -77,6 +80,7 @@
|
|||||||
<strong id="user-data-number">{{ user.userprofile.submissions_number }}</strong>
|
<strong id="user-data-number">{{ user.userprofile.submissions_number }}</strong>
|
||||||
<span id="user-data-text">Submissions</span>
|
<span id="user-data-text">Submissions</span>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<td>{{ contest.end_time }}</td>
|
<td>{{ contest.end_time }}</td>
|
||||||
<td>{{ contest|contest_status }}</td>
|
<td>{{ contest|contest_status }}</td>
|
||||||
{% ifequal contest.contest_type 0 %}
|
{% ifequal contest.contest_type 0 %}
|
||||||
<td>小组赛</td>
|
<td>私有小组赛</td>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
{% ifequal contest.contest_type 1 %}
|
{% ifequal contest.contest_type 1 %}
|
||||||
<td>公开赛</td>
|
<td>公开赛</td>
|
||||||
@ -29,6 +29,9 @@
|
|||||||
{% ifequal contest.contest_type 2 %}
|
{% ifequal contest.contest_type 2 %}
|
||||||
<td>公开赛(密码保护)</td>
|
<td>公开赛(密码保护)</td>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
|
{% ifequal contest.contest_type 3 %}
|
||||||
|
<td>小组邀请赛</td>
|
||||||
|
{% endifequal %}
|
||||||
|
|
||||||
<td>{{ contest.created_by.username }}</td>
|
<td>{{ contest.created_by.username }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
<td>{{ item.start_time }}</td>
|
<td>{{ item.start_time }}</td>
|
||||||
|
|
||||||
{% ifequal item.contest_type 0 %}
|
{% ifequal item.contest_type 0 %}
|
||||||
<td>小组赛</td>
|
<td>私有小组赛</td>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
{% ifequal item.contest_type 1 %}
|
{% ifequal item.contest_type 1 %}
|
||||||
<td>公开赛</td>
|
<td>公开赛</td>
|
||||||
@ -45,6 +45,9 @@
|
|||||||
{% ifequal item.contest_type 2 %}
|
{% ifequal item.contest_type 2 %}
|
||||||
<td>公开赛(密码保护)</td>
|
<td>公开赛(密码保护)</td>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
|
{% ifequal item.contest_type 3 %}
|
||||||
|
<td>小组邀请赛</td>
|
||||||
|
{% endifequal %}
|
||||||
|
|
||||||
<td class="{{ item|contest_status_color }}">{{ item|contest_status }}</td>
|
<td class="{{ item|contest_status_color }}">{{ item|contest_status }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -35,11 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button class="btn btn-primary" id="sendApplication">
|
<button class="btn btn-primary" id="sendApplication">{% if group.join_group_setting %}申请{% endif %}加入</button>
|
||||||
{% if group.join_group_setting %}
|
|
||||||
申请
|
|
||||||
{% endif %}
|
|
||||||
加入</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
无需申请
|
无需申请
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ item.admin }}</td>
|
<td>{{ item.created_by }}</td>
|
||||||
<td>{{ item.create_time }}</td>
|
<td>{{ item.create_time }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
Loading…
Reference in New Issue
Block a user