Merge branch 'dev' into new-arch

* dev:
  mail 拆分模块
  修改错误提示措辞
  精简重置密码邮件模板
  添加重置密码以后的跳转到登录页面
  如果比赛已经开始,就不再显示之前测试题目的提交
  admin 添加和编辑比赛页面增加提示
  修改一些细节问题
  添加重置密码页面和js
  修改申请重置密码页面及js名称
  添加重置密码api url,调整url
  修改申请找回用户登录信息的api逻辑,没有用户名也可申请
  添加找回用户信息功能,修改邮件模板的一些细节
  去掉申请重置密码的服务中要求填写用户名,因为有很多用户不记得用户名了
  添加重置密码页面的url,并在用户登录页面添加url
  添加重置密码页面的url
  修改检测邮箱api使其可以被重置密码页面使用
  update read
  增加访问首页的参数
  修改首页样式;增加背景图片

Conflicts:
	oj/settings.py
This commit is contained in:
virusdefender
2015-12-09 11:06:20 +08:00
26 changed files with 346 additions and 123 deletions

View File

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

View File

@@ -50,7 +50,6 @@ class EditUserSerializer(serializers.Serializer):
class ApplyResetPasswordSerializer(serializers.Serializer): class ApplyResetPasswordSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30)
email = serializers.EmailField() email = serializers.EmailField()
captcha = serializers.CharField(max_length=4, min_length=4) captcha = serializers.CharField(max_length=4, min_length=4)

View File

@@ -14,7 +14,7 @@ from rest_framework.response import Response
from utils.shortcuts import (serializer_invalid_response, error_response, from utils.shortcuts import (serializer_invalid_response, error_response,
success_response, error_page, paginate, rand_str) success_response, error_page, paginate, rand_str)
from utils.captcha import Captcha from utils.captcha import Captcha
from mail.tasks import send_email from utils.mail import send_email
from .decorators import login_required from .decorators import login_required
from .models import User, UserProfile from .models import User, UserProfile
@@ -62,10 +62,9 @@ def index_page(request):
if not request.user.is_authenticated(): if not request.user.is_authenticated():
return render(request, "oj/index.html") return render(request, "oj/index.html")
try: if request.META.get('HTTP_REFERER') or request.GET.get("index"):
if request.META['HTTP_REFERER']:
return render(request, "oj/index.html") return render(request, "oj/index.html")
except KeyError: else:
return http.HttpResponseRedirect('/problems/') return http.HttpResponseRedirect('/problems/')
@@ -149,17 +148,27 @@ class UsernameCheckAPIView(APIView):
class EmailCheckAPIView(APIView): class EmailCheckAPIView(APIView):
def get(self, request): 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) email = request.GET.get("email", None)
if email: if email:
try: try:
User.objects.get(email=email) User.objects.get(email=email)
return Response(status=400) return Response(status=existed)
except Exception: except Exception:
return Response(status=200) return Response(status=does_not_existed)
return Response(status=200) return Response(status=does_not_existed)
class UserAdminAPIView(APIView): class UserAdminAPIView(APIView):
@@ -274,7 +283,7 @@ class ApplyResetPasswordAPIView(APIView):
if not captcha.check(data["captcha"]): if not captcha.check(data["captcha"]):
return error_response(u"验证码错误") return error_response(u"验证码错误")
try: try:
user = User.objects.get(username=data["username"], email=data["email"]) user = User.objects.get(email=data["email"])
except User.DoesNotExist: except User.DoesNotExist:
return error_response(u"用户不存在") return error_response(u"用户不存在")
if user.reset_password_token_create_time and (now() - user.reset_password_token_create_time).total_seconds() < 20 * 60: if user.reset_password_token_create_time and (now() - user.reset_password_token_create_time).total_seconds() < 20 * 60:
@@ -286,14 +295,14 @@ class ApplyResetPasswordAPIView(APIView):
email_template = email_template.replace("{{ username }}", user.username).\ email_template = email_template.replace("{{ username }}", user.username).\
replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]).\ 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"], send_email(settings.WEBSITE_INFO["website_name"],
user.email, user.email,
user.username, user.username,
settings.WEBSITE_INFO["website_name"] + u" 密码找回邮件", settings.WEBSITE_INFO["website_name"] + u" 登录信息找回邮件",
email_template) email_template)
return success_response(u"邮件发送成功") return success_response(u"邮件发送成功,请前往您的邮箱查收")
else: else:
return serializer_invalid_response(serializer) return serializer_invalid_response(serializer)
@@ -354,4 +363,14 @@ class SSOAPIView(APIView):
token = rand_str() token = rand_str()
request.user.auth_token = token request.user.auth_token = token
request.user.save() request.user.save()
return render(request, "oj/account/sso.html", {"redirect_url": callback + "?token=" + token, "callback": callback}) 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

@@ -515,6 +515,10 @@ def contest_problem_submissions_list_page(request, contest_id, page=1):
values("id", "contest_id", "problem_id", "result", "create_time", values("id", "contest_id", "problem_id", "result", "create_time",
"accepted_answer_time", "language", "user_id").order_by("-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) user_id = request.GET.get("user_id", None)
if user_id: if user_id:
submissions = submissions.filter(user_id=request.GET.get("user_id")) submissions = submissions.filter(user_id=request.GET.get("user_id"))

View File

@@ -1 +0,0 @@
# coding=utf-8

View File

@@ -1 +0,0 @@
# coding=utf-8

View File

@@ -1,19 +0,0 @@
# coding=utf-8
import os
from envelopes import Envelope
SMTP_CONFIG = {"smtp_server": "smtp.mxhichina.com",
"email": "noreply@qduoj.com",
"password": os.environ.get("smtp_password", "111111"),
"tls": False}
def send_email(from_name, to_email, to_name, subject, content):
envelope = Envelope(from_addr=(SMTP_CONFIG["email"], from_name),
to_addr=(to_email, to_name),
subject=subject,
html_body=content)
envelope.send(SMTP_CONFIG["smtp_server"],
login=SMTP_CONFIG["email"],
password=SMTP_CONFIG["password"],
tls=SMTP_CONFIG["tls"])

View File

@@ -55,7 +55,6 @@ INSTALLED_APPS = (
'admin', 'admin',
'submission', 'submission',
'contest', 'contest',
'mail',
'judge', 'judge',
'judge_dispatcher', 'judge_dispatcher',
@@ -125,7 +124,6 @@ AUTH_USER_MODEL = 'account.User'
LOG_PATH = "log/" LOG_PATH = "log/"
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
'disable_existing_loggers': True, 'disable_existing_loggers': True,
@@ -191,9 +189,10 @@ TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_case/')
IMAGE_UPLOAD_DIR = os.path.join(BASE_DIR, 'upload/') IMAGE_UPLOAD_DIR = os.path.join(BASE_DIR, 'upload/')
WEBSITE_INFO = {"website_name": "qduoj", WEBSITE_INFO = {"website_name": "qduoj",
"website_footer": u"青岛大学信息工程学院 创新实验室", "website_footer": u"青岛大学信息工程学院 创新实验室 <a href=\"http://www.miibeian.gov.cn/\">京ICP备15062075号-1</a>",
"url": "https://qduoj.com"} "url": "https://qduoj.com"}
HUEY = { HUEY = {
'backend': 'huey.backends.redis_backend', 'backend': 'huey.backends.redis_backend',
'name': 'task_queue', 'name': 'task_queue',
@@ -201,4 +200,11 @@ HUEY = {
'always_eager': False, # Defaults to False when running via manage.py run_huey 'always_eager': False, # Defaults to False when running via manage.py run_huey
# Options to pass into the consumer when running ``manage.py run_huey`` # Options to pass into the consumer when running ``manage.py run_huey``
'consumer_options': {'workers': 50}, 'consumer_options': {'workers': 50},
} }
SMTP_CONFIG = {"smtp_server": "smtp.mxhichina.com",
"email": "noreply@qduoj.com",
"password": os.environ.get("smtp_password", "111111"),
"tls": False}

View File

@@ -5,7 +5,7 @@ from django.views.generic import TemplateView
from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView,
UserChangePasswordAPIView, EmailCheckAPIView, UserChangePasswordAPIView, EmailCheckAPIView,
UserAdminAPIView, UserInfoAPIView, UserAdminAPIView, UserInfoAPIView, ResetPasswordAPIView,
ApplyResetPasswordAPIView, SSOAPIView, UserProfileAPIView) ApplyResetPasswordAPIView, SSOAPIView, UserProfileAPIView)
from announcement.views import AnnouncementAdminAPIView from announcement.views import AnnouncementAdminAPIView
@@ -122,12 +122,14 @@ urlpatterns = [
url(r'^user/(?P<username>.+)/$', "account.views.user_index_page"), url(r'^user/(?P<username>.+)/$', "account.views.user_index_page"),
url(r'^api/reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"), url(r'^api/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/$', TemplateView.as_view(template_name="oj/account/settings.html"), name="account_setting_page"),
url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"), url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"),
url(r'^account/sso/$', SSOAPIView.as_view(), name="sso_api"), url(r'^account/sso/$', SSOAPIView.as_view(), name="sso_api"),
url('^api/account/userprofile/$', UserProfileAPIView.as_view(), name="userprofile_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")
] ]

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

@@ -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,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

@@ -57,25 +57,28 @@
problem_2_pack: "app/oj/problem/problem", problem_2_pack: "app/oj/problem/problem",
submissionList_3_pack: "app/admin/problem/submissionList", submissionList_3_pack: "app/admin/problem/submissionList",
contestCountdown_4_pack: "app/oj/contest/contestCountdown", contestCountdown_4_pack: "app/oj/contest/contestCountdown",
addProblem_5_pack: "app/admin/problem/addProblem", avatar_5_pack: "app/oj/account/avatar",
problem_6_pack: "app/admin/problem/problem", addProblem_6_pack: "app/admin/problem/addProblem",
contestList_7_pack: "app/admin/contest/contestList", problem_7_pack: "app/admin/problem/problem",
admin_8_pack: "app/admin/admin", contestList_8_pack: "app/admin/contest/contestList",
login_9_pack: "app/oj/account/login", admin_9_pack: "app/admin/admin",
addContest_10_pack: "app/admin/contest/addContest", login_10_pack: "app/oj/account/login",
contestPassword_11_pack: "app/oj/contest/contestPassword", applyResetPassword_11_pack: "app/oj/account/applyResetPassword",
changePassword_12_pack: "app/oj/account/changePassword", addContest_12_pack: "app/admin/contest/addContest",
monitor_13_pack: "app/admin/monitor/monitor", contestPassword_13_pack: "app/oj/contest/contestPassword",
editProblem_14_pack: "app/admin/contest/editProblem", changePassword_14_pack: "app/oj/account/changePassword",
joinGroupRequestList_15_pack: "app/admin/group/joinGroupRequestList", monitor_15_pack: "app/admin/monitor/monitor",
group_16_pack: "app/oj/group/group", editProblem_16_pack: "app/admin/contest/editProblem",
contestProblemList_17_pack: "app/admin/contest/contestProblemList", joinGroupRequestList_17_pack: "app/admin/group/joinGroupRequestList",
editProblem_18_pack: "app/admin/problem/editProblem", group_18_pack: "app/oj/group/group",
register_19_pack: "app/oj/account/register", contestProblemList_19_pack: "app/admin/contest/contestProblemList",
groupDetail_20_pack: "app/admin/group/groupDetail", editProblem_20_pack: "app/admin/problem/editProblem",
editContest_21_pack: "app/admin/contest/editContest", register_21_pack: "app/oj/account/register",
group_22_pack: "app/admin/group/group", groupDetail_22_pack: "app/admin/group/groupDetail",
settings_23_pack: "app/oj/account/settings" editContest_23_pack: "app/admin/contest/editContest",
resetPassword_24_pack: "app/oj/account/resetPassword",
group_25_pack: "app/admin/group/group",
settings_26_pack: "app/oj/account/settings"
}, },
shim: { shim: {
avalon: { avalon: {
@@ -86,12 +89,6 @@
appDir: "../", appDir: "../",
dir: "../../release/", dir: "../../release/",
modules: [ modules: [
{
name: "bootstrap",
},
{
name: "codeMirror"
},
{ {
name: "announcement_0_pack" name: "announcement_0_pack"
}, },
@@ -108,62 +105,71 @@
name: "contestCountdown_4_pack" name: "contestCountdown_4_pack"
}, },
{ {
name: "addProblem_5_pack" name: "avatar_5_pack"
}, },
{ {
name: "problem_6_pack" name: "addProblem_6_pack"
}, },
{ {
name: "contestList_7_pack" name: "problem_7_pack"
}, },
{ {
name: "admin_8_pack" name: "contestList_8_pack"
}, },
{ {
name: "login_9_pack" name: "admin_9_pack"
}, },
{ {
name: "addContest_10_pack" name: "login_10_pack"
}, },
{ {
name: "contestPassword_11_pack" name: "applyResetPassword_11_pack"
}, },
{ {
name: "changePassword_12_pack" name: "addContest_12_pack"
}, },
{ {
name: "monitor_13_pack" name: "contestPassword_13_pack"
}, },
{ {
name: "editProblem_14_pack" name: "changePassword_14_pack"
}, },
{ {
name: "joinGroupRequestList_15_pack" name: "monitor_15_pack"
}, },
{ {
name: "group_16_pack" name: "editProblem_16_pack"
}, },
{ {
name: "contestProblemList_17_pack" name: "joinGroupRequestList_17_pack"
}, },
{ {
name: "editProblem_18_pack" name: "group_18_pack"
}, },
{ {
name: "register_19_pack" name: "contestProblemList_19_pack"
}, },
{ {
name: "groupDetail_20_pack" name: "editProblem_20_pack"
}, },
{ {
name: "editContest_21_pack" name: "register_21_pack"
}, },
{ {
name: "group_22_pack" name: "groupDetail_22_pack"
}, },
{ {
name: "settings_23_pack" name: "editContest_23_pack"
}, },
{
name: "resetPassword_24_pack"
},
{
name: "group_25_pack"
},
{
name: "settings_26_pack"
}
], ],
optimizeCss: "standard", optimizeCss: "standard",
}) })

View File

@@ -59,25 +59,28 @@ var require = {
problem_2_pack: "app/oj/problem/problem", problem_2_pack: "app/oj/problem/problem",
submissionList_3_pack: "app/admin/problem/submissionList", submissionList_3_pack: "app/admin/problem/submissionList",
contestCountdown_4_pack: "app/oj/contest/contestCountdown", contestCountdown_4_pack: "app/oj/contest/contestCountdown",
addProblem_5_pack: "app/admin/problem/addProblem", avatar_5_pack: "app/oj/account/avatar",
problem_6_pack: "app/admin/problem/problem", addProblem_6_pack: "app/admin/problem/addProblem",
contestList_7_pack: "app/admin/contest/contestList", problem_7_pack: "app/admin/problem/problem",
admin_8_pack: "app/admin/admin", contestList_8_pack: "app/admin/contest/contestList",
login_9_pack: "app/oj/account/login", admin_9_pack: "app/admin/admin",
addContest_10_pack: "app/admin/contest/addContest", login_10_pack: "app/oj/account/login",
contestPassword_11_pack: "app/oj/contest/contestPassword", applyResetPassword_11_pack: "app/oj/account/applyResetPassword",
changePassword_12_pack: "app/oj/account/changePassword", addContest_12_pack: "app/admin/contest/addContest",
monitor_13_pack: "app/admin/monitor/monitor", contestPassword_13_pack: "app/oj/contest/contestPassword",
editProblem_14_pack: "app/admin/contest/editProblem", changePassword_14_pack: "app/oj/account/changePassword",
joinGroupRequestList_15_pack: "app/admin/group/joinGroupRequestList", monitor_15_pack: "app/admin/monitor/monitor",
group_16_pack: "app/oj/group/group", editProblem_16_pack: "app/admin/contest/editProblem",
contestProblemList_17_pack: "app/admin/contest/contestProblemList", joinGroupRequestList_17_pack: "app/admin/group/joinGroupRequestList",
editProblem_18_pack: "app/admin/problem/editProblem", group_18_pack: "app/oj/group/group",
register_19_pack: "app/oj/account/register", contestProblemList_19_pack: "app/admin/contest/contestProblemList",
groupDetail_20_pack: "app/admin/group/groupDetail", editProblem_20_pack: "app/admin/problem/editProblem",
editContest_21_pack: "app/admin/contest/editContest", register_21_pack: "app/oj/account/register",
group_22_pack: "app/admin/group/group", groupDetail_22_pack: "app/admin/group/groupDetail",
settings_23_pack: "app/oj/account/settings" editContest_23_pack: "app/admin/contest/editContest",
resetPassword_24_pack: "app/oj/account/resetPassword",
group_25_pack: "app/admin/group/group",
settings_26_pack: "app/oj/account/settings",
}, },
shim: { shim: {
avalon: { avalon: {

View File

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

View File

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

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

@@ -36,7 +36,7 @@
<div class="form-group"> <div class="form-group">
<label for="confirm_password">确认密码</label> <label for="confirm_password">确认密码</label>
<input type="password" class="form-control input-lg" id="confirm_password" name="confirm_password" <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> placeholder="确认密码" maxlength="30" data-match="#new_password" data-match-error="两密码不一致" required>
<div class="help-block with-errors"></div> <div class="help-block with-errors"></div>
</div> </div>

View File

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

View File

@@ -1,10 +1,53 @@
<!DOCTYPE html> {% extends "oj_base.html" %}
<html> {% block title %}
<head lang="en"> 找回登录信息
<meta charset="UTF-8"> {% endblock %}
<title></title> {% block body %}
</head> <div class="container main">
<body> <div class="col-md-6 col-md-offset-3">
<h2 class="text-center">找回登录信息</h2><br>
<br>
</body> <form id="login-form">
</html> <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

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

View File

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

15
utils/mail.py Normal file
View File

@@ -0,0 +1,15 @@
# coding=utf-8
from envelopes import Envelope
from django.conf import settings
def send_email(from_name, to_email, to_name, subject, content):
envelope = Envelope(from_addr=(settings.SMTP_CONFIG["email"], from_name),
to_addr=(to_email, to_name),
subject=subject,
html_body=content)
envelope.send(settings.SMTP_CONFIG["smtp_server"],
login=settings.SMTP_CONFIG["email"],
password=settings.SMTP_CONFIG["password"],
tls=settings.SMTP_CONFIG["tls"])