diff --git a/account/decorators.py b/account/decorators.py index 2646c02..b0dbecb 100644 --- a/account/decorators.py +++ b/account/decorators.py @@ -1,4 +1,5 @@ # coding=utf-8 +import urllib import functools from functools import wraps @@ -27,7 +28,7 @@ class BasePermissionDecorator(object): if self.request.is_ajax(): return error_response(u"请先登录") else: - return HttpResponseRedirect("/login/") + return HttpResponseRedirect("/login/?__from=" + urllib.quote(self.request.build_absolute_uri())) def check_permission(self): raise NotImplementedError() diff --git a/account/migrations/0013_userprofile.py b/account/migrations/0013_userprofile.py new file mode 100644 index 0000000..02689e3 --- /dev/null +++ b/account/migrations/0013_userprofile.py @@ -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', + }, + ), + ] diff --git a/account/models.py b/account/models.py index 4c6bfca..0fb7858 100644 --- a/account/models.py +++ b/account/models.py @@ -48,3 +48,26 @@ class User(AbstractBaseUser): class Meta: 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为ac,2为正在进行 + problems_status = JSONField(default={}) + + class Meta: + db_table = "user_profile" diff --git a/account/serializers.py b/account/serializers.py index 13f3e24..5004ee1 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -57,4 +57,8 @@ class ApplyResetPasswordSerializer(serializers.Serializer): class ResetPasswordSerializer(serializers.Serializer): token = serializers.CharField(min_length=1, max_length=40) password = serializers.CharField(min_length=6, max_length=30) - captcha = serializers.CharField(max_length=4, min_length=4) \ No newline at end of file + captcha = serializers.CharField(max_length=4, min_length=4) + + +class SSOSerializer(serializers.Serializer): + token = serializers.CharField(max_length=40) \ No newline at end of file diff --git a/account/views.py b/account/views.py index b50e0a3..5b75ce9 100644 --- a/account/views.py +++ b/account/views.py @@ -5,6 +5,7 @@ from django.contrib import auth from django.shortcuts import render from django.db.models import Q from django.conf import settings +from django.http import HttpResponseRedirect from django.core.exceptions import MultipleObjectsReturned from django.utils.timezone import now @@ -20,7 +21,8 @@ from .models import User from .serializers import (UserLoginSerializer, UsernameCheckSerializer, UserRegisterSerializer, UserChangePasswordSerializer, EmailCheckSerializer, UserSerializer, EditUserSerializer, - ApplyResetPasswordSerializer, ResetPasswordSerializer) + ApplyResetPasswordSerializer, ResetPasswordSerializer, + SSOSerializer) from .decorators import super_admin_required @@ -284,15 +286,37 @@ class ResetPasswordAPIView(APIView): 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): - if not request.user.is_authenticated(): - return render(request, "oj/account/oauth.html") - callback = request.GET.get("callback", None) - if not callback: - return error_page(request, u"参数错误") - token = rand_str() - request.user.auth_token = token - return render(request, "oj/account/oauth.html", {"callback": callback, "token": token}) +class SSOAPIView(APIView): + def post(self, request): + serializer = SSOSerializer(data=request.data) + if serializer.is_valid(): + try: + user = User.objects.get(auth_token=serializer.data["token"]) + return success_response({"username": user.username}) + except User.DoesNotExist: + 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}) \ No newline at end of file diff --git a/contest/decorators.py b/contest/decorators.py index d08783c..2f5e3ba 100644 --- a/contest/decorators.py +++ b/contest/decorators.py @@ -1,4 +1,5 @@ # coding=utf-8 +import urllib from functools import wraps from django.http import HttpResponse, HttpResponseRedirect @@ -30,7 +31,7 @@ def check_user_contest_permission(func): if request.is_ajax(): return error_response(u"请先登录") else: - return HttpResponseRedirect("/login/") + return HttpResponseRedirect("/login/?__from=" + urllib.quote(request.build_absolute_uri())) # kwargs 就包含了 url 里面的参数 if "contest_id" in kwargs: diff --git a/contest/serializers.py b/contest/serializers.py index 8f3bf0b..e0fde2a 100644 --- a/contest/serializers.py +++ b/contest/serializers.py @@ -11,10 +11,8 @@ from .models import Contest, ContestProblem class CreateContestSerializer(serializers.Serializer): title = serializers.CharField(max_length=40) description = serializers.CharField(max_length=5000) - mode = serializers.IntegerField() contest_type = serializers.IntegerField() real_time_rank = serializers.BooleanField() - show_user_submission = serializers.BooleanField() password = serializers.CharField(max_length=30, required=False, default=None) start_time = serializers.DateTimeField() end_time = serializers.DateTimeField() @@ -45,10 +43,8 @@ class EditContestSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=40) description = serializers.CharField(max_length=10000) - mode = serializers.IntegerField() contest_type = serializers.IntegerField() real_time_rank = serializers.BooleanField() - show_user_submission = serializers.BooleanField() password = serializers.CharField(max_length=30, required=False, default=None) start_time = serializers.DateTimeField() end_time = serializers.DateTimeField() diff --git a/contest/test_urls.py b/contest/test_urls.py deleted file mode 100644 index 234c600..0000000 --- a/contest/test_urls.py +++ /dev/null @@ -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"), -] diff --git a/contest/views.py b/contest/views.py index d9eee7b..29c943a 100644 --- a/contest/views.py +++ b/contest/views.py @@ -64,9 +64,8 @@ class ContestAdminAPIView(APIView): return error_response(u"比赛的开始时间必须早于比赛结束的时间") try: 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"], - show_user_submission=data["show_user_submission"], start_time=dateparse.parse_datetime(data["start_time"]), end_time=dateparse.parse_datetime(data["end_time"]), created_by=request.user, visible=data["visible"]) @@ -125,10 +124,8 @@ class ContestAdminAPIView(APIView): contest.title = data["title"] contest.description = data["description"] - contest.mode = data["mode"] contest.contest_type = data["contest_type"] 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.end_time = dateparse.parse_datetime(data["end_time"]) contest.visible = data["visible"] @@ -225,6 +222,7 @@ class ContestProblemAdminAPIView(APIView): contest_problem.visible = data["visible"] contest_problem.sort_index = data["sort_index"] contest_problem.score = data["score"] + contest_problem.last_update_time = now() contest_problem.save() return success_response(ContestProblemSerializer(contest_problem).data) else: @@ -486,6 +484,10 @@ def contest_problem_submissions_list_page(request, contest_id, page=1): if 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 (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", {"submissions": current_page, "page": int(page), "previous_page": previous_page, "next_page": next_page, "start_id": int(page) * 20 - 20, - "contest": contest, "filter": filter, "user_id": user_id}) \ No newline at end of file + "contest": contest, "filter": filter, "user_id": user_id, "problem_id": problem_id}) \ No newline at end of file diff --git a/oj/local_settings.py b/oj/local_settings.py index 074b1b0..a08ae7e 100644 --- a/oj/local_settings.py +++ b/oj/local_settings.py @@ -11,18 +11,13 @@ DATABASES = { }, # submission 的 name 和 engine 请勿修改,其他代码会用到 'submission': { - 'NAME': 'oj_submission', - 'ENGINE': 'django.db.backends.mysql', - 'CONN_MAX_AGE': 0.1, - 'HOST': "127.0.0.1", - 'PORT': 3306, - 'USER': 'root', - 'PASSWORD': 'root', + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'), } } REDIS_CACHE = { - "host": "121.42.32.129", + "host": "127.0.0.1", "port": 6379, "db": 1 } @@ -36,4 +31,6 @@ ALLOWED_HOSTS = [] STATICFILES_DIRS = [os.path.join(BASE_DIR, "static/src/"), BASE_DIR] # 模板文件夹 -TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/src/')] \ No newline at end of file +TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'template/src/')] + +SSO = {"callback": "http://localhost:8765/login"} \ No newline at end of file diff --git a/oj/server_settings.py b/oj/server_settings.py index 9f8f084..bba27c6 100644 --- a/oj/server_settings.py +++ b/oj/server_settings.py @@ -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/')] SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') + +SSO = {"callback": "https://discuss.acmer.site/login"} diff --git a/oj/urls.py b/oj/urls.py index f95fa2b..dfbe669 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -6,7 +6,7 @@ from django.views.generic import TemplateView from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView, EmailCheckAPIView, UserAdminAPIView, UserInfoAPIView, - ApplyResetPasswordAPIView) + ApplyResetPasswordAPIView, SSOAPIView) 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/admin/rejudge/$', SubmissionRejudgeAdminAPIView.as_view(), name="submission_rejudge_api"), - url(r'^user/(?P\w+)/$', "account.views.user_index_page"), + url(r'^user/(?P.+)/$', "account.views.user_index_page"), 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/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"), ] diff --git a/static/src/css/global.css b/static/src/css/global.css index 339c0d6..796a250 100644 --- a/static/src/css/global.css +++ b/static/src/css/global.css @@ -62,3 +62,32 @@ pre, code { font-family: "source_code_pro"; 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; +} \ No newline at end of file diff --git a/static/src/js/app/admin/contest/addContest.js b/static/src/js/app/admin/contest/addContest.js index e2059b8..b05ba4e 100644 --- a/static/src/js/app/admin/contest/addContest.js +++ b/static/src/js/app/admin/contest/addContest.js @@ -1,5 +1,5 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "datetimePicker", - "validator"], + "validator", "editorComponent"], function ($, avalon, editor, uploader, bsAlert, csrfTokenHeader) { $("#add-contest-form").validator().on('submit', function (e) { @@ -7,11 +7,9 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date e.preventDefault(); var ajaxData = { title: vm.title, - description: vm.description, - mode: vm.mode, + description: avalon.vmodels.contestDescriptionEditor.content, contest_type: 0, real_time_rank: vm.realTimeRank, - show_user_submission: vm.showSubmission, start_time: vm.startTime, end_time: vm.endTime, visible: false @@ -38,12 +36,11 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date bsAlert("你没有选择参赛用户!"); return false; } - if (vm.editDescription == "") { + if (ajaxData.description.trim() == "") { bsAlert("比赛描述不能为空!"); return false; } - $.ajax({ // Add contest - beforeSend: csrfTokenHeader, + $.ajax({ url: "/api/admin/contest/", dataType: "json", contentType: "application/json;charset=UTF-8", @@ -52,17 +49,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date success: function (data) { if (!data.code) { bsAlert("添加成功!将转到比赛列表页以便为比赛添加问题(注意比赛当前状态为:隐藏)"); - vm.title = ""; - vm.description = ""; - vm.startTime = ""; - vm.endTime = ""; - vm.password = ""; - vm.mode = "0"; - vm.showSubmission = true; location.hash = "#contest/contest_list"; - vm.isGlobal = true; - vm.allGroups = []; - vm.showGlobalViewRadio = true; } else { bsAlert(data.data); @@ -73,23 +60,25 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date return false; }); - editor("#editor"); + //editor("#editor"); if (avalon.vmodels.add_contest) var vm = avalon.vmodels.add_contest; else var vm = avalon.define({ $id: "add_contest", title: "", - description: "", startTime: "", endTime: "", password: "", - mode: "0", - showSubmission: true, isGlobal: true, allGroups: [], showGlobalViewRadio: true, - realTimeRank: true + realTimeRank: true, + + contestDescriptionEditor: { + editorId: "contest-description-editor", + placeholder: "比赛介绍内容" + } }); $.ajax({ @@ -102,7 +91,6 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "date if (data.data.admin_type == 1) { vm.isGlobal = false; vm.showGlobalViewRadio = false; - } } $.ajax({ diff --git a/static/src/js/app/admin/contest/contestList.js b/static/src/js/app/admin/contest/contestList.js index 4951979..7eb47fa 100644 --- a/static/src/js/app/admin/contest/contestList.js +++ b/static/src/js/app/admin/contest/contestList.js @@ -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 () { - - $("#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 = []; - 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 { 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"; + showVisibleOnly: false, + pager: { + getPage: function(page){ + getPage(page); } }, - 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); - } - } - }); + search: function () { + getPage(1); + avalon.vmodels.contestListPager.currentPage = 1; } - }); - 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" + vm.$watch("showVisibleOnly", function () { + getPage(1); + avalon.vmodels.contestListPager.currentPage = 1; }); - function getPageData(page) { - var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=10"; + function getPage(page) { + var url = "/api/admin/contest/?paging=true&page=" + page + "&page_size=20"; if (vm.showVisibleOnly) - url += "&visible=true" + url += "&visible=true"; if (vm.keyword != "") url += "&keyword=" + vm.keyword; $.ajax({ @@ -281,10 +42,8 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", 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; + vm.announcementList = data.data.results; + avalon.vmodels.contestListPager.totalPage = data.data.total_page; } else { 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(); }); \ No newline at end of file diff --git a/static/src/js/app/admin/contest/contestList1.js b/static/src/js/app/admin/contest/contestList1.js new file mode 100644 index 0000000..07ddc1e --- /dev/null +++ b/static/src/js/app/admin/contest/contestList1.js @@ -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(); +}); \ No newline at end of file diff --git a/static/src/js/app/oj/account/login.js b/static/src/js/app/oj/account/login.js index 42b8d68..8e1351d 100644 --- a/static/src/js/app/oj/account/login.js +++ b/static/src/js/app/oj/account/login.js @@ -14,21 +14,18 @@ require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, c method: "post", success: function (data) { if (!data.code) { - //成功登陆 - var ref = document.referrer; - if (ref) { - // 注册页和本页的来源的跳转回首页,防止死循环 - if (ref.indexOf("register") > -1 || ref.indexOf("login") > -1) { - location.href = "/"; - return; - } - // 判断来源,只有同域下才跳转 - if (ref.split("/")[2].split(":")[0] == location.hostname) { - location.href = ref; - return; - } + function getLocationVal(id){ + var temp = unescape(location.search).split(id+"=")[1] || ""; + return temp.indexOf("&")>=0 ? temp.split("&")[0] : temp; + } + var from = getLocationVal("__from"); + if(from != ""){ + console.log(from); + window.location.href = from; + } + else{ + location.href = "/"; } - location.href = "/"; } else { refresh_captcha(); diff --git a/static/src/js/build.js b/static/src/js/build.js index 96247c1..6904946 100644 --- a/static/src/js/build.js +++ b/static/src/js/build.js @@ -25,6 +25,7 @@ // ------ admin web 组件 ---------- pager: "components/pager", + editorComponent: "components/editorComponent", // ------ 下面写的都不要直接用,而是使用上面的封装版本 ------ //富文本编辑器simditor -> editor @@ -153,7 +154,7 @@ }, { name: "submissionList_21_pack" - } + }, ], optimizeCss: "standard", }) \ No newline at end of file diff --git a/template/src/admin/contest/add_contest.html b/template/src/admin/contest/add_contest.html index 54d16d8..ce8fd14 100644 --- a/template/src/admin/contest/add_contest.html +++ b/template/src/admin/contest/add_contest.html @@ -17,8 +17,7 @@
- -

请填写比赛描述

+
@@ -39,7 +38,7 @@
- +
@@ -70,30 +69,12 @@   {{ el.name }}
-
- -
- - -
-
-
- -
- -
-
-
+ +
diff --git a/template/src/admin/contest/contest_list.html b/template/src/admin/contest/contest_list.html index cf027c7..0a1eeb4 100644 --- a/template/src/admin/contest/contest_list.html +++ b/template/src/admin/contest/contest_list.html @@ -6,7 +6,7 @@
- +

@@ -38,10 +38,10 @@
- 页数:{{ page }}/{{ totalPage }}   - - +
+ + +
  {{ el.name }}
@@ -186,6 +186,8 @@
+ --> + diff --git a/template/src/oj/account/oauth.html b/template/src/oj/account/oauth.html deleted file mode 100644 index 2713d19..0000000 --- a/template/src/oj/account/oauth.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "oj_base.html" %} -{% block title %} - 授权登录 -{% endblock %} -{% block body %} -
-
- {% if request.user.is_authenticated %} -

3秒钟后将跳转到{{ callback }}

- - {% else %} - - {% endif %} - - -
-
-{% endblock %} -{% block js_block %} - -{% endblock %} \ No newline at end of file diff --git a/template/src/oj/account/sso.html b/template/src/oj/account/sso.html new file mode 100644 index 0000000..23f0445 --- /dev/null +++ b/template/src/oj/account/sso.html @@ -0,0 +1,20 @@ +{% extends "oj_base.html" %} +{% block title %} + 授权登录 +{% endblock %} +{% block body %} +
+
+

3秒钟后将使用账号{{ request.user.username }}登录{{ callback }}

+ + + +
+
+{% endblock %} +{% block js_block %} + +{% endblock %} \ No newline at end of file diff --git a/template/src/oj/account/user_index.html b/template/src/oj/account/user_index.html index 2b2041d..5ab6e44 100644 --- a/template/src/oj/account/user_index.html +++ b/template/src/oj/account/user_index.html @@ -1,90 +1,83 @@ {% extends "oj_base.html" %} {% block title %} - +{{ user.username }}的主页 {% endblock %} {% block body %}
- +
-

virusdefender

+

+ {{ user.username }} + {% ifequal user.admin_type 2 %} + + {% endifequal %} + {% ifequal user.admin_type 1 %} + + {% endifequal %} + +

+

{{ user.userprofile.mood }}

-
+
+ + {% if user.userprofile.blog %}

- https://virusdefender.net + {{ blog_link }}

+ {% endif %} + + {% if user.userprofile.hduoj_username %} +

+ + + {{ user.userprofile.hduoj_username }} + +

+ {% endif %} + + {% if user.userprofile.bestcoder_username %} +

+ + + {{ user.userprofile.bestcoder_username }} + +

+ {% endif %} + + {% if user.userprofile.codeforces_username %} +

+ + + {{ user.userprofile.codeforces_username }} + +

+ {% endif %}

- - https://virusdefender.net + + {{ user.create_time }}

- -

- - https://virusdefender.net -

- -

- - https://virusdefender.net -

- - -

2015-9-10

+
+
+ {{ user.userprofile.rank }} + Rank +
+
+ {{ user.userprofile.accepted_number }} + AC +
+
+ {{ user.userprofile.submissions_number }} + Submissions +
+
- - - -
-
-

正在做的题

- -
-

- problem title - 3 / 10 - Accepted -

- -

- problem title - 3 / 10 - Accepted -

- -

- problem title - 3 / 10 - Accepted -

- -

- problem title - 3 / 10 - Accepted -

- -
- -
-
-
-
-

分享的代码

-
Panel content
-
-
+
-{% endblock %} -{% block js_block %} - {% endblock %} \ No newline at end of file diff --git a/template/src/oj/contest/contest_rank.html b/template/src/oj/contest/contest_rank.html index b14eb28..0236365 100644 --- a/template/src/oj/contest/contest_rank.html +++ b/template/src/oj/contest/contest_rank.html @@ -40,7 +40,7 @@ 用时 + 罚时 {% for item in contest_problems %} - {{ item.sort_index }} + {{ item.sort_index }} {% endfor %} @@ -48,7 +48,7 @@ {% for item in rank %} - {{ forloop.counter|add:paging_info.offset}} + {% if item.total_ac_number %}{{ forloop.counter|add:paging_info.offset}}{% else %}-{% endif %} {{ item.user__username }} diff --git a/template/src/oj/contest/submissions_list.html b/template/src/oj/contest/submissions_list.html index 3646c24..354ce1d 100644 --- a/template/src/oj/contest/submissions_list.html +++ b/template/src/oj/contest/submissions_list.html @@ -107,14 +107,14 @@