Accept Merge Request #298 好像好久没有合并过了。。。 : (dev-s -> dev)

Merge Request: 好像好久没有合并过了。。。
Created By: @esp
Accepted By: @virusdefender
URL: https://coding.net/u/virusdefender/p/qduoj/git/merge/298
This commit is contained in:
virusdefender
2015-11-30 18:32:31 +08:00
45 changed files with 804 additions and 547 deletions

View File

@@ -8,9 +8,10 @@ demo: https://qduoj.com
TODO
- 将判题服务器改为 rpc 通信
- 重构消息队列
- 完善测试
- 完善小组功能
- 后台重构
![oj_previewindex.png][1]

24
account/middleware.py Normal file
View File

@@ -0,0 +1,24 @@
# coding=utf-8
import time
import json
import urllib
from django.http import HttpResponseRedirect, HttpResponse
from django.contrib import auth
from utils.shortcuts import error_response, error_page
from .models import ADMIN
class SessionSecurityMiddleware(object):
def process_request(self, request):
if request.user.is_authenticated() and request.user.admin_type >= ADMIN:
if "last_activity" in request.session:
# 24个小时没有活动
if time.time() - request.session["last_activity"] >= 24 * 60 * 60:
auth.logout(request)
if request.is_ajax():
return HttpResponse(json.dumps({"code": 1, "data": u"请先登录"}),
content_type="application/json")
else:
return HttpResponseRedirect("/login/?__from=" + urllib.quote(request.build_absolute_uri()))
# 更新最后活动日期
request.session["last_activity"] = time.time()

View File

@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0013_userprofile'),
]
operations = [
migrations.AddField(
model_name='userprofile',
name='phone_number',
field=models.CharField(max_length=15, null=True, blank=True),
),
migrations.AddField(
model_name='userprofile',
name='school',
field=models.CharField(max_length=200, null=True, blank=True),
),
]

View File

@@ -68,6 +68,9 @@ class UserProfile(models.Model):
submissions_number = models.IntegerField(default=0)
# JSON字典用来表示该用户的问题的解决状态 1为ac2为正在进行
problems_status = JSONField(default={})
phone_number = models.CharField(max_length=15, blank=True, null=True)
school = models.CharField(max_length=200, blank=True, null=True)
class Meta:
db_table = "user_profile"

View File

@@ -1,7 +1,7 @@
# coding=utf-8
from rest_framework import serializers
from .models import User
from .models import User, UserProfile
class UserLoginSerializer(serializers.Serializer):
@@ -21,6 +21,7 @@ class EmailCheckSerializer(serializers.Serializer):
class UserRegisterSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30)
real_name = serializers.CharField(max_length=30)
school = serializers.CharField(max_length=200)
password = serializers.CharField(max_length=30, min_length=6)
email = serializers.EmailField(max_length=254)
captcha = serializers.CharField(max_length=4, min_length=4)
@@ -49,7 +50,6 @@ class EditUserSerializer(serializers.Serializer):
class ApplyResetPasswordSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30)
email = serializers.EmailField()
captcha = serializers.CharField(max_length=4, min_length=4)
@@ -62,3 +62,23 @@ class ResetPasswordSerializer(serializers.Serializer):
class SSOSerializer(serializers.Serializer):
token = serializers.CharField(max_length=40)
class EditUserProfileSerializer(serializers.Serializer):
avatar = serializers.CharField(max_length=50, required=False, default=None)
blog = serializers.URLField(required=False, allow_blank=True, default='')
mood = serializers.CharField(max_length=60, required=False, default='')
hduoj_username = serializers.CharField(max_length=30, required=False, allow_blank=True, default='')
bestcoder_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='')
phone_number = serializers.CharField(max_length=15, required=False, allow_blank=True, default='')
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ["avatar", "blog", "mood", "hduoj_username", "bestcoder_username", "codeforces_username",
"rank", "accepted_number", "submissions_number", "problems_status", "phone_number", "school"]

View File

@@ -17,12 +17,14 @@ from utils.captcha import Captcha
from mail.tasks import send_email
from .decorators import login_required
from .models import User
from .serializers import (UserLoginSerializer, UsernameCheckSerializer,
UserRegisterSerializer, UserChangePasswordSerializer,
EmailCheckSerializer, UserSerializer, EditUserSerializer,
from .models import User, UserProfile
from .serializers import (UserLoginSerializer, UserRegisterSerializer,
UserChangePasswordSerializer,
UserSerializer, EditUserSerializer,
ApplyResetPasswordSerializer, ResetPasswordSerializer,
SSOSerializer)
SSOSerializer, EditUserProfileSerializer, UserProfileSerializer)
from .decorators import super_admin_required
@@ -60,10 +62,9 @@ def index_page(request):
if not request.user.is_authenticated():
return render(request, "oj/index.html")
try:
if request.META['HTTP_REFERER']:
if request.META.get('HTTP_REFERER') or request.GET.get("index"):
return render(request, "oj/index.html")
except KeyError:
else:
return http.HttpResponseRedirect('/problems/')
@@ -96,6 +97,7 @@ class UserRegisterAPIView(APIView):
email=data["email"])
user.set_password(data["password"])
user.save()
UserProfile.objects.create(user=user, school=data["school"])
return success_response(u"注册成功!")
else:
return serializer_invalid_response(serializer)
@@ -146,17 +148,27 @@ class UsernameCheckAPIView(APIView):
class EmailCheckAPIView(APIView):
def get(self, request):
"""
检测邮箱是否存在,存在返回状态码400不存在返回200
检测邮箱是否存在,用状态码标识结果
---
"""
#这里是为了适应前端表单验证空间的要求
reset = request.GET.get("reset", None)
#如果reset为true说明该请求是重置密码页面发出的要返回的状态码应正好相反
if reset:
existed = 200
does_not_existed = 400
else:
existed = 400
does_not_existed = 200
email = request.GET.get("email", None)
if email:
try:
User.objects.get(email=email)
return Response(status=400)
return Response(status=existed)
except Exception:
return Response(status=200)
return Response(status=200)
return Response(status=does_not_existed)
return Response(status=does_not_existed)
class UserAdminAPIView(APIView):
@@ -225,6 +237,38 @@ class UserInfoAPIView(APIView):
return success_response(UserSerializer(request.user).data)
class UserProfileAPIView(APIView):
@login_required
def get(self, request):
"""
返回这个用户的个人信息
---
response_serializer: UserSerializer
"""
return success_response(UserSerializer(request.user).data)
@login_required
def put(self, request):
serializer = EditUserProfileSerializer(data=request.data)
if serializer.is_valid():
data = serializer.data
user_profile = request.user.userprofile
if data["avatar"]:
user_profile.avatar = data["avatar"]
else:
user_profile.mood = data["mood"]
user_profile.hduoj_username = data["hduoj_username"]
user_profile.bestcoder_username = data["bestcoder_username"]
user_profile.codeforces_username = data["codeforces_username"]
user_profile.blog = data["blog"]
user_profile.school = data["school"]
user_profile.phone_number = data["phone_number"]
user_profile.save()
return success_response(u"修改成功")
else:
return serializer_invalid_response(serializer)
class ApplyResetPasswordAPIView(APIView):
def post(self, request):
"""
@@ -239,7 +283,7 @@ class ApplyResetPasswordAPIView(APIView):
if not captcha.check(data["captcha"]):
return error_response(u"验证码错误")
try:
user = User.objects.get(username=data["username"], email=data["email"])
user = User.objects.get(email=data["email"])
except User.DoesNotExist:
return error_response(u"用户不存在")
if user.reset_password_token_create_time and (now() - user.reset_password_token_create_time).total_seconds() < 20 * 60:
@@ -251,14 +295,14 @@ class ApplyResetPasswordAPIView(APIView):
email_template = email_template.replace("{{ username }}", user.username).\
replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]).\
replace("{{ link }}", request.scheme + "://" + request.META['HTTP_HOST'] + "/reset_password/?token=" + user.reset_password_token)
replace("{{ link }}", request.scheme + "://" + request.META['HTTP_HOST'] + "/reset_password/t/" + user.reset_password_token)
send_email(settings.WEBSITE_INFO["website_name"],
user.email,
user.username,
settings.WEBSITE_INFO["website_name"] + u" 密码找回邮件",
settings.WEBSITE_INFO["website_name"] + u" 登录信息找回邮件",
email_template)
return success_response(u"邮件发送成功")
return success_response(u"邮件发送成功,请前往您的邮箱查收")
else:
return serializer_invalid_response(serializer)
@@ -320,3 +364,13 @@ class SSOAPIView(APIView):
request.user.auth_token = token
request.user.save()
return render(request, "oj/account/sso.html", {"redirect_url": callback + "?token=" + token, "callback": callback})
def reset_password_page(request, token):
try:
user = User.objects.get(reset_password_token=token)
except User.DoesNotExist:
return error_page(request, u"链接已失效")
if (now() - user.reset_password_token_create_time).total_seconds() > 30 * 60:
return error_page(request, u"链接已过期")
return render(request, "oj/account/reset_password.html", {"user": user})

View File

@@ -4,6 +4,7 @@ from functools import wraps
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.core.urlresolvers import reverse
from utils.shortcuts import error_response, error_page
@@ -92,7 +93,7 @@ def check_user_contest_permission(func):
{"reason": "contest_not_start", "show_tab": False, "contest": contest})
# 比赛已经结束了,只拦截 ajax 的答案提交
if contest.status == CONTEST_ENDED and request.is_ajax():
if contest.status == CONTEST_ENDED and request.path == reverse("contest_submission_api") and request.is_ajax():
return error_response(u"比赛已经结束")
return func(*args, **kwargs)

View File

@@ -278,6 +278,8 @@ class MakeContestProblemPublicAPIView(APIView):
problem_id = request.data.get("problem_id", -1)
try:
problem = ContestProblem.objects.get(id=problem_id)
problem.is_public = True
problem.save()
except ContestProblem.DoesNotExist:
return error_response(u"比赛不存在")
Problem.objects.create(title=problem.title, description=problem.description,
@@ -288,10 +290,11 @@ class MakeContestProblemPublicAPIView(APIView):
hint=problem.hint, created_by=problem.created_by,
time_limit=problem.time_limit, memory_limit=problem.memory_limit,
visible=False, difficulty=-1, source=problem.contest.title)
problem.is_public = True
problem.save()
return success_response(u"创建成功")
class ContestPasswordVerifyAPIView(APIView):
@login_required
def post(self, request):
@@ -374,7 +377,7 @@ def contest_problems_list_page(request, contest_id):
比赛所有题目的列表页
"""
contest = Contest.objects.get(id=contest_id)
contest_problems = ContestProblem.objects.filter(contest=contest).select_related("contest").order_by("sort_index")
contest_problems = ContestProblem.objects.filter(contest=contest, visible=True).select_related("contest").order_by("sort_index")
return render(request, "oj/contest/contest_problems_list.html", {"contest_problems": contest_problems,
"contest": {"id": contest_id}})
@@ -423,7 +426,7 @@ def contest_list_page(request, page=1):
@check_user_contest_permission
def contest_rank_page(request, contest_id):
contest = Contest.objects.get(id=contest_id)
contest_problems = ContestProblem.objects.filter(contest=contest).order_by("sort_index")
contest_problems = ContestProblem.objects.filter(contest=contest, visible=True).order_by("sort_index")
r = get_cache_redis()
cache_key = str(contest_id) + "_rank_cache"
@@ -466,6 +469,7 @@ class ContestTimeAPIView(APIView):
"""
获取比赛开始或者结束的倒计时,返回毫秒数字
"""
def get(self, request):
contest_id = request.GET.get("contest_id", -1)
try:
@@ -511,6 +515,10 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
values("id", "contest_id", "problem_id", "result", "create_time",
"accepted_answer_time", "language", "user_id").order_by("-create_time")
# 如果比赛已经开始,就不再显示之前测试题目的提交
if contest.status != CONTEST_NOT_START:
submissions = submissions.filter(create_time__gte=contest.start_time)
user_id = request.GET.get("user_id", None)
if user_id:
submissions = submissions.filter(user_id=request.GET.get("user_id"))

View File

@@ -71,7 +71,8 @@ MIDDLEWARE_CLASSES = (
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'admin.middleware.AdminRequiredMiddleware'
'admin.middleware.AdminRequiredMiddleware',
'account.middleware.SessionSecurityMiddleware'
)
ROOT_URLCONF = 'oj.urls'

View File

@@ -5,8 +5,8 @@ from django.views.generic import TemplateView
from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView,
UserChangePasswordAPIView, EmailCheckAPIView,
UserAdminAPIView, UserInfoAPIView,
ApplyResetPasswordAPIView, SSOAPIView)
UserAdminAPIView, UserInfoAPIView, ResetPasswordAPIView,
ApplyResetPasswordAPIView, SSOAPIView, UserProfileAPIView)
from announcement.views import AnnouncementAdminAPIView
@@ -122,11 +122,14 @@ urlpatterns = [
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/apply_reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"),
url(r'^api/reset_password/$', ResetPasswordAPIView.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/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"),
url(r'^account/sso/$', SSOAPIView.as_view(), name="sso_api"),
url(r'^api/account/userprofile/$', UserProfileAPIView.as_view(), name="userprofile_api"),
url(r'^reset_password/$', TemplateView.as_view(template_name="oj/account/apply_reset_password.html"), name="apply_reset_password_page"),
url(r'^reset_password/t/(?P<token>\w+)/$', "account.views.reset_password_page", name="reset_password_page")
]

View File

@@ -40,6 +40,7 @@ class ProblemTagAdminAPIView(APIView):
"""
获取所有标签的列表
"""
def get(self, request):
return success_response(ProblemTagSerializer(ProblemTag.objects.all(), many=True).data)
@@ -189,33 +190,23 @@ class TestCaseUploadAPIView(APIView):
return error_response(u"解压失败")
name_list = test_case_file.namelist()
l = []
# 如果文件是直接打包的那么name_list 就是["1.in", "1.out"]这样的
if len(name_list) == 0:
return error_response(u"压缩包内没有文件")
if "1.in" in name_list and "1.out" in name_list:
for file_name in name_list:
if self._is_legal_test_case_file_name(file_name):
name = file_name.split(".")
# 有了.in 判断对应的.out 在不在
if name[1] == "in":
if (name[0] + ".out") in name_list:
l.append(file_name)
else:
return error_response(u"测试用例文件不完整,缺少" + name[0] + ".out")
else:
# 有了.out 判断对应的 .in 在不在
if (name[0] + ".in") in name_list:
l.append(file_name)
else:
return error_response(u"测试用例文件不完整,缺少" + name[0] + ".in")
if len(name_list) % 2 == 1:
return error_response(u"测试用例文件格式错误,文件数目为奇数")
for index in range(1, len(name_list) / 2 + 1):
if not (str(index) + ".in" in name_list and str(index) + ".out" in name_list):
return error_response(u"测试用例文件格式错误,缺少" + str(index) + u".in/.out文件")
problem_test_dir = rand_str()
test_case_dir = settings.TEST_CASE_DIR + problem_test_dir + "/"
# 得到了合法的测试用例文件列表 然后去解压缩
os.mkdir(test_case_dir)
for name in l:
for name in name_list:
f = open(test_case_dir + name, "wb")
try:
f.write(test_case_file.read(name).replace("\r\n", "\n"))
@@ -223,15 +214,15 @@ class TestCaseUploadAPIView(APIView):
return error_response(u"单个测试数据体积过大!")
finally:
f.close()
l.sort()
name_list.sort()
file_info = {"test_case_number": len(l) / 2, "test_cases": {}}
file_info = {"test_case_number": len(name_list) / 2, "test_cases": {}}
# 计算输出文件的md5
for i in range(len(l) / 2):
for i in range(1, len(name_list) / 2 + 1):
md5 = hashlib.md5()
striped_md5 = hashlib.md5()
f = open(test_case_dir + str(i + 1) + ".out", "r")
f = open(test_case_dir + str(i) + ".out", "r")
# 完整文件的md5
while True:
data = f.read(2 ** 8)
@@ -244,20 +235,18 @@ class TestCaseUploadAPIView(APIView):
f.seek(0)
striped_md5.update(f.read().rstrip())
file_info["test_cases"][str(i + 1)] = {"input_name": str(i + 1) + ".in",
"output_name": str(i + 1) + ".out",
file_info["test_cases"][str(i)] = {"input_name": str(i) + ".in",
"output_name": str(i) + ".out",
"output_md5": md5.hexdigest(),
"striped_output_md5": striped_md5.hexdigest(),
"input_size": os.path.getsize(test_case_dir + str(i + 1) + ".in"),
"output_size": os.path.getsize(test_case_dir + str(i + 1) + ".out")}
"input_size": os.path.getsize(test_case_dir + str(i) + ".in"),
"output_size": os.path.getsize(test_case_dir + str(i) + ".out")}
# 写入配置文件
with open(test_case_dir + "info", "w") as f:
f.write(json.dumps(file_info))
return success_response({"test_case_id": problem_test_dir,
"file_list": file_info["test_cases"]})
else:
return error_response(u"测试用例压缩文件格式错误,请保证测试用例文件在根目录下直接压缩")
def get(self, request):
test_case_id = request.GET.get("test_case_id", None)
@@ -273,7 +262,6 @@ class TestCaseUploadAPIView(APIView):
return success_response({"file_list": config["test_cases"]})
def problem_list_page(request, page=1):
"""
前台的问题列表
@@ -306,7 +294,7 @@ def problem_list_page(request, page=1):
return error_page(request, u"标签不存在")
problems = tag.problem_set.all().filter(visible=True)
paginator = Paginator(problems, 20)
paginator = Paginator(problems, 40)
try:
current_page = paginator.page(int(page))
except Exception:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -1,4 +1,5 @@
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "editorComponent"],
require(["jquery", "avalon", "editor", "uploader", "bsAlert",
"csrfToken", "tagEditor", "validator", "editorComponent", "testCaseUploader"],
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
avalon.ready(function () {
@@ -7,7 +8,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
.on('submit', function (e) {
if (!e.isDefaultPrevented()) {
e.preventDefault();
if (vm.testCaseId == "") {
if (!avalon.vmodels.testCaseUploader.uploaded) {
bsAlert("你还没有上传测试数据!");
return false;
}
@@ -35,7 +36,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
time_limit: vm.timeLimit,
memory_limit: vm.memoryLimit,
samples: [],
test_case_id: vm.testCaseId,
test_case_id: avalon.vmodels.testCaseUploader.testCaseId,
hint: avalon.vmodels.contestProblemHintEditor.content,
visible: vm.visible,
contest_id: avalon.vmodels.admin.contestId,
@@ -71,6 +72,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
success: function (data) {
if (!data.code) {
bsAlert(alertContent);
avalon.vmodels.admin.template_url = "template/contest/problem_list.html";
}
else {
bsAlert(data.data);
@@ -97,7 +99,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
outputDescription: "",
testCaseId: "",
testCaseList: [],
uploadSuccess: false,
contestProblemDescriptionEditor: {
editorId: "contest-problem-description-editor",
@@ -134,37 +135,19 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
});
else {
var vm = avalon.vmodels.editProblem;
title = "";
description = "";
timeLimit = 1000;
memoryLimit = 128;
samples = [];
hint = "";
sortIndex = "";
visible = true;
inputDescription = "";
outputDescription = "";
testCaseId = "";
testCaseList = [];
uploadSuccess = false;
}
var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/", function (file, response) {
if (response.code)
bsAlert(response.data);
else {
vm.testCaseId = response.data.test_case_id;
vm.title = "";
vm.description = "";
vm.timeLimit = 1000;
vm.memoryLimit = 128;
vm.samples = [];
vm.hint = "";
vm.sortIndex = "";
vm.visible = true;
vm.inputDescription = "";
vm.outputDescription = "";
vm.testCaseId = "";
vm.testCaseList = [];
for (var key in response.data.file_list) {
vm.testCaseList.push({
input: response.data.file_list[key].input_name,
output: response.data.file_list[key].output_name
})
}
vm.uploadSuccess = true;
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
}
});
if (avalon.vmodels.admin.contestProblemStatus == "edit") {
$.ajax({
@@ -188,7 +171,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
vm.inputDescription = problem.input_description;
vm.outputDescription = problem.output_description;
vm.score = problem.score;
vm.testCaseId = problem.test_case_id;
avalon.vmodels.testCaseUploader.setTestCase(problem.test_case_id);
vm.samples = [];
for (var i = 0; i < problem.samples.length; i++) {
vm.samples.push({
@@ -198,26 +181,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
})
}
avalon.vmodels.contestProblemHintEditor.content = problem.hint;
$.ajax({
url: "/api/admin/test_case_upload/?test_case_id=" + vm.testCaseId,
method: "get",
dataType: "json",
success: function (response) {
if (response.code) {
bsAlert(response.data);
}
else {
vm.testCaseList = [];
for (var key in response.data.file_list) {
vm.testCaseList.push({
input: response.data.file_list[key].input_name,
output: response.data.file_list[key].output_name
})
}
vm.uploadSuccess = true;
}
}
})
}
}
});

View File

@@ -1,11 +1,12 @@
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI", "editorComponent"],
require(["jquery", "avalon", "editor", "uploader", "bsAlert",
"csrfToken", "tagEditor", "validator", "jqueryUI", "editorComponent", "testCaseUploader"],
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
avalon.ready(function () {
$("#add-problem-form").validator()
.on('submit', function (e) {
if (!e.isDefaultPrevented()) {
if (vm.testCaseId == "") {
if (!avalon.vmodels.testCaseUploader.uploaded) {
bsAlert("你还没有上传测试数据!");
return false;
}
@@ -39,7 +40,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
time_limit: vm.timeLimit,
memory_limit: vm.memoryLimit,
samples: [],
test_case_id: vm.testCaseId,
test_case_id: avalon.vmodels.testCaseUploader.testCaseId,
hint: avalon.vmodels.problemHintEditor.content,
source: vm.source,
visible: vm.visible,
@@ -139,29 +140,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
}
});
var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/",
function (file, response) {
if (response.code) {
vm.uploadProgress = 0;
bsAlert(response.data);
}
else {
vm.testCaseId = response.data.test_case_id;
vm.uploadSuccess = true;
vm.testCaseList = [];
for (var key in response.data.file_list) {
vm.testCaseList.push({
input: response.data.file_list[key].input_name,
output: response.data.file_list[key].output_name
})
}
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
}
},
function (file, percentage) {
vm.uploadProgress = parseInt(percentage * 100);
});
var tagAutoCompleteList = [];
$.ajax({

View File

@@ -1,4 +1,5 @@
require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagEditor", "validator", "jqueryUI", "editorComponent"],
require(["jquery", "avalon", "editor", "uploader", "bsAlert",
"csrfToken", "tagEditor", "validator", "jqueryUI", "editorComponent", "testCaseUploader"],
function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) {
avalon.ready(function () {
@@ -6,7 +7,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
$("#edit-problem-form").validator()
.on('submit', function (e) {
if (!e.isDefaultPrevented()) {
if (vm.testCaseId == "") {
if (!avalon.vmodels.testCaseUploader.uploaded) {
bsAlert("你还没有上传测试数据!");
return false;
}
@@ -40,7 +41,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
time_limit: vm.timeLimit,
memory_limit: vm.memoryLimit,
samples: [],
test_case_id: vm.testCaseId,
test_case_id: avalon.vmodels.testCaseUploader.testCaseId,
hint: avalon.vmodels.problemHintEditor.content,
source: vm.source,
visible: vm.visible,
@@ -127,29 +128,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
avalon.vmodels.admin.template_url = "template/problem/problem_list.html";
}
});
var testCaseUploader = uploader("#testCaseFile", "/api/admin/test_case_upload/",
function (file, response) {
if (response.code) {
vm.uploadProgress = 0;
bsAlert(response.data);
}
else {
vm.testCaseId = response.data.test_case_id;
vm.uploadSuccess = true;
vm.testCaseList = [];
for(var key in response.data.file_list){
vm.testCaseList.push({
input: response.data.file_list[key].input_name,
output: response.data.file_list[key].output_name
})
}
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
}
},
function (file, percentage) {
vm.uploadProgress = parseInt(percentage * 100);
}
);
$.ajax({
url: "/api/admin/problem/?problem_id=" + avalon.vmodels.admin.problemId,
@@ -178,7 +156,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE
vm.difficulty = problem.difficulty;
vm.inputDescription = problem.input_description;
vm.outputDescription = problem.output_description;
vm.testCaseId = problem.test_case_id;
avalon.vmodels.testCaseUploader.setTestCase(problem.test_case_id);
vm.source = problem.source;
var problemTags = problem.tags;
$.ajax({

View File

@@ -0,0 +1,39 @@
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
var applied_captcha = false;
$('form').validator().on('submit', function (e) {
if (!e.isDefaultPrevented()) {
var email = $("#email").val();
var captcha = $("#captcha").val();
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/apply_reset_password/",
data: {email: email, captcha: captcha},
dataType: "json",
method: "post",
success: function (data) {
if (!data.code) {
refresh_captcha();
bsAlert(data.data);
}
else {
refresh_captcha();
bsAlert(data.data);
}
},
error: function(){
bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。")
}
});
return false;
}
});
function refresh_captcha(){
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
$("#captcha")[0].value = "";
}
$("#captcha-img").click(function(){
refresh_captcha();
});
});

View File

@@ -0,0 +1,39 @@
require(["jquery", "bsAlert", "csrfToken"], function ($, bsAlert, csrfTokenHeader) {
var avatar = "";
function changeAvatar(event) {
avatar = $(event.target).attr('src');
$('#current_avatar').attr('src', avatar);
}
$('.avatar-item').click(changeAvatar);
$('#save_avatar').click(function () {
if (avatar)
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/account/userprofile/",
data: {
avatar: avatar
},
dataType: "json",
method: "put",
success: function (data) {
if (!data.code) {
bsAlert("已保存!");
}
else {
bsAlert(data.data);
}
},
error: function () {
bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。")
}
});
else
bsAlert("请选择一个头像");
});
});

View File

@@ -3,13 +3,14 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c
if (!e.isDefaultPrevented()) {
var username = $("#username").val();
var realName = $("#real_name").val();
var school = $('#school').val();
var password = $("#password").val();
var email = $("#email").val();
var captcha = $("#captcha").val();
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/register/",
data: {username: username, real_name: realName, password: password, email: email, captcha:captcha},
data: {username: username, school: school, real_name: realName, password: password, email: email, captcha:captcha},
dataType: "json",
method: "post",
success: function (data) {

View File

@@ -0,0 +1,41 @@
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
var applied_captcha = false;
$('form').validator().on('submit', function (e) {
if (!e.isDefaultPrevented()) {
var index = location.href.indexOf("/t/");
var token = location.href.substr(36+3, 32);
var captcha = $("#captcha").val();
var password = $("#new_password").val();
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/reset_password/",
data: {password: password, captcha: captcha, token:token},
dataType: "json",
method: "post",
success: function (data) {
if (!data.code) {
refresh_captcha();
bsAlert(data.data);
window.location.href = "/login/";
}
else {
refresh_captcha();
bsAlert(data.data);
}
},
error: function(){
bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。")
}
});
return false;
}
});
function refresh_captcha(){
$("#captcha-img")[0].src = "/captcha/?" + Math.random();
$("#captcha")[0].value = "";
}
$("#captcha-img").click(function(){
refresh_captcha();
});
});

View File

@@ -0,0 +1,43 @@
require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) {
$('form').validator().on('submit', function (e) {
if (!e.isDefaultPrevented()) {
var phone = $("#phone").val();
var hduoj_username = $("#hduoj_username").val();
var bestcoder_username = $("#bestcoder_username").val();
var codeforces_username = $("#codeforces_username").val();
var blog = $("#blog").val();
var mood = $("#mood").val();
var school = $("#school").val();
$.ajax({
beforeSend: csrfTokenHeader,
url: "/api/account/userprofile/",
data: {
phone_number: phone,
hduoj_username: hduoj_username,
bestcoder_username: bestcoder_username,
codeforces_username: codeforces_username,
blog: blog,
mood: mood,
school: school
},
dataType: "json",
method: "put",
success: function (data) {
if (!data.code) {
bsAlert("修改成功");
}
else{
bsAlert(data.data);
}
},
error: function () {
bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。")
}
});
return false;
}
});
});

View File

@@ -26,6 +26,7 @@
// ------ admin web 组件 ----------
pager: "components/pager",
editorComponent: "components/editorComponent",
testCaseUploader: "components/testCaseUploader",
// ------ 下面写的都不要直接用,而是使用上面的封装版本 ------
//富文本编辑器simditor -> editor
@@ -74,6 +75,7 @@
groupDetail_20_pack: "app/admin/group/groupDetail",
editContest_21_pack: "app/admin/contest/editContest",
group_22_pack: "app/admin/group/group",
settings_23_pack: "app/oj/account/settings"
},
shim: {
avalon: {
@@ -158,7 +160,10 @@
},
{
name: "group_22_pack"
}
},
{
name: "settings_23_pack"
},
],
optimizeCss: "standard",
})

View File

@@ -0,0 +1,83 @@
define("testCaseUploader", ["avalon", "uploader", "bsAlert", "jquery"], function(avalon, uploader, bsAlert, $){
avalon.component("ms:testcaseuploader", {
$template: '<div class="col-md-12">' +
'<br> ' +
'<label>测试数据</label><br> ' +
'<small class="text-info">' +
'请将所有测试用例打包在一个文件中上传,' +
'所有文件要在压缩包的根目录,' +
'且输入输出文件名要以从1开始连续数字标识要对应例如' +
'<br>1.in 1.out 2.in 2.out </small> ' +
'<p>上传进度<span ms-text="uploadProgress"></span>%</p> ' +
'<table class="table table-striped" ms-visible="uploaded"> ' +
'<tr> <td>编号</td> <td>输入文件名</td> <td>输出文件名</td> </tr> ' +
'<tr ms-repeat="testCaseList"> ' +
'<td>{{ $index + 1 }}</td> ' +
'<td>{{ el.input }}</td> ' +
'<td>{{ el.output }}</td> </tr> ' +
'</table> ' +
'</div> ' +
'<div class="col-md-12"> ' +
'<div class="form-group">' +
' <div id="testCaseFileSelector">选择文件</div> ' +
'</div> ' +
'</div>',
testCaseId: "",
testCaseList: [],
uploaded: false,
uploadProgress: 0,
setTestCaseId: function(){},
$init: function(vm, el){
vm.setTestCase = function(testCaseId){
vm.testCaseId = testCaseId;
$.ajax({
url: "/api/admin/test_case_upload/?test_case_id=" + testCaseId,
method: "get",
success: function(data){
if(data.code){
bsAlert("获取测试用例列表失败");
}
else{
for(var key in data.data.file_list){
vm.testCaseList.push({
input: data.data.file_list[key].input_name,
output: data.data.file_list[key].output_name
})
}
vm.uploaded = true;
vm.uploadProgress = 100;
}
}
});
}
},
$ready: function(vm, el){
el.msRetain = true;
var testCaseUploader = uploader("#testCaseFileSelector", "/api/admin/test_case_upload/",
function (file, response) {
if (response.code) {
bsAlert(response.data);
}
else {
vm.testCaseId = response.data.test_case_id;
vm.uploaded = true;
vm.testCaseList = [];
for(var key in response.data.file_list){
vm.testCaseList.push({
input: response.data.file_list[key].input_name,
output: response.data.file_list[key].output_name
})
}
bsAlert("测试数据添加成功!共添加" + vm.testCaseList.length + "组测试数据");
}
},
function (file, percentage) {
vm.uploadProgress = parseInt(percentage * 100);
});
}
})
});

View File

@@ -27,6 +27,7 @@ var require = {
// ------ admin web 组件 ----------
pager: "components/pager",
editorComponent: "components/editorComponent",
testCaseUploader: "components/testCaseUploader",
// ------ 下面写的都不要直接用,而是使用上面的封装版本 ------
@@ -76,6 +77,7 @@ var require = {
groupDetail_20_pack: "app/admin/group/groupDetail",
editContest_21_pack: "app/admin/contest/editContest",
group_22_pack: "app/admin/group/group",
settings_23_pack: "app/oj/account/settings"
},
shim: {
avalon: {

View File

@@ -1,6 +1,9 @@
<div ms-controller="add_contest">
<form id="add-contest-form">
<div class="col-md-9">
<div class="alert alert-warning" role="alert">
<p>注意!管理员在测试比赛题目的时候请务必保持比赛状态为没有开始,这时只有管理员可以正常查看和提交题目,而且不会产生排名。</p>
</div>
<div class="col-md-12">
<label>比赛名称</label>
</div>

View File

@@ -41,153 +41,5 @@
<ms:pager $id="contestListPager" config="pager"></ms:pager>
</div>
<!--
<div ms-visible="editingContestId">
<form id="edit-contest-form">
<div class="col-md-12">
<label>比赛名称</label>
</div>
<div class="col-md-12">
<div class="form-group">
<input type="text" name="name" class="form-control" ms-duplex="editTitle"
data-error="请填写比赛名称(名称不能超过50个字)" required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12">
<label>说明</label>
</div>
<div class="col-md-12">
<div class="form-group">
<textarea id="editor" placeholder="这里输入内容" autofocus ms-duplex="editDescription"></textarea>
<div class="help-block with-errors"></div>
<p class="error-info" ms-visible="editDescription==''">请填写比赛描述</p>
</div>
</div>
<div class="col-md-6">
<label>开始时间</label>
<div class="form-group">
<input type="text" class="form-control" name="start_time" id="contest_start_time"
ms-duplex="editStartTime" data-error="请填写比赛开始时间" required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-6">
<label>结束时间</label>
<div class="form-group">
<input type="text" class="form-control" name="end_time" id="contest_end_time"
ms-duplex="editEndTime" data-error="请填写比赛结束时间" required>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>可见范围</label>
<span ms-if="showGlobalViewRadio">
<label>
<small><input type="radio" value="true" name="isGlobal" ms-duplex-boolean="isGlobal">全局可见
</small>
</label>
</span>
<span>
<label>
<small><input type="radio" value="false" name="isGlobal" ms-duplex-boolean="isGlobal">小组内可见
</small>
</label>
</span>
</div>
</div>
<div class="col-md-6" ms-visible="isGlobal">
<label>密码保护</label>
<input type="text" class="form-control" name="password" placeholder="留空就是公开赛" ms-duplex="editPassword">
</div>
<div class="form-group col-md-12" ms-visible="!isGlobal">
<div ms-repeat="allGroups" class="col-md-4">
<input type="checkbox" value="group_id" ms-duplex-checked="el.isSelected">&nbsp;&nbsp;{{ el.name }}
</div>
</div>
<div class="col-md-3">
<label>排名方式</label>
<div class="form-group">
<label><input type="radio" name="mode" ms-duplex-string="editMode" value="0">
<small>ACM</small>
</label>
<label><input type="radio" name="mode" ms-duplex-string="editMode" value="1">
<small>分数</small>
</label>
</div>
</div>
<div class="col-md-2">
<div class="form-group">
<label>是否可见</label>
<label><input type="checkbox" ms-duplex-checked="editVisible">
<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="editShowSubmission">
<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="editRealTimeRank">
<small>是</small>
</label>
</div>
</div>
<div class="col-md-12">
<button class="btn btn-success" type="submit">保存修改</button>
</div>
</form>
</div>
<div class="col-md-12" ms-visible="editingProblemContestIndex">
<label>题目列表</label>
<a href="javascript:void(0)" class="btn btn-primary btn-sm" ms-click="addProblem()">添加题目</a>
<a href="javascript:void(0)" class="btn btn-info btn-sm" ms-click="showSubmissionPage()">查看提交</a>
<table class="table table-striped">
<tr>
<th>编号</th>
<th>题目</th>
<th ms-visible="editMode=='2'">分值</th>
<th>可见</th>
<th>创建时间</th>
<td></td>
</tr>
<tr ms-repeat="editProblemList">
<td>{{ el.sort_index }}</td>
<td>{{ el.title }}</td>
<td ms-visible="editMode=='2'">{{ el.score }}</td>
<td ms-text="el.visible?'可见':'不可见'"></td>
<td>{{ el.create_time|date("yyyy-MM-dd HH:mm:ss") }}</td>
<td>
<a href="javascript:void(0)" class="btn-sm btn-info"
ms-click="showProblemEditPage(el)">编辑</a>
<a href="javascript:void(0)" class="btn-sm btn-info"
ms-click="showSubmissionPage(el)">提交</a>
<a href="javascript:void(0)" class="btn-sm btn-info"
ms-click="addToProblemList(el)" ms-visible="admin_type=='2'">添加到前台</a>
</td>
</tr>
</table>
</div>
-->
</div>
<script src="/static/js/app/admin/contest/contestList.js"></script>

View File

@@ -6,6 +6,9 @@
aria-hidden="true">&larr;</span> 返回</a></li>
</ul>
</nav>
<div class="alert alert-warning" role="alert">
<p>注意!管理员在测试比赛题目的时候请务必保持比赛状态为没有开始,这时只有管理员可以正常查看和提交题目,而且不会产生排名。</p>
</div>
<div class="col-md-12">
<label>比赛名称</label>
</div>

View File

@@ -96,29 +96,7 @@
</div>
</div>
</div>
<div class="col-md-12"><br>
<label>测试数据<span ms-if="uploadSuccess">(当前已上传,继续上传将覆盖原有测试用例)</span></label><br>
<small class="text-info">请将所有测试用例打包在一个文件中上传所有文件要在压缩包的根目录且输入输出文件名要以从1开始连续数字标识要对应例如<br>
1.in 1.out 2.in 2.out
</small>
<table class="table table-striped" ms-visible="uploadSuccess">
<tr>
<td>编号</td>
<td>输入文件名</td>
<td>输出文件名</td>
</tr>
<tr ms-repeat="testCaseList">
<td>{{ $index + 1 }}</td>
<td>{{ el.input }}</td>
<td>{{ el.output }}</td>
</tr>
</table>
</div>
<div class="col-md-12">
<div class="form-group">
<div id="testCaseFile">选择文件</div>
</div>
</div>
<ms:testcaseuploader $id="testCaseUploader"></ms:testcaseuploader>
<div class="form-group col-md-12">
<label>提示</label>
<ms:editor $id="contestProblemHintEditor" config="contestProblemHintEditor"></ms:editor>

View File

@@ -27,7 +27,7 @@
<td>{{ el.total_accepted_number }}/{{ el.total_submit_number }}</td>
<td>
<button class="btn-sm btn-info" ms-click="showEditProblemPage(el.id)">编辑</button>
<button class="btn-sm btn-primary" ms-if="!el.is_public && adminType == 2" ms-click="makeProblemPublic(el)">公开</button>
<button class="btn-sm btn-primary" ms-if="!el.is_public && adminType == 2 && el.visible" ms-click="makeProblemPublic(el)">公开</button>
</td>
</tr>
</table>

View File

@@ -99,30 +99,7 @@
</div>
</div>
</div>
<div class="col-md-12"><br>
<label>测试数据</label><br>
<small class="text-info">请将所有测试用例打包在一个文件中上传所有文件要在压缩包的根目录且输入输出文件名要以从1开始连续数字标识要对应例如<br>
1.in 1.out 2.in 2.out
</small>
<p>上传进度<span ms-text="uploadProgress"></span>%</p>
<table class="table table-striped" ms-visible="uploadSuccess">
<tr>
<td>编号</td>
<td>输入文件名</td>
<td>输出文件名</td>
</tr>
<tr ms-repeat="testCaseList">
<td>{{$index + 1}}</td>
<td>{{ el.input }}</td>
<td>{{ el.output }}</td>
</tr>
</table>
</div>
<div class="col-md-12">
<div class="form-group">
<div id="testCaseFile">选择文件</div>
</div>
</div>
<ms:testcaseuploader $id="testCaseUploader"></ms:testcaseuploader>
<div class="form-group col-md-12">
<label>提示</label>
<ms:editor $id="problemHintEditor" config="problemHintEditor"></ms:editor>

View File

@@ -105,30 +105,7 @@
</div>
</div>
</div>
<div class="col-md-12"><br>
<label>测试数据(当前已上传,继续上传将覆盖原有测试用例)</label><br>
<small class="text-info">请将所有测试用例打包在一个文件中上传所有文件要在压缩包的根目录且输入输出文件名要以从1开始连续数字标识要对应例如<br>
1.in 1.out 2.in 2.out
</small>
<p>上传进度<span ms-text="uploadProgress"></span>%</p>
<table class="table table-striped" ms-visible="uploadSuccess">
<tr>
<td>编号</td>
<td>输入文件名</td>
<td>输出文件名</td>
</tr>
<tr ms-repeat="testCaseList">
<td>{{ $index + 1 }}</td>
<td>{{ el.input }}</td>
<td>{{ el.output }}</td>
</tr>
</table>
</div>
<div class="col-md-12">
<div class="form-group">
<div id="testCaseFile">选择文件</div>
</div>
</div>
<ms:testcaseuploader $id="testCaseUploader"></ms:testcaseuploader>
<div class="form-group col-md-12">
<label>提示</label>
<ms:editor $id="problemHintEditor" config="problemHintEditor"></ms:editor>

View File

@@ -0,0 +1,40 @@
{% extends "oj_base.html" %}
{% block title %}
找回登录信息
{% endblock %}
{% block body %}
<div class="container main">
<div class="col-md-6 col-md-offset-3">
<h2 class="text-center">找回登录信息</h2><br>
<div>
<p>请输入你注册时使用的邮箱地址,系统将自动向你的邮箱发送一封含有您登录信息的电子邮件,
你可以看到你的用户名并可以选择重新设置登录密码注意为了你的账户安全重置密码链接仅在30分钟内有效</p>
</div>
<br>
<form id="login-form">
<div class="form-group">
<label for="email">注册电子邮件地址</label>
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址"
data-remote="/api/email_check/?reset=true" data-remote-error="该邮箱未被注册!" data-error="请填写正确的邮箱地址"
required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group" id="captcha-area">
<label for="captcha">验证码</label>&nbsp;&nbsp;<img src="/captcha/" id="captcha-img">
<small><p></p></small>
<input type="text" class="form-control input-lg" id="captcha" name="captcha"
placeholder="验证码" maxlength="4" data-error="请填写验证码" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</form>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/account/applyResetPassword.js"></script>
{% endblock %}

View File

@@ -14,27 +14,38 @@
</ul>
</div>
<div class="col-lg-3">
<img src="https://coding.net/static/fruit_avatar/Fruit-1.png" class="img-responsive"
style="height: 200px;width: 200px;">
<img src="{{ user.userprofile.avatar }}" class="img-responsive"
style="height: 200px;width: 200px;" id="current_avatar">
<br>
</div>
<div class="col-lg-6">
<form>
<div>
<div class="row">
{% for i in "aaaaaaaaaaaaaaaaaaaa" %}
<div class="col-lg-2 avatar-item">
<div style="border: 1px solid red;" >
<img src="/static/img/avatar/avatar-{{ forloop.counter }}.png">
<div>
<img src="/static/img/avatar/avatar-{{ forloop.counter }}.png" style="cursor:pointer">
</div>
</div>
{% endfor %}
</div>
<div title="Coding.net 是深圳市扣钉网络科技有限公司打造的国内最大的一站式云端开发平台提供包括代码托管项目管理产品演示WebIDE 等工具。">
<img src="https://coding.net/static/d0aa9dd75e2500930b9e6ef61d9bcfa0.jpg" style="height: 100px;width: 150px">
<img src="https://coding.net/static/d0aa9dd75e2500930b9e6ef61d9bcfa0.jpg"
style="height: 100px;width: 150px">
水果头像由<a href="https://coding.net">coding.net</a>提供。
</div>
<div class="form-group left">
<button id="save_avatar" type="submit" class="btn btn-primary">保存</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/account/avatar.js"></script>
{% endblock %}

View File

@@ -1,33 +1,52 @@
{% extends "oj_base.html" %}
{% block title %}
用户修改密码
用户设置
{% endblock %}
{% block body %}
<div class="container main">
<div class="col-md-6 col-md-offset-3">
<h2 class="text-center">修改密码</h2>
<div class="col-lg-2">
<ul class="list-group">
<li class="list-group-header">通用设置</li>
<li class="list-group-item"><a href="/account/settings/">个人信息</a></li>
<li class="list-group-item"><a href="/account/settings/avatar/">更换头像</a></li>
<li class="list-group-item active"><a href="/change_password/">修改密码</a></li>
</ul>
</div>
<div class="col-lg-3">
<img src="{{ user.userprofile.avatar }}" class="img-responsive"
style="height: 200px;width: 200px;">
</div>
<div class="col-lg-6">
<form id="change_password-form">
<div class="form-group">
<label for="password">旧密码</label>
<input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码" data-error="请填写旧密码" maxlength="30" required>
<input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码"
data-error="请填写旧密码" maxlength="30" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="new_password">新密码</label>
<input type="password" class="form-control input-lg" id="new_password" name="new_password" placeholder="新密码" maxlength="30" data-minlength="6" data-error="密码不得少于6位" required>
<input type="password" class="form-control input-lg" id="new_password" name="new_password"
placeholder="新密码" maxlength="30" data-minlength="6" data-error="密码不得少于6位" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="confirm_password">确认密码</label>
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password" placeholder="确认密码" maxlength="30" data-match="#new_password" data-match-error="两个密码不一致" required>
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password"
placeholder="确认密码" maxlength="30" data-match="#new_password" data-match-error="两次密码不一致" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="captcha">验证码</label>
&nbsp;&nbsp;<img src="/captcha/" id="captcha-img">
<small><p></p></small>
<input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码" maxlength="4" data-error="请填写验证码" required>
<input type="text" class="form-control input-lg" id="captcha" name="captcha" placeholder="验证码"
maxlength="4" data-error="请填写验证码" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
@@ -36,6 +55,7 @@
</form>
</div>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/account/changePassword.js"></script>

View File

@@ -32,6 +32,7 @@
<div class="form-group">
<button type="submit" class="btn btn-primary">提交</button>
</div>
<a href="/reset_password/">忘记用户名/密码</a><br>
<a href="/register/">还没有帐号?点击注册</a>
</form>

View File

@@ -18,6 +18,11 @@
<input type="text" class="form-control input-lg" id="real_name" name="real_name" placeholder="真实姓名" data-error="请填写姓名" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="real_name">学校</label>
<input type="text" class="form-control input-lg" id="school" name="school" placeholder="学校" data-error="请填写学校" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<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>

View File

@@ -1,10 +1,53 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
{% extends "oj_base.html" %}
{% block title %}
找回登录信息
{% endblock %}
{% block body %}
<div class="container main">
<div class="col-md-6 col-md-offset-3">
<h2 class="text-center">找回登录信息</h2><br>
<br>
</body>
</html>
<form id="login-form">
<div class="form-group">
<label for="email">注册电子邮件地址</label>
<input type="text" class="form-control input-lg" name="email" value="{{ user.email }}" readonly>
</div>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control input-lg" name="username" value="{{ user.username }}" readonly>
</div>
<div class="form-group">
<label for="new_password">新密码</label>
<input type="password" class="form-control input-lg" id="new_password" name="new_password"
placeholder="新密码" maxlength="30" data-minlength="6" data-error="密码不得少于6位" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label for="confirm_password">确认密码</label>
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password"
placeholder="确认密码" maxlength="30" data-match="#new_password" data-error="请输入确认密码"
data-match-error="两次密码不一致"
required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group" id="captcha-area">
<label for="captcha">验证码</label>&nbsp;&nbsp;<img src="/captcha/" id="captcha-img">
<small><p></p></small>
<input type="text" class="form-control input-lg" id="captcha" name="captcha"
placeholder="验证码" maxlength="4" data-error="请填写验证码" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</form>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/account/resetPassword.js"></script>
{% endblock %}

View File

@@ -14,8 +14,7 @@
</ul>
</div>
<div class="col-lg-3">
<img src="https://coding.net/static/fruit_avatar/Fruit-1.png" class="img-responsive"
style="height: 200px;width: 200px;">
<img src="{{ user.userprofile.avatar }}" class="img-responsive index-avatar">
</div>
<div class="col-lg-6">
<form>
@@ -23,57 +22,78 @@
<div class="form-group col-md-6"><label>用户名</label>
<input name="username" type="text" class="form-control"
value="{{ request.user.username }}" readonly>
</div>
<div class="form-group col-md-6"><label>真实姓名</label>
<input name="real_name" type="text" class="form-control"
value="{{ request.user.real_name }}" readonly>
</div>
</div>
<div class="row">
<div class="form-group col-md-6"><label>电子邮箱</label>
<input name="email" type="email" class="form-control"
value="{{ request.user.email }}" readonly>
<div class="help-block with-errors"></div>
</div>
<div class="form-group col-md-6"><label>手机</label>
<input name="phone" type="text" class="form-control"
data-error="请填写手机号码" value="{{ request.user.phone }}">
<input name="phone" type="text" maxlength="11" id="phone"
class="form-control"
value="{% if request.user.userprofile.phone_number %}{{ request.user.userprofile.phone_number }}{% endif %}">
</div>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="row">
<div class="form-group col-md-6">
<label>学校</label>
<input name="school" type="text" class="form-control" id="school"
value="{% if request.user.userprofile.school %}{{ request.user.userprofile.school }}{% endif %}">
<div class="help-block with-errors"></div>
</div>
<div class="form-group col-md-6">
<label>hduoj 用户名</label>
<input name=hduoj" type="text" class="form-control"
value="{{ request.user.hduoj_username }}">
<input name="hduoj" type="text" class="form-control" id="hduoj_username"
value="{{ request.user.userprofile.hduoj_username }}">
<div class="help-block with-errors"></div>
</div>
<div class="form-group col-md-6">
<label>BestCoder 用户名</label>
<input name=bestcoder" type="text" class="form-control"
value="{{ request.user.bestcoder_username }}">
<input name="bestcoder" type="text" class="form-control" id="bestcoder_username"
value="{{ request.user.userprofile.bestcoder_username }}">
<div class="help-block with-errors"></div>
</div>
<div class="form-group col-md-6">
<label>Codeforces 用户名</label>
<input name=codeforces" type="text" class="form-control"
value="{{ request.user.bestcoder_username }}">
<input name="codeforces" type="text" class="form-control" id="codeforce_username"
value="{{ request.user.userprofile.bestcoder_username }}">
<div class="help-block with-errors"></div>
</div>
<div class="form-group col-md-12"><label>blog</label>
<input name=blog" type="url" class="form-control"
value="{{ request.user.blog }}">
<input name="blog" type="url" class="form-control" id="blog"
value="{{ request.user.userprofile.blog }}">
<div class="help-block with-errors"></div>
</div>
<div class="form-group col-md-12">
<label>签名</label>
<input name="mood" type="text" maxlength="60" class="form-control" id="mood"
value="{% if request.user.userprofile.mood %}{{ request.user.userprofile.mood }}{% endif %}"
data-error="字数限制在30字以内">
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
{% block js_block %}
<script src="/static/js/app/oj/account/settings.js"></script>
{% endblock %}

View File

@@ -19,7 +19,9 @@
{% endifequal %}
</h2>
{% if user.userprofile.mood %}
<p id="user-mood">{{ user.userprofile.mood }}</p>
{% endif %}
</div>
<div class="list-group col-lg-9">
@@ -32,7 +34,7 @@
{% 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">
<a href="http://acm.hdu.edu.cn/userstatus.php?user={{ user.userprofile.hduoj_username }}" target="_blank">
{{ user.userprofile.hduoj_username }}
</a>
</p>
@@ -61,15 +63,17 @@
{{ user.create_time }}
</p>
<div class="rows">
<!--
<div class="col-lg-4 text-center">
<strong id="user-data-number">{{ user.userprofile.rank }}</strong>
<span id="user-data-text">Rank</span>
</div>
<div class="col-lg-4 text-center">
-->
<div class="col-lg-6 text-center">
<strong id="user-data-number">{{ user.userprofile.accepted_number }}</strong>
<span id="user-data-text">AC</span>
</div>
<div class="col-lg-4 text-center">
<div class="col-lg-6 text-center">
<strong id="user-data-number">{{ user.userprofile.submissions_number }}</strong>
<span id="user-data-text">Submissions</span>
</div>

View File

@@ -90,6 +90,9 @@
</nav>
{% else %}
<p>还没有结果</p>
<input type="checkbox" id="auto-refresh" {% if auto_refresh %}checked{% endif %}
onchange="if(this.checked){location.href=location.href + '&auto_refresh=true'}else{location.href=location.href=location.href.replace('&auto_refresh=true', '')}">
自动刷新
{% endif %}
</div>

View File

@@ -100,6 +100,15 @@
max-width: 300px;
}
.section{
background-size: cover;
}
#section0{
background-image: url(/static/img/index/bg/bg3.jpg);
padding: 0 0 0 0;
}
</style>
<link rel="stylesheet" href="/static/css/animate/animate.css">
@@ -110,12 +119,21 @@
<script type="text/javascript">
$(document).ready(function () {
$('#fullpage').fullpage({
sectionsColor: ['#28ac72', '#2f7ddb', '#FAC832', '#B01414'],
sectionsColor: ['#ffffff', '#2f7ddb', '#FAC832', '#B01414'],
css3: true,
navigation: true,
loopBottom: true
});
});
/*
var c = 0;
setInterval(function(){
//alert(1);
$("#section0").css("background-image", "url(/static/img/index/bg/bg" + (c++ % 4).toString() + ".jpg)")
}, 1000)
*/
</script>
</head>
@@ -172,7 +190,7 @@
<div class="section-text">
<img class="icon" id="img3" src="/static/img/index/m.png">
<h1>自由举办小组赛(10月上线)</h1>
<h1>自由举办小组赛(12月上线)</h1>
<h3>内部比赛,日常作业,期末考试,通通搞定</h3>
</div>

View File

@@ -61,9 +61,11 @@
<ul class="dropdown-menu">
{% if request.user.admin_type > 0 %}
<li><a href="/admin/">后台管理</a></li>
<li role="separator" class="divider"></li>
{% endif %}
<li><a href="/user/{{ request.user.username }}/">我的主页</a></li>
<li><a href="/submissions/">我的提交</a></li>
<li><a href="#">我的资料</a></li>
<li><a href="/account/settings/">设置</a></li>
<li role="separator" class="divider"></li>
<li><a href="/logout/">退出</a></li>
</ul>

View File

@@ -8,7 +8,7 @@
<tbody>
<tr height="39" style="background-color:#50a5e6;">
<td style="padding-left:15px;font-family:'微软雅黑','黑体',arial;">
{{ website_name }} 密码找回邮件
{{ website_name }} 登录信息找回
</td>
</tr>
</tbody>
@@ -32,12 +32,12 @@
</tr>
<tr height="30">
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:14px;">
您刚刚在 {{ website_name }} 使用了找回密码功能
您刚刚在 {{ website_name }} 申请了找回登录信息服务
</td>
</tr>
<tr height="30">
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:14px;">
请在<span style="color:rgb(255,0,0)">60分钟</span>内点击下面链接设置您的新密码:
请在<span style="color:rgb(255,0,0)">30分钟</span>内点击下面链接设置您的新密码:
</td>
</tr>
<tr height="60">
@@ -63,7 +63,8 @@
</tr>
<tr height="20">
<td style="padding-left:55px;padding-right:55px;font-family:'微软雅黑','黑体',arial;font-size:12px;">
如果没有提出过密码修改申请,请忽略此邮件。有可能是其他用户误填了你的用户名。我们不会对你的帐户进行任何修改。
如果没有提出过申请,请忽略此邮件。有可能是其他用户误填了您的邮件地址,我们不会对你的帐户进行任何修改。
请不要向他人透露本邮件的内容,否则可能会导致您的账号被盗。
</td>
</tr>
<tr height="20">