Accept Merge Request #281 后台部分重构,还未完成;部分代码修改为 web 组件;修复部分小 bug;更新个人主页样式 : (virusdefender-dev -> dev)

Merge Request: 后台部分重构,还未完成;部分代码修改为 web 组件;修复部分小 bug;更新个人主页样式
Created By: @virusdefender
Accepted By: @virusdefender
URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/281
This commit is contained in:
virusdefender 2015-10-26 10:47:24 +08:00
commit a176999750
25 changed files with 610 additions and 505 deletions

View File

@ -1,4 +1,5 @@
# coding=utf-8 # coding=utf-8
import urllib
import functools import functools
from functools import wraps from functools import wraps
@ -27,7 +28,7 @@ class BasePermissionDecorator(object):
if self.request.is_ajax(): if self.request.is_ajax():
return error_response(u"请先登录") return error_response(u"请先登录")
else: else:
return HttpResponseRedirect("/login/") return HttpResponseRedirect("/login/?__from=" + urllib.quote(self.request.build_absolute_uri()))
def check_permission(self): def check_permission(self):
raise NotImplementedError() raise NotImplementedError()

View File

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import jsonfield.fields
import account.models
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('account', '0012_auto_20151012_1546'),
]
operations = [
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('avatar', models.CharField(default=account.models._random_avatar, max_length=50)),
('blog', models.URLField(null=True, blank=True)),
('mood', models.CharField(max_length=200, null=True, blank=True)),
('hduoj_username', models.CharField(max_length=30, null=True, blank=True)),
('bestcoder_username', models.CharField(max_length=30, null=True, blank=True)),
('codeforces_username', models.CharField(max_length=30, null=True, blank=True)),
('rank', models.IntegerField(default=65535)),
('accepted_number', models.IntegerField(default=0)),
('submissions_number', models.IntegerField(default=0)),
('problems_status', jsonfield.fields.JSONField(default={})),
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_profile',
},
),
]

View File

@ -48,3 +48,26 @@ class User(AbstractBaseUser):
class Meta: class Meta:
db_table = "user" db_table = "user"
def _random_avatar():
import random
return "/static/img/avatar/avatar-" + str(random.randint(1, 20)) + ".png"
class UserProfile(models.Model):
user = models.OneToOneField(User)
avatar = models.CharField(max_length=50, default=_random_avatar)
blog = models.URLField(blank=True, null=True)
mood = models.CharField(max_length=200, blank=True, null=True)
hduoj_username = models.CharField(max_length=30, blank=True, null=True)
bestcoder_username = models.CharField(max_length=30, blank=True, null=True)
codeforces_username = models.CharField(max_length=30, blank=True, null=True)
rank = models.IntegerField(default=65535)
accepted_number = models.IntegerField(default=0)
submissions_number = models.IntegerField(default=0)
# JSON字典用来表示该用户的问题的解决状态 1为ac2为正在进行
problems_status = JSONField(default={})
class Meta:
db_table = "user_profile"

View File

@ -58,3 +58,7 @@ class ResetPasswordSerializer(serializers.Serializer):
token = serializers.CharField(min_length=1, max_length=40) token = serializers.CharField(min_length=1, max_length=40)
password = serializers.CharField(min_length=6, max_length=30) password = serializers.CharField(min_length=6, max_length=30)
captcha = serializers.CharField(max_length=4, min_length=4) captcha = serializers.CharField(max_length=4, min_length=4)
class SSOSerializer(serializers.Serializer):
token = serializers.CharField(max_length=40)

View File

@ -5,6 +5,7 @@ from django.contrib import auth
from django.shortcuts import render from django.shortcuts import render
from django.db.models import Q from django.db.models import Q
from django.conf import settings from django.conf import settings
from django.http import HttpResponseRedirect
from django.core.exceptions import MultipleObjectsReturned from django.core.exceptions import MultipleObjectsReturned
from django.utils.timezone import now from django.utils.timezone import now
@ -20,7 +21,8 @@ from .models import User
from .serializers import (UserLoginSerializer, UsernameCheckSerializer, from .serializers import (UserLoginSerializer, UsernameCheckSerializer,
UserRegisterSerializer, UserChangePasswordSerializer, UserRegisterSerializer, UserChangePasswordSerializer,
EmailCheckSerializer, UserSerializer, EditUserSerializer, EmailCheckSerializer, UserSerializer, EditUserSerializer,
ApplyResetPasswordSerializer, ResetPasswordSerializer) ApplyResetPasswordSerializer, ResetPasswordSerializer,
SSOSerializer)
from .decorators import super_admin_required from .decorators import super_admin_required
@ -284,15 +286,37 @@ class ResetPasswordAPIView(APIView):
def user_index_page(request, username): def user_index_page(request, username):
return render(request, "oj/account/user_index.html") try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return error_page(request, u"用户不存在")
blog_link = ""
if user.userprofile.blog:
blog_link = user.userprofile.blog.replace("http://", "").replace("https://", "")
return render(request, "oj/account/user_index.html", {"user": user, "blog_link": blog_link})
def auth_page(request): class SSOAPIView(APIView):
if not request.user.is_authenticated(): def post(self, request):
return render(request, "oj/account/oauth.html") serializer = SSOSerializer(data=request.data)
callback = request.GET.get("callback", None) if serializer.is_valid():
if not callback: try:
return error_page(request, u"参数错误") user = User.objects.get(auth_token=serializer.data["token"])
token = rand_str() return success_response({"username": user.username})
request.user.auth_token = token except User.DoesNotExist:
return render(request, "oj/account/oauth.html", {"callback": callback, "token": token}) return error_response(u"用户不存在")
else:
return serializer_invalid_response(serializer)
@login_required
def get(self, request):
callback = request.GET.get("callback", None)
if not callback or callback != settings.SSO["callback"]:
return error_page(request, u"参数错误")
token = rand_str()
request.user.auth_token = token
request.user.save()
return render(request, "oj/account/sso.html", {"redirect_url": callback + "?token=" + token, "callback": callback})

View File

@ -1,4 +1,5 @@
# coding=utf-8 # coding=utf-8
import urllib
from functools import wraps from functools import wraps
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
@ -30,7 +31,7 @@ def check_user_contest_permission(func):
if request.is_ajax(): if request.is_ajax():
return error_response(u"请先登录") return error_response(u"请先登录")
else: else:
return HttpResponseRedirect("/login/") return HttpResponseRedirect("/login/?__from=" + urllib.quote(request.build_absolute_uri()))
# kwargs 就包含了 url 里面的参数 # kwargs 就包含了 url 里面的参数
if "contest_id" in kwargs: if "contest_id" in kwargs:

View File

@ -11,10 +11,8 @@ from .models import Contest, ContestProblem
class CreateContestSerializer(serializers.Serializer): class CreateContestSerializer(serializers.Serializer):
title = serializers.CharField(max_length=40) title = serializers.CharField(max_length=40)
description = serializers.CharField(max_length=5000) description = serializers.CharField(max_length=5000)
mode = serializers.IntegerField()
contest_type = serializers.IntegerField() contest_type = serializers.IntegerField()
real_time_rank = serializers.BooleanField() real_time_rank = serializers.BooleanField()
show_user_submission = serializers.BooleanField()
password = serializers.CharField(max_length=30, required=False, default=None) password = serializers.CharField(max_length=30, required=False, default=None)
start_time = serializers.DateTimeField() start_time = serializers.DateTimeField()
end_time = serializers.DateTimeField() end_time = serializers.DateTimeField()
@ -45,10 +43,8 @@ class EditContestSerializer(serializers.Serializer):
id = serializers.IntegerField() id = serializers.IntegerField()
title = serializers.CharField(max_length=40) title = serializers.CharField(max_length=40)
description = serializers.CharField(max_length=10000) description = serializers.CharField(max_length=10000)
mode = serializers.IntegerField()
contest_type = serializers.IntegerField() contest_type = serializers.IntegerField()
real_time_rank = serializers.BooleanField() real_time_rank = serializers.BooleanField()
show_user_submission = serializers.BooleanField()
password = serializers.CharField(max_length=30, required=False, default=None) password = serializers.CharField(max_length=30, required=False, default=None)
start_time = serializers.DateTimeField() start_time = serializers.DateTimeField()
end_time = serializers.DateTimeField() end_time = serializers.DateTimeField()

View File

@ -1,9 +0,0 @@
# coding=utf-8
from django.conf.urls import include, url
from django.views.generic import TemplateView
urlpatterns = [
url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"),
]

View File

@ -64,9 +64,8 @@ class ContestAdminAPIView(APIView):
return error_response(u"比赛的开始时间必须早于比赛结束的时间") return error_response(u"比赛的开始时间必须早于比赛结束的时间")
try: try:
contest = Contest.objects.create(title=data["title"], description=data["description"], contest = Contest.objects.create(title=data["title"], description=data["description"],
mode=data["mode"], contest_type=data["contest_type"], contest_type=data["contest_type"],
real_time_rank=data["real_time_rank"], password=data["password"], real_time_rank=data["real_time_rank"], password=data["password"],
show_user_submission=data["show_user_submission"],
start_time=dateparse.parse_datetime(data["start_time"]), start_time=dateparse.parse_datetime(data["start_time"]),
end_time=dateparse.parse_datetime(data["end_time"]), end_time=dateparse.parse_datetime(data["end_time"]),
created_by=request.user, visible=data["visible"]) created_by=request.user, visible=data["visible"])
@ -125,10 +124,8 @@ class ContestAdminAPIView(APIView):
contest.title = data["title"] contest.title = data["title"]
contest.description = data["description"] contest.description = data["description"]
contest.mode = data["mode"]
contest.contest_type = data["contest_type"] contest.contest_type = data["contest_type"]
contest.real_time_rank = data["real_time_rank"] contest.real_time_rank = data["real_time_rank"]
contest.show_user_submission = data["show_user_submission"]
contest.start_time = dateparse.parse_datetime(data["start_time"]) contest.start_time = dateparse.parse_datetime(data["start_time"])
contest.end_time = dateparse.parse_datetime(data["end_time"]) contest.end_time = dateparse.parse_datetime(data["end_time"])
contest.visible = data["visible"] contest.visible = data["visible"]
@ -225,6 +222,7 @@ class ContestProblemAdminAPIView(APIView):
contest_problem.visible = data["visible"] contest_problem.visible = data["visible"]
contest_problem.sort_index = data["sort_index"] contest_problem.sort_index = data["sort_index"]
contest_problem.score = data["score"] contest_problem.score = data["score"]
contest_problem.last_update_time = now()
contest_problem.save() contest_problem.save()
return success_response(ContestProblemSerializer(contest_problem).data) return success_response(ContestProblemSerializer(contest_problem).data)
else: else:
@ -486,6 +484,10 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
if user_id: if user_id:
submissions = submissions.filter(user_id=request.GET.get("user_id")) submissions = submissions.filter(user_id=request.GET.get("user_id"))
problem_id = request.GET.get("problem_id", None)
if problem_id:
submissions = submissions.filter(problem_id=problem_id)
# 封榜的时候只能看到自己的提交 # 封榜的时候只能看到自己的提交
if not contest.real_time_rank: if not contest.real_time_rank:
if not (request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by): if not (request.user.admin_type == SUPER_ADMIN or request.user == contest.created_by):
@ -535,4 +537,4 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
return render(request, "oj/contest/submissions_list.html", return render(request, "oj/contest/submissions_list.html",
{"submissions": current_page, "page": int(page), {"submissions": current_page, "page": int(page),
"previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20, "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20,
"contest": contest, "filter": filter, "user_id": user_id}) "contest": contest, "filter": filter, "user_id": user_id, "problem_id": problem_id})

View File

@ -11,18 +11,13 @@ DATABASES = {
}, },
# submission 的 name 和 engine 请勿修改,其他代码会用到 # submission 的 name 和 engine 请勿修改,其他代码会用到
'submission': { 'submission': {
'NAME': 'oj_submission', 'ENGINE': 'django.db.backends.sqlite3',
'ENGINE': 'django.db.backends.mysql', 'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'),
'CONN_MAX_AGE': 0.1,
'HOST': "127.0.0.1",
'PORT': 3306,
'USER': 'root',
'PASSWORD': 'root',
} }
} }
REDIS_CACHE = { REDIS_CACHE = {
"host": "121.42.32.129", "host": "127.0.0.1",
"port": 6379, "port": 6379,
"db": 1 "db": 1
} }
@ -37,3 +32,5 @@ STATICFILES_DIRS = [os.path.join(BASE_DIR, "static/src/"), BASE_DIR]
# 模板文件夹 # 模板文件夹
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/src/')] TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/src/')]
SSO = {"callback": "http://localhost:8765/login"}

View File

@ -43,3 +43,5 @@ STATICFILES_DIRS = [os.path.join(BASE_DIR, "static/release/"), os.path.join(BASE
TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/release/')] TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/release/')]
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SSO = {"callback": "https://discuss.acmer.site/login"}

View File

@ -6,7 +6,7 @@ from django.views.generic import TemplateView
from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView,
UserChangePasswordAPIView, EmailCheckAPIView, UserChangePasswordAPIView, EmailCheckAPIView,
UserAdminAPIView, UserInfoAPIView, UserAdminAPIView, UserInfoAPIView,
ApplyResetPasswordAPIView) ApplyResetPasswordAPIView, SSOAPIView)
from announcement.views import AnnouncementAdminAPIView from announcement.views import AnnouncementAdminAPIView
@ -121,13 +121,13 @@ urlpatterns = [
url(r'^api/contest/time/$', ContestTimeAPIView.as_view(), name="contest_time_api_view"), url(r'^api/contest/time/$', ContestTimeAPIView.as_view(), name="contest_time_api_view"),
url(r'^api/admin/rejudge/$', SubmissionRejudgeAdminAPIView.as_view(), name="submission_rejudge_api"), url(r'^api/admin/rejudge/$', SubmissionRejudgeAdminAPIView.as_view(), name="submission_rejudge_api"),
url(r'^user/(?P<username>\w+)/$', "account.views.user_index_page"), url(r'^user/(?P<username>.+)/$', "account.views.user_index_page"),
url(r'^api/reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"), url(r'^api/reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"),
url(r'^account/settings/$', TemplateView.as_view(template_name="oj/account/settings.html"), name="account_setting_page"), url(r'^account/settings/$', TemplateView.as_view(template_name="oj/account/settings.html"), name="account_setting_page"),
url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"), url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"),
url(r'^account/auth/$', "account.views.auth_page", name="auth_login_page"), url(r'^account/sso/$', SSOAPIView.as_view(), name="sso_api"),
] ]

View File

@ -62,3 +62,32 @@ pre, code {
font-family: "source_code_pro"; font-family: "source_code_pro";
background-color: white; background-color: white;
} }
#index-avatar{
height: 200px;
width: 200px;
}
#user-mood{
max-width: 70%;
}
#oj-logo{
height: 20px;
}
.super-admin-star{
color: #ffd700;
}
.admin-star{
color: #c0c0c0;
}
#user-data-number{
font-size: 20px;
}
#user-data-text{
display: block;
}

View File

@ -1,5 +1,5 @@
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker", require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker",
"validator"], "validator", "editorComponent"],
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
$("#add-contest-form").validator().on('submit', function (e) { $("#add-contest-form").validator().on('submit', function (e) {
@ -7,11 +7,9 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
e.preventDefault(); e.preventDefault();
var ajaxData = { var ajaxData = {
title: vm.title, title: vm.title,
description: vm.description, description: avalon.vmodels.contestDescriptionEditor.content,
mode: vm.mode,
contest_type: 0, contest_type: 0,
real_time_rank: vm.realTimeRank, real_time_rank: vm.realTimeRank,
show_user_submission: vm.showSubmission,
start_time: vm.startTime, start_time: vm.startTime,
end_time: vm.endTime, end_time: vm.endTime,
visible: false visible: false
@ -38,12 +36,11 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
bsAlert("你没有选择参赛用户!"); bsAlert("你没有选择参赛用户!");
return false; return false;
} }
if (vm.editDescription == "") { if (ajaxData.description.trim() == "") {
bsAlert("比赛描述不能为空!"); bsAlert("比赛描述不能为空!");
return false; return false;
} }
$.ajax({ // Add contest $.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/contest/", url: "/api/admin/contest/",
dataType: "json", dataType: "json",
contentType: "application/json;charset=UTF-8", contentType: "application/json;charset=UTF-8",
@ -52,17 +49,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
bsAlert("添加成功!将转到比赛列表页以便为比赛添加问题(注意比赛当前状态为:隐藏)"); bsAlert("添加成功!将转到比赛列表页以便为比赛添加问题(注意比赛当前状态为:隐藏)");
vm.title = "";
vm.description = "";
vm.startTime = "";
vm.endTime = "";
vm.password = "";
vm.mode = "0";
vm.showSubmission = true;
location.hash = "#contest/contest_list"; location.hash = "#contest/contest_list";
vm.isGlobal = true;
vm.allGroups = [];
vm.showGlobalViewRadio = true;
} }
else { else {
bsAlert(data.data); bsAlert(data.data);
@ -73,23 +60,25 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
return false; return false;
}); });
editor("#editor"); //editor("#editor");
if (avalon.vmodels.add_contest) if (avalon.vmodels.add_contest)
var vm = avalon.vmodels.add_contest; var vm = avalon.vmodels.add_contest;
else else
var vm = avalon.define({ var vm = avalon.define({
$id: "add_contest", $id: "add_contest",
title: "", title: "",
description: "",
startTime: "", startTime: "",
endTime: "", endTime: "",
password: "", password: "",
mode: "0",
showSubmission: true,
isGlobal: true, isGlobal: true,
allGroups: [], allGroups: [],
showGlobalViewRadio: true, showGlobalViewRadio: true,
realTimeRank: true realTimeRank: true,
contestDescriptionEditor: {
editorId: "contest-description-editor",
placeholder: "比赛介绍内容"
}
}); });
$.ajax({ $.ajax({
@ -102,7 +91,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date
if (data.data.admin_type == 1) { if (data.data.admin_type == 1) {
vm.isGlobal = false; vm.isGlobal = false;
vm.showGlobalViewRadio = false; vm.showGlobalViewRadio = false;
} }
} }
$.ajax({ $.ajax({

View File

@ -1,277 +1,38 @@
require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", "validator"], function ($, avalon, csrfTokenHeader, bsAlert, editor) { require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", "validator", "pager"], function ($, avalon, csrfTokenHeader, bsAlert, editor) {
avalon.ready(function () { avalon.ready(function () {
$("#edit-contest-form").validator().on('submit', function (e) {
if (!e.isDefaultPrevented()) {
e.preventDefault();
var ajaxData = {
id: vm.contestList[vm.editingContestId - 1].id,
title: vm.editTitle,
description: vm.editDescription,
mode: vm.editMode,
contest_type: 0,
real_time_rank: vm.editRealTimeRank,
show_user_submission: vm.editShowSubmission,
start_time: vm.editStartTime,
end_time: vm.editEndTime,
visible: vm.editVisible
};
var selectedGroups = [];
if (!vm.isGlobal) {
for (var i = 0; i < vm.allGroups.length; i++) {
if (vm.allGroups[i].isSelected) {
selectedGroups.push(vm.allGroups[i].id);
}
}
ajaxData.groups = selectedGroups;
}
else {
if (vm.editPassword) {
ajaxData.password = vm.editPassword;
ajaxData.contest_type = 2;
}
else
ajaxData.contest_type = 1;
}
if (!vm.isGlobal && !selectedGroups.length) {
bsAlert("你没有选择参赛用户!");
return false;
}
if (vm.editDescription == "") {
bsAlert("比赛描述不能为空!");
return false;
}
$.ajax({ // modify contest info
beforeSend: csrfTokenHeader,
url: "/api/admin/contest/",
dataType: "json",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify(ajaxData),
method: "put",
success: function (data) {
if (!data.code) {
bsAlert("修改成功!");
vm.editingContestId = 0; // Hide the editor
vm.getPage(1); // Refresh the contest list
}
else {
bsAlert(data.data);
}
}
});
}
return false;
});
if (avalon.vmodels.contestList) { if (avalon.vmodels.contestList) {
// this page has been loaded before, so set the default value
var vm = avalon.vmodels.contestList; var vm = avalon.vmodels.contestList;
vm.contestList = []; vm.contestList = [];
vm.previousPage = 0;
vm.nextPage = 0;
vm.page = 1;
vm.totalPage = 1;
vm.keyword = "";
vm.editingContestId = 0;
vm.editTitle = "";
vm.editDescription = "";
vm.editProblemList = [];
vm.editPassword = "";
vm.editStartTime = "";
vm.editEndTime = "";
vm.editMode = "";
vm.editShowSubmission = false;
vm.editVisible = false;
vm.editingProblemContestIndex = 0;
vm.editRealTimeRank = true;
} }
else { else {
var vm = avalon.define({ var vm = avalon.define({
$id: "contestList", $id: "contestList",
contestList: [], contestList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
showVisibleOnly: false,
keyword: "", keyword: "",
editingContestId: 0, showVisibleOnly: false,
editTitle: "", pager: {
editDescription: "", getPage: function(page){
editProblemList: [], getPage(page);
editPassword: "",
editStartTime: "",
editEndTime: "",
editMode: "",
editShowSubmission: false,
editVisible: false,
editRealTimeRank: true,
editingProblemContestIndex: 0,
isGlobal: true,
allGroups: [],
showGlobalViewRadio: true,
admin_type: 1,
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) {
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
} }
}, },
getPage: function (page_index) {
getPageData(page_index);
},
showEditContestArea: function (contestId) {
if (vm.editingContestId && !confirm("如果继续将丢失未保存的信息,是否继续?"))
return;
if (contestId == vm.editingContestId)
vm.editingContestId = 0;
else {
vm.editingContestId = contestId;
vm.editTitle = vm.contestList[contestId - 1].title;
vm.editPassword = vm.contestList[contestId - 1].password;
vm.editStartTime = vm.contestList[contestId - 1].start_time.substring(0, 16).replace("T", " ");
vm.editEndTime = vm.contestList[contestId - 1].end_time.substring(0, 16).replace("T", " ");
vm.editMode = vm.contestList[contestId - 1].mode;
vm.editVisible = vm.contestList[contestId - 1].visible;
vm.editRealTimeRank = vm.contestList[contestId - 1].real_time_rank;
if (vm.contestList[contestId - 1].contest_type == 0) { //contest type == 0, contest in group
vm.isGlobal = false;
for (var i = 0; i < vm.allGroups.length; i++) {
vm.allGroups[i].isSelected = false;
}
for (var i = 0; i < vm.contestList[contestId - 1].groups.length; i++) {
var id = parseInt(vm.contestList[contestId - 1].groups[i]);
for (var index = 0; vm.allGroups[index]; index++) { search: function () {
if (vm.allGroups[index].id == id) { getPage(1);
vm.allGroups[index].isSelected = true; avalon.vmodels.contestListPager.currentPage = 1;
break;
}
}
}
}
else {
vm.isGlobal = true;
}
vm.editShowSubmission = vm.contestList[contestId - 1].show_user_submission;
editor("#editor").setValue(vm.contestList[contestId - 1].description);
vm.editingProblemContestIndex = 0;
}
},
showEditProblemArea: function (contestId) {
if (vm.editingProblemContestIndex == contestId) {
vm.editingProblemContestIndex = 0;
return;
}
if (vm.editingContestId && !confirm("如果继续将丢失未保存的信息,是否继续?")) {
return;
}
$.ajax({ // Get the problem list of current contest
beforeSend: csrfTokenHeader,
url: "/api/admin/contest_problem/?contest_id=" + vm.contestList[contestId - 1].id,
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
vm.editProblemList = data.data;
}
else {
bsAlert(data.data);
}
}
});
vm.editingContestId = 0;
vm.editingProblemContestIndex = contestId;
vm.editMode = vm.contestList[contestId - 1].mode;
},
addProblem: function () {
vm.$fire("up!showContestProblemPage", 0, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode);
},
showProblemEditPage: function (el) {
vm.$fire("up!showContestProblemPage", el.id, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode);
},
showSubmissionPage: function (el) {
var problemId = 0
if (el)
problemId = el.id;
vm.$fire("up!showContestSubmissionPage", problemId, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode);
},
addToProblemList: function (problem) {
var ajaxData = {
title: problem.title,
description: problem.description,
time_limit: problem.time_limit,
memory_limit: problem.memory_limit,
samples: [],
test_case_id: problem.test_case_id,
hint: problem.hint,
source: problem.contest.title,
visible: false,
tags: [],
input_description: problem.input_description,
output_description: problem.output_description,
difficulty: 0
};
for (var i = 0; i < problem.samples.length; i++) {
ajaxData.samples.push({input: problem.samples[i].input, output: problem.samples[i].output})
}
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/problem/",
dataType: "json",
data: JSON.stringify(ajaxData),
method: "post",
contentType: "application/json;charset=UTF-8",
success: function (data) {
if (!data.code) {
bsAlert("题目添加成功!题目现在处于隐藏状态,请到题目列表手动修改,并添加分类和难度信息!");
}
else {
bsAlert(data.data);
}
}
});
} }
});
vm.$watch("showVisibleOnly", function () {
getPageData(1);
}) })
} }
getPageData(1);
//init time picker vm.$watch("showVisibleOnly", function () {
$("#contest_start_time").datetimepicker({ getPage(1);
format: "yyyy-mm-dd hh:ii", avalon.vmodels.contestListPager.currentPage = 1;
minuteStep: 5,
weekStart: 1,
language: "zh-CN"
});
$("#contest_end_time").datetimepicker({
format: "yyyy-mm-dd hh:ii",
minuteStep: 5,
weekStart: 1,
language: "zh-CN"
}); });
function getPageData(page) { function getPage(page) {
var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=10"; var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=20";
if (vm.showVisibleOnly) if (vm.showVisibleOnly)
url += "&visible=true" url += "&visible=true";
if (vm.keyword != "") if (vm.keyword != "")
url += "&keyword=" + vm.keyword; url += "&keyword=" + vm.keyword;
$.ajax({ $.ajax({
@ -281,10 +42,8 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
vm.contestList = data.data.results; vm.contestList = data.data.results;
vm.totalPage = data.data.total_page; vm.announcementList = data.data.results;
vm.previousPage = data.data.previous_page; avalon.vmodels.contestListPager.totalPage = data.data.total_page;
vm.nextPage = data.data.next_page;
vm.page = page;
} }
else { else {
bsAlert(data.data); bsAlert(data.data);
@ -293,46 +52,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker",
}); });
} }
// Get group list
$.ajax({
url: "/api/user/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
var admin_type = data.data.admin_type;
vm.admin_type = admin_type;
if (data.data.admin_type == 1) {
vm.isGlobal = false;
vm.showGlobalViewRadio = false;
}
}
$.ajax({
url: "/api/admin/group/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (!data.data.length) {
if (admin_type != 2)
bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组");
return;
}
for (var i = 0; i < data.data.length; i++) {
var item = data.data[i];
item["isSelected"] = false;
vm.allGroups.push(item);
}
}
else {
bsAlert(data.data);
}
}
});
}
});
}); });
avalon.scan(); avalon.scan();
}); });

View File

@ -0,0 +1,321 @@
require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", "validator"], function ($, avalon, csrfTokenHeader, bsAlert, editor) {
avalon.ready(function () {
$("#edit-contest-form").validator().on('submit', function (e) {
if (!e.isDefaultPrevented()) {
e.preventDefault();
var ajaxData = {
id: vm.contestList[vm.editingContestId - 1].id,
title: vm.editTitle,
description: vm.editDescription,
mode: vm.editMode,
contest_type: 0,
real_time_rank: vm.editRealTimeRank,
show_user_submission: vm.editShowSubmission,
start_time: vm.editStartTime,
end_time: vm.editEndTime,
visible: vm.editVisible
};
var selectedGroups = [];
if (!vm.isGlobal) {
for (var i = 0; i < vm.allGroups.length; i++) {
if (vm.allGroups[i].isSelected) {
selectedGroups.push(vm.allGroups[i].id);
}
}
ajaxData.groups = selectedGroups;
}
else {
if (vm.editPassword) {
ajaxData.password = vm.editPassword;
ajaxData.contest_type = 2;
}
else
ajaxData.contest_type = 1;
}
if (!vm.isGlobal && !selectedGroups.length) {
bsAlert("你没有选择参赛用户!");
return false;
}
if (vm.editDescription == "") {
bsAlert("比赛描述不能为空!");
return false;
}
$.ajax({ // modify contest info
beforeSend: csrfTokenHeader,
url: "/api/admin/contest/",
dataType: "json",
contentType: "application/json;charset=UTF-8",
data: JSON.stringify(ajaxData),
method: "put",
success: function (data) {
if (!data.code) {
bsAlert("修改成功!");
vm.editingContestId = 0; // Hide the editor
vm.getPage(1); // Refresh the contest list
}
else {
bsAlert(data.data);
}
}
});
}
return false;
});
if (avalon.vmodels.contestList) {
// this page has been loaded before, so set the default value
var vm = avalon.vmodels.contestList;
vm.contestList = [];
}
else {
var vm = avalon.define({
$id: "contestList",
contestList: [],
previousPage: 0,
nextPage: 0,
page: 1,
totalPage: 1,
showVisibleOnly: false,
keyword: "",
editingContestId: 0,
editTitle: "",
editDescription: "",
editProblemList: [],
editPassword: "",
editStartTime: "",
editEndTime: "",
editMode: "",
editShowSubmission: false,
editVisible: false,
editRealTimeRank: true,
editingProblemContestIndex: 0,
isGlobal: true,
allGroups: [],
showGlobalViewRadio: true,
admin_type: 1,
getNext: function () {
if (!vm.nextPage)
return;
getPageData(vm.page + 1);
},
getPrevious: function () {
if (!vm.previousPage)
return;
getPageData(vm.page - 1);
},
getBtnClass: function (btn) {
if (btn == "next") {
return vm.nextPage ? "btn btn-primary" : "btn btn-primary disabled";
}
else {
return vm.previousPage ? "btn btn-primary" : "btn btn-primary disabled";
}
},
getPage: function (page_index) {
getPageData(page_index);
},
showEditContestArea: function (contestId) {
if (vm.editingContestId && !confirm("如果继续将丢失未保存的信息,是否继续?"))
return;
if (contestId == vm.editingContestId)
vm.editingContestId = 0;
else {
vm.editingContestId = contestId;
vm.editTitle = vm.contestList[contestId - 1].title;
vm.editPassword = vm.contestList[contestId - 1].password;
vm.editStartTime = vm.contestList[contestId - 1].start_time.substring(0, 16).replace("T", " ");
vm.editEndTime = vm.contestList[contestId - 1].end_time.substring(0, 16).replace("T", " ");
vm.editMode = vm.contestList[contestId - 1].mode;
vm.editVisible = vm.contestList[contestId - 1].visible;
vm.editRealTimeRank = vm.contestList[contestId - 1].real_time_rank;
if (vm.contestList[contestId - 1].contest_type == 0) { //contest type == 0, contest in group
vm.isGlobal = false;
for (var i = 0; i < vm.allGroups.length; i++) {
vm.allGroups[i].isSelected = false;
}
for (var i = 0; i < vm.contestList[contestId - 1].groups.length; i++) {
var id = parseInt(vm.contestList[contestId - 1].groups[i]);
for (var index = 0; vm.allGroups[index]; index++) {
if (vm.allGroups[index].id == id) {
vm.allGroups[index].isSelected = true;
break;
}
}
}
}
else {
vm.isGlobal = true;
}
vm.editShowSubmission = vm.contestList[contestId - 1].show_user_submission;
editor("#editor").setValue(vm.contestList[contestId - 1].description);
vm.editingProblemContestIndex = 0;
}
},
showEditProblemArea: function (contestId) {
if (vm.editingProblemContestIndex == contestId) {
vm.editingProblemContestIndex = 0;
return;
}
if (vm.editingContestId && !confirm("如果继续将丢失未保存的信息,是否继续?")) {
return;
}
$.ajax({ // Get the problem list of current contest
beforeSend: csrfTokenHeader,
url: "/api/admin/contest_problem/?contest_id=" + vm.contestList[contestId - 1].id,
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
vm.editProblemList = data.data;
}
else {
bsAlert(data.data);
}
}
});
vm.editingContestId = 0;
vm.editingProblemContestIndex = contestId;
vm.editMode = vm.contestList[contestId - 1].mode;
},
addProblem: function () {
vm.$fire("up!showContestProblemPage", 0, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode);
},
showProblemEditPage: function (el) {
vm.$fire("up!showContestProblemPage", el.id, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode);
},
showSubmissionPage: function (el) {
var problemId = 0
if (el)
problemId = el.id;
vm.$fire("up!showContestSubmissionPage", problemId, vm.contestList[vm.editingProblemContestIndex - 1].id, vm.editMode);
},
addToProblemList: function (problem) {
var ajaxData = {
title: problem.title,
description: problem.description,
time_limit: problem.time_limit,
memory_limit: problem.memory_limit,
samples: [],
test_case_id: problem.test_case_id,
hint: problem.hint,
source: problem.contest.title,
visible: false,
tags: [],
input_description: problem.input_description,
output_description: problem.output_description,
difficulty: 0
};
for (var i = 0; i < problem.samples.length; i++) {
ajaxData.samples.push({input: problem.samples[i].input, output: problem.samples[i].output})
}
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/admin/problem/",
dataType: "json",
data: JSON.stringify(ajaxData),
method: "post",
contentType: "application/json;charset=UTF-8",
success: function (data) {
if (!data.code) {
bsAlert("题目添加成功!题目现在处于隐藏状态,请到题目列表手动修改,并添加分类和难度信息!");
}
else {
bsAlert(data.data);
}
}
});
}
});
vm.$watch("showVisibleOnly", function () {
getPageData(1);
})
}
getPageData(1);
//init time picker
$("#contest_start_time").datetimepicker({
format: "yyyy-mm-dd hh:ii",
minuteStep: 5,
weekStart: 1,
language: "zh-CN"
});
$("#contest_end_time").datetimepicker({
format: "yyyy-mm-dd hh:ii",
minuteStep: 5,
weekStart: 1,
language: "zh-CN"
});
function getPageData(page) {
var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=10";
if (vm.showVisibleOnly)
url += "&visible=true"
if (vm.keyword != "")
url += "&keyword=" + vm.keyword;
$.ajax({
url: url,
dataType: "json",
method: "get",
success: function (data) {
if (!data.code) {
vm.contestList = data.data.results;
vm.totalPage = data.data.total_page;
vm.previousPage = data.data.previous_page;
vm.nextPage = data.data.next_page;
vm.page = page;
}
else {
bsAlert(data.data);
}
}
});
}
// Get group list
$.ajax({
url: "/api/user/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
var admin_type = data.data.admin_type;
vm.admin_type = admin_type;
if (data.data.admin_type == 1) {
vm.isGlobal = false;
vm.showGlobalViewRadio = false;
}
}
$.ajax({
url: "/api/admin/group/",
method: "get",
dataType: "json",
success: function (data) {
if (!data.code) {
if (!data.data.length) {
if (admin_type != 2)
bsAlert("您的用户权限只能创建小组内比赛,但是您还没有创建过小组");
return;
}
for (var i = 0; i < data.data.length; i++) {
var item = data.data[i];
item["isSelected"] = false;
vm.allGroups.push(item);
}
}
else {
bsAlert(data.data);
}
}
});
}
});
});
avalon.scan();
});

View File

@ -14,21 +14,18 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
method: "post", method: "post",
success: function (data) { success: function (data) {
if (!data.code) { if (!data.code) {
//成功登陆 function getLocationVal(id){
var ref = document.referrer; var temp = unescape(location.search).split(id+"=")[1] || "";
if (ref) { return temp.indexOf("&")>=0 ? temp.split("&")[0] : temp;
// 注册页和本页的来源的跳转回首页,防止死循环 }
if (ref.indexOf("register") > -1 || ref.indexOf("login") > -1) { var from = getLocationVal("__from");
location.href = "/"; if(from != ""){
return; console.log(from);
} window.location.href = from;
// 判断来源,只有同域下才跳转 }
if (ref.split("/")[2].split(":")[0] == location.hostname) { else{
location.href = ref; location.href = "/";
return;
}
} }
location.href = "/";
} }
else { else {
refresh_captcha(); refresh_captcha();

View File

@ -25,6 +25,7 @@
// ------ admin web 组件 ---------- // ------ admin web 组件 ----------
pager: "components/pager", pager: "components/pager",
editorComponent: "components/editorComponent",
// ------ 下面写的都不要直接用,而是使用上面的封装版本 ------ // ------ 下面写的都不要直接用,而是使用上面的封装版本 ------
//富文本编辑器simditor -> editor //富文本编辑器simditor -> editor
@ -153,7 +154,7 @@
}, },
{ {
name: "submissionList_21_pack" name: "submissionList_21_pack"
} },
], ],
optimizeCss: "standard", optimizeCss: "standard",
}) })

View File

@ -17,8 +17,7 @@
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
<div class="form-group"> <div class="form-group">
<textarea id="editor" placeholder="这里输入内容" autofocus ms-duplex="description"></textarea> <ms:editor $id="contestDescriptionEditor" config="contestDescriptionEditor"></ms:editor>
<p class="error-info" ms-visible="description==''">请填写比赛描述</p>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
@ -39,7 +38,7 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group"> <div class="form-group">
<label>可见范围</label> <label>比赛类型</label>
<div> <div>
<span ms-if="showGlobalViewRadio"> <span ms-if="showGlobalViewRadio">
@ -70,30 +69,12 @@
<input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected">&nbsp;&nbsp;{{ el.name }} <input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected">&nbsp;&nbsp;{{ el.name }}
</div> </div>
</div> </div>
<div class="col-md-3">
<label>排名方式</label> <div class="col-md-12">
<div class="form-group">
<label><input type="radio" name="mode" ms-duplex-string="mode" value="0">
<small>ACM</small>
</label>
<label><input type="radio" name="mode" ms-duplex-string="mode" value="1">
<small>分数</small>
</label>
</div>
</div>
<div class="col-md-3">
<label>公开提交记录</label>
<div class="form-group">
<label class="text"><input type="checkbox" ms-duplex-checked="showSubmission">
<small>公开</small>
</label>
</div>
</div>
<div class="col-md-3">
<label>实时排名</label> <label>实时排名</label>
<div class="form-group"> <div class="form-group">
<label class="text"><input type="checkbox" ms-duplex-checked="realTimeRank"> <label class="text"><input type="checkbox" ms-duplex-checked="realTimeRank">
<small></small> <small>如果不勾选,排名会被缓存,不会更新,而且只显示自己的提交。用于 acm 封榜。</small>
</label> </label>
</div> </div>
</div> </div>

View File

@ -6,7 +6,7 @@
<div class="form-group-sm"> <div class="form-group-sm">
<label>搜索</label> <label>搜索</label>
<input name="keyWord" class="form-control" placeholder="请输入关键词" ms-duplex="keyword"> <input name="keyWord" class="form-control" placeholder="请输入关键词" ms-duplex="keyword">
<input type="submit" value="搜索" class="btn btn-primary" ms-click="getPage(1)"> <input type="submit" value="搜索" class="btn btn-primary" ms-click="search()">
</div> </div>
</form> </form>
<br> <br>
@ -38,10 +38,10 @@
<label>仅显示可见 <input ms-duplex-checked="showVisibleOnly" type="checkbox"/></label> <label>仅显示可见 <input ms-duplex-checked="showVisibleOnly" type="checkbox"/></label>
</div> </div>
<div class="text-right"> <div class="text-right">
页数:{{ page }}/{{ totalPage }}&nbsp;&nbsp; <ms:pager $id="contestListPager" config="pager"></ms:pager>
<button ms-attr-class="getBtnClass('pre')" ms-click="getPrevious">上一页</button>
<button ms-attr-class="getBtnClass('next')" ms-click="getNext">下一页</button>
</div> </div>
<!--
<div ms-visible="editingContestId"> <div ms-visible="editingContestId">
<form id="edit-contest-form"> <form id="edit-contest-form">
<div class="col-md-12"> <div class="col-md-12">
@ -108,7 +108,7 @@
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="editPassword"> <input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="editPassword">
</div> </div>
<div class="form-group col-md-12" ms-visible="!isGlobal"> <div class="form-group col-md-12" ms-visible="!isGlobal">
<!-- radio 的value 没有用 但是没有的话,表单验证会出错-->
<div ms-repeat="allGroups" class="col-md-4"> <div ms-repeat="allGroups" class="col-md-4">
<input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected">&nbsp;&nbsp;{{ el.name }} <input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected">&nbsp;&nbsp;{{ el.name }}
</div> </div>
@ -186,6 +186,8 @@
</div> </div>
-->
</div> </div>
<script src="/static/js/app/admin/contest/contestList.js"></script> <script src="/static/js/app/admin/contest/contestList.js"></script>

View File

@ -1,24 +0,0 @@
{% extends "oj_base.html" %}
{% block title %}
授权登录
{% endblock %}
{% block body %}
<div class="container main">
<div class="text-center">
{% if request.user.is_authenticated %}
<p>3秒钟后将跳转到<span id="link">{{ callback }}</span></p>
<script>setTimeout(function(){
window.location.href = "{{ callback }}?token={{ token }}"},
3000);
</script>
{% else %}
<script>window.location.href = "/login/";</script>
{% endif %}
</div>
</div>
{% endblock %}
{% block js_block %}
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends "oj_base.html" %}
{% block title %}
授权登录
{% endblock %}
{% block body %}
<div class="container main">
<div class="text-center">
<p>3秒钟后将使用账号{{ request.user.username }}登录<span id="link">{{ callback }}</span></p>
<button class="btn btn-warning" onclick="location.href='/'">取消登录</button>
<button class="btn btn-success" onclick="location.href='/login/'">更换账号</button>
<script>setTimeout(function(){
window.location.href = "{{ redirect_url }}"},
3000);
</script>
</div>
</div>
{% endblock %}
{% block js_block %}
{% endblock %}

View File

@ -1,90 +1,83 @@
{% extends "oj_base.html" %} {% extends "oj_base.html" %}
{% block title %} {% block title %}
{{ user.username }}的主页
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<div class="container main"> <div class="container main">
<div class="col-lg-4"> <div class="col-lg-4">
<div class="avatar"> <div class="avatar">
<img src="https://coding.net/static/fruit_avatar/Fruit-1.png" class="img-responsive" <img src="{{ user.userprofile.avatar }}" class="img-responsive" id="index-avatar">
style="height: 200px;width: 200px;">
</div> </div>
<div> <div>
<h2>virusdefender</h2> <h2>
{{ user.username }}
{% ifequal user.admin_type 2 %}
<span class="glyphicon glyphicon-star super-admin-star" title="超级管理员"></span>
{% endifequal %}
{% ifequal user.admin_type 1 %}
<span class="glyphicon glyphicon-star admin-star"></span>
{% endifequal %}
</h2>
<p id="user-mood">{{ user.userprofile.mood }}</p>
</div> </div>
<div class="list-group col-lg-10"> <div class="list-group col-lg-9">
{% if user.userprofile.blog %}
<p class="list-group-item"><span class="glyphicon glyphicon-link"></span> <p class="list-group-item"><span class="glyphicon glyphicon-link"></span>
<a href="https://virusdefender.net">https://virusdefender.net</a> <a href="{{ user.userprofile.blog }}" target="_blank">{{ blog_link }}</a>
</p> </p>
{% endif %}
{% if user.userprofile.hduoj_username %}
<p class="list-group-item">
<img src="/static/img/oj_logo/hdu_logo.png" id="oj-logo">
<a href="http://bestcoder.hdu.edu.cn/rating.php?user={{ user.userprofile.hduoj_username }}" target="_blank">
{{ user.userprofile.hduoj_username }}
</a>
</p>
{% endif %}
{% if user.userprofile.bestcoder_username %}
<p class="list-group-item">
<img src="/static/img/oj_logo/bestcoder_logo.png" id="oj-logo">
<a href="http://bestcoder.hdu.edu.cn/rating.php?user={{ user.userprofile.bestcoder_username }}" target="_blank">
{{ user.userprofile.bestcoder_username }}
</a>
</p>
{% endif %}
{% if user.userprofile.codeforces_username %}
<p class="list-group-item">
<img src="/static/img/oj_logo/codeforces_logo.png" id="oj-logo">
<a href="http://codeforces.com/profile/{{ user.userprofile.codeforces_username }}" target="_blank">
{{ user.userprofile.codeforces_username }}
</a>
</p>
{% endif %}
<p class="list-group-item"> <p class="list-group-item">
<img src="/static/img/oj_logo/hdu_logo.png" style="height: 20px"> <span class="glyphicon glyphicon-calendar"></span>
<a href="https://virusdefender.net">https://virusdefender.net</a> {{ user.create_time }}
</p> </p>
<div class="rows">
<p class="list-group-item"> <div class="col-lg-4 text-center">
<img src="/static/img/oj_logo/bestcoder_logo.png" style="height: 20px"> <strong id="user-data-number">{{ user.userprofile.rank }}</strong>
<a href="https://virusdefender.net">https://virusdefender.net</a> <span id="user-data-text">Rank</span>
</p> </div>
<div class="col-lg-4 text-center">
<p class="list-group-item"> <strong id="user-data-number">{{ user.userprofile.accepted_number }}</strong>
<img src="/static/img/oj_logo/codeforces_logo.png" style="height: 20px"> <span id="user-data-text">AC</span>
<a href="https://virusdefender.net">https://virusdefender.net</a> </div>
</p> <div class="col-lg-4 text-center">
<strong id="user-data-number">{{ user.userprofile.submissions_number }}</strong>
<span id="user-data-text">Submissions</span>
<p class="list-group-item"><span class="glyphicon glyphicon-calendar"></span> 2015-9-10</p> </div>
</div>
</div> </div>
</div> </div>
<div class="col-lg-8"> <div class="col-lg-8">
<ul class="nav nav-tabs" style="margin: 10px;;">
<li role="presentation" class="active"><a href="#">Home</a></li>
<li role="presentation"><a href="#123">全部分享</a></li>
</ul>
<div class="col-lg-6">
<div class="panel panel-success">
<div class="panel-heading"><h3 class="panel-title">正在做的题</h3></div>
<div class="list-group">
<p class="list-group-item">
<a href="#" style="font-size: large;">problem title</a>
<span class="right">3 / 10</span>
<span style="display: block;">Accepted</span>
</p>
<p class="list-group-item">
<a href="#" style="font-size: large;">problem title</a>
<span class="right">3 / 10</span>
<span style="display: block;">Accepted</span>
</p>
<p class="list-group-item">
<a href="#" style="font-size: large;">problem title</a>
<span class="right">3 / 10</span>
<span style="display: block;">Accepted</span>
</p>
<p class="list-group-item">
<a href="#" style="font-size: large;">problem title</a>
<span class="right">3 / 10</span>
<span style="display: block;">Accepted</span>
</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">分享的代码</h3></div>
<div class="panel-body"> Panel content</div>
</div>
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/account/register.js"></script>
{% endblock %}

View File

@ -40,7 +40,7 @@
<th class="text-center">用时 + 罚时</th> <th class="text-center">用时 + 罚时</th>
{% for item in contest_problems %} {% for item in contest_problems %}
<th class="text-center"> <th class="text-center">
<a href="/contest/{{ contest.id }}/problem/{{ item.id }}/">{{ item.sort_index }}</a> <a href="/contest/{{ contest.id }}/submissions/?problem_id={{ item.id }}">{{ item.sort_index }}</a>
</th> </th>
{% endfor %} {% endfor %}
</tr> </tr>
@ -48,7 +48,7 @@
<tbody class="rank"> <tbody class="rank">
{% for item in rank %} {% for item in rank %}
<tr> <tr>
<th scope="row">{{ forloop.counter|add:paging_info.offset}}</th> <th scope="row">{% if item.total_ac_number %}{{ forloop.counter|add:paging_info.offset}}{% else %}-{% endif %}</th>
<td> <td>
<a href="/contest/{{ contest.id }}/submissions/?user_id={{ item.user__id }}"> <a href="/contest/{{ contest.id }}/submissions/?user_id={{ item.user__id }}">
{{ item.user__username }} {{ item.user__username }}

View File

@ -107,14 +107,14 @@
<ul class="pager"> <ul class="pager">
{% if previous_page %} {% if previous_page %}
<li class="previous"> <li class="previous">
<a href="/contest/{{ contest.id }}/submissions/{{ previous_page }}/{% if filter %}?{{ filter.name }}={{ filter.content }}{% if user_id %}&user_id={{ user_id }}{% endif %}{% else %}{% if user_id %}?user_id={{ user_id }}{% endif %}{% endif %}"> <a href="/contest/{{ contest.id }}/submissions/{{ previous_page }}/?{% if filter %}{{ filter.name }}={{ filter.content }}&{% endif %}{% if user_id %}user_id={{ user_id }}&{% endif %}{% if problem_id %}problem_id={{ problem_id }}&{% endif %}">
<span aria-hidden="true">&larr;</span> 上一页 <span aria-hidden="true">&larr;</span> 上一页
</a> </a>
</li> </li>
{% endif %} {% endif %}
{% if next_page %} {% if next_page %}
<li class="next"> <li class="next">
<a href="/contest/{{ contest.id }}/submissions/{{ next_page }}/{% if filter %}?{{ filter.name }}={{ filter.content }}{% if user_id %}&user_id={{ user_id }}{% endif %}{% else %}{% if user_id %}?user_id={{ user_id }}{% endif %}{% endif %}"> <a href="/contest/{{ contest.id }}/submissions/{{ next_page }}/?{% if filter %}{{ filter.name }}={{ filter.content }}&{% endif %}{% if user_id %}user_id={{ user_id }}&{% endif %}{% if problem_id %}problem_id={{ problem_id }}&{% endif %}">
下一页 <span aria-hidden="true">&rarr;</span> 下一页 <span aria-hidden="true">&rarr;</span>
</a> </a>
</li> </li>