Merge branch 'dev' into hohoTT-dev

This commit is contained in:
hohoTT 2015-08-05 20:12:59 +08:00
commit c0d8d900cd
14 changed files with 376 additions and 177 deletions

View File

@ -1,3 +1,14 @@
from django.shortcuts import render # coding=utf-8
from django.conf import settings
from django.http import HttpResponse, Http404
# Create your views here. from rest_framework.views import APIView
class AdminTemplateView(APIView):
def get(self, request, template_dir, template_name):
path = settings.TEMPLATE_DIRS[0] + "/admin/" + template_dir + "/" + template_name + ".html"
try:
return HttpResponse(open(path).read(), content_type="text/html")
except IOError:
raise Http404

View File

@ -6,11 +6,12 @@ from django.views.generic import TemplateView
from account.views import UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView, \ from account.views import UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView, \
EmailCheckAPIView EmailCheckAPIView
from announcement.views import AnnouncementAPIView from announcement.views import AnnouncementAPIView
from admin.views import AdminTemplateView
urlpatterns = [ urlpatterns = [
url("^$", TemplateView.as_view(template_name="oj/index.html"), name="index_page"), url("^$", TemplateView.as_view(template_name="oj/index.html"), name="index_page"),
url(r'^docs/', include('rest_framework_swagger.urls')), url(r'^docs/', include('rest_framework_swagger.urls')),
url(r'^admin/$', TemplateView.as_view(template_name="admin/index.html"), name="admin_index_page"), url(r'^admin/$', TemplateView.as_view(template_name="admin/admin.html"), name="admin_spa_page"),
url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"), url(r'^login/$', TemplateView.as_view(template_name="oj/account/login.html"), name="user_login_page"),
url(r'^register/$', TemplateView.as_view(template_name="oj/account/register.html"), name="user_register_page"), url(r'^register/$', TemplateView.as_view(template_name="oj/account/register.html"), name="user_register_page"),
url(r'^change_password/$', TemplateView.as_view(template_name="oj/account/change_password.html"), name="user_change_password_page"), url(r'^change_password/$', TemplateView.as_view(template_name="oj/account/change_password.html"), name="user_change_password_page"),
@ -24,4 +25,5 @@ urlpatterns = [
url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), name="add_contest_page"), url(r'^admin/contest/$', TemplateView.as_view(template_name="admin/contest/add_contest.html"), name="add_contest_page"),
url(r'^problems/$', TemplateView.as_view(template_name="oj/problem/problem_list.html"), name="problem_list_page"), url(r'^problems/$', TemplateView.as_view(template_name="oj/problem/problem_list.html"), name="problem_list_page"),
url(r'^admin/template/(?P<template_dir>\w+)/(?P<template_name>\w+).html', AdminTemplateView.as_view(), name="admin_template")
] ]

View File

@ -0,0 +1,31 @@
define("admin", ["jquery", "avalon"], function($, avalon){
function li_active(selector){
$(selector).attr("class", "list-group-item active");
}
function li_inactive(selector){
$(".list-group-item").attr("class", "list-group-item");
}
var hash = window.location.hash.substring(1);
if(hash){
li_active("#li-" + hash);
}else {
li_active("#li-index");
}
window.onhashchange = function() {
var hash = window.location.hash.substring(1);
if(hash){
li_inactive(".list-group-item");
li_active("#li-" + hash);
vm.template_url = "template/index/" + hash + ".html";
}
};
var vm = avalon.define({
$id: "admin",
template_url: "template/index/index.html"
});
});

View File

@ -2,6 +2,11 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf
$("#register-form") $("#register-form")
.formValidation({ .formValidation({
framework: "bootstrap", framework: "bootstrap",
icon: {
valid: 'glyphicon glyphicon-ok',
invalid: 'glyphicon glyphicon-remove',
validating: 'glyphicon glyphicon-refresh'
},
fields: { fields: {
username: { username: {
validators: { validators: {
@ -13,8 +18,10 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf
max: 30, max: 30,
message: '用户名长度必须在3到30位之间' message: '用户名长度必须在3到30位之间'
}, },
usernameCheck:{ remote: {
message: '用户名已存在' message: "用户名已存在",
url: "/api/username_check/",
field: 'username'
} }
} }
}, },
@ -50,6 +57,21 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf
message: "两次输入的密码必须一致" message: "两次输入的密码必须一致"
} }
} }
},
email: {
validators: {
notEmpty: {
message: "请填写电子邮箱邮箱地址"
},
emailAddress: {
message: "请填写有效的邮箱地址"
},
remote: {
message: "您已经注册过了",
url: "/api/email_check/",
field: 'email'
}
}
} }
} }
} }
@ -72,8 +94,6 @@ require(["jquery", "bs_alert", "csrf", "validation"], function($, bs_alert, csrf
bs_alert(data.data); bs_alert(data.data);
} }
} }
}) })
}); });
}); });

View File

@ -15,6 +15,7 @@ var require = {
submit_code: "app/oj/problem/submit_code", submit_code: "app/oj/problem/submit_code",
contest: "app/admin/contest/contest", contest: "app/admin/contest/contest",
csrf: "utils/csrf", csrf: "utils/csrf",
admin: "app/admin/admin",
//formValidation 不要在代码中单独使用而是使用和修改utils/validation //formValidation 不要在代码中单独使用而是使用和修改utils/validation
base: "lib/formValidation/base", base: "lib/formValidation/base",
@ -26,8 +27,9 @@ var require = {
"validator/date": "lib/formValidation/validator/date", "validator/date": "lib/formValidation/validator/date",
"validator/integer": "lib/formValidation/validator/integer", "validator/integer": "lib/formValidation/validator/integer",
"validator/between": "lib/formValidation/validator/between", "validator/between": "lib/formValidation/validator/between",
'validator/confirm':"lib/formValidation/validator/confirm", "validator/confirm":"lib/formValidation/validator/confirm",
"validator/usernameCheck":"lib/formValidation/validator/usernameCheck", "validator/remote":"lib/formValidation/validator/remote",
"validator/emailAddress":"lib/formValidation/validator/emailAddress",
//富文本编辑器 不要直接使用而是使用上面的editor //富文本编辑器 不要直接使用而是使用上面的editor
simditor: "lib/simditor/simditor", simditor: "lib/simditor/simditor",
"simple-module": "lib/simditor/module", "simple-module": "lib/simditor/module",

View File

@ -1,146 +1,46 @@
/** /**
* remote validator * remote validator
*
* @link http://formvalidation.io/validators/remote/
* @author https://twitter.com/nghuuphuoc
* @copyright (c) 2013 - 2015 Nguyen Huu Phuoc
* @license http://formvalidation.io/license/
*/ */
(function(root, factory) { (function(root, factory) {
"use strict"; "use strict";
// AMD module is defined // AMD module is defined
if (typeof define === "function" && define.amd) { if (typeof define === "function" && define.amd) {
define("validator/remote", ["jquery", "base"], factory); define("validator/remote", ["jquery", "base", "csrf"], factory);
} else { } else {
// planted over the root! // planted over the root!
factory(root.jQuery, root.FormValidation); factory(root.jQuery, root.FormValidation);
} }
}(this, function ($, FormValidation, csrfHeader) {
}(this, function ($, FormValidation) {
FormValidation.I18n = $.extend(true, FormValidation.I18n || {}, { FormValidation.I18n = $.extend(true, FormValidation.I18n || {}, {
'en_US': { 'en_US': {
remote: { remote: {
'default': 'Please enter a valid value' 'default': ''
} }
} }
}); });
FormValidation.Validator.remote = { FormValidation.Validator.remote = {
html5Attributes: {
message: 'message',
name: 'name',
type: 'type',
url: 'url',
data: 'data',
delay: 'delay'
},
/**
* Destroy the timer when destroying the bootstrapValidator (using validator.destroy() method)
*/
destroy: function(validator, $field, options) {
var ns = validator.getNamespace(),
timer = $field.data(ns + '.remote.timer');
if (timer) {
clearTimeout(timer);
$field.removeData(ns + '.remote.timer');
}
},
/**
* Request a remote server to check the input value
*
* @param {FormValidation.Base} validator Plugin instance
* @param {jQuery} $field Field element
* @param {Object} options Can consist of the following keys:
* - url {String|Function}
* - type {String} [optional] Can be GET or POST (default)
* - data {Object|Function} [optional]: By default, it will take the value
* {
* <fieldName>: <fieldValue>
* }
* - delay
* - name {String} [optional]: Override the field name for the request.
* - message: The invalid message
* - headers: Additional headers
* @returns {Deferred}
*/
validate: function(validator, $field, options) { validate: function(validator, $field, options) {
var ns = validator.getNamespace(), var dfd = new $.Deferred(), ajaxData = {};
value = validator.getFieldValue($field, 'remote'), ajaxData[options.field] = $field.val();
dfd = new $.Deferred(); if ($field.val() === '')
if (value === '') { return true;
dfd.resolve($field, 'remote', { valid: true }); var url = options.url;
return dfd; var xhr = $.ajax({
} beforeSend: csrfHeader,
url: url,
var name = $field.attr('data-' + ns + '-field'), dataType: 'json',
data = options.data || {}, data: ajaxData,
url = options.url, method: "post"
type = options.type || 'GET', });
headers = options.headers || {}; xhr.success(function(response) {
dfd.resolve($field, 'remote',{valid:!response.data, message:options.msg});
// Support dynamic data })
if ('function' === typeof data) { .error(function(response) {
data = data.call(this, validator); dfd.resolve($field, 'remote', {valid: false});
} });
return dfd;
// Parse string data from HTML5 attribute
if ('string' === typeof data) {
data = JSON.parse(data);
}
// Support dynamic url
if ('function' === typeof url) {
url = url.call(this, validator);
}
data[options.name || name] = value;
function runCallback() {
var xhr = $.ajax({
type: type,
headers: headers,
url: url,
dataType: 'json',
data: data
});
xhr
.success(function(response) {
response.valid = response.valid === true || response.valid === 'true';
dfd.resolve($field, 'remote', response);
})
.error(function(response) {
dfd.resolve($field, 'remote', {
valid: false
});
});
dfd.fail(function() {
xhr.abort();
});
return dfd;
}
if (options.delay) {
// Since the form might have multiple fields with the same name
// I have to attach the timer to the field element
if ($field.data(ns + '.remote.timer')) {
clearTimeout($field.data(ns + '.remote.timer'));
}
$field.data(ns + '.remote.timer', setTimeout(runCallback, options.delay));
return dfd;
} else {
return runCallback();
}
} }
}; };
return FormValidation.Validator.remote;
})); }));

View File

@ -0,0 +1,146 @@
/**
* remote validator
*
* @link http://formvalidation.io/validators/remote/
* @author https://twitter.com/nghuuphuoc
* @copyright (c) 2013 - 2015 Nguyen Huu Phuoc
* @license http://formvalidation.io/license/
*/
(function(root, factory) {
"use strict";
// AMD module is defined
if (typeof define === "function" && define.amd) {
define("validator/remote", ["jquery", "base"], factory);
} else {
// planted over the root!
factory(root.jQuery, root.FormValidation);
}
}(this, function ($, FormValidation) {
FormValidation.I18n = $.extend(true, FormValidation.I18n || {}, {
'en_US': {
remote: {
'default': 'Please enter a valid value'
}
}
});
FormValidation.Validator.remote = {
html5Attributes: {
message: 'message',
name: 'name',
type: 'type',
url: 'url',
data: 'data',
delay: 'delay'
},
/**
* Destroy the timer when destroying the bootstrapValidator (using validator.destroy() method)
*/
destroy: function(validator, $field, options) {
var ns = validator.getNamespace(),
timer = $field.data(ns + '.remote.timer');
if (timer) {
clearTimeout(timer);
$field.removeData(ns + '.remote.timer');
}
},
/**
* Request a remote server to check the input value
*
* @param {FormValidation.Base} validator Plugin instance
* @param {jQuery} $field Field element
* @param {Object} options Can consist of the following keys:
* - url {String|Function}
* - type {String} [optional] Can be GET or POST (default)
* - data {Object|Function} [optional]: By default, it will take the value
* {
* <fieldName>: <fieldValue>
* }
* - delay
* - name {String} [optional]: Override the field name for the request.
* - message: The invalid message
* - headers: Additional headers
* @returns {Deferred}
*/
validate: function(validator, $field, options) {
var ns = validator.getNamespace(),
value = validator.getFieldValue($field, 'remote'),
dfd = new $.Deferred();
if (value === '') {
dfd.resolve($field, 'remote', { valid: true });
return dfd;
}
var name = $field.attr('data-' + ns + '-field'),
data = options.data || {},
url = options.url,
type = options.type || 'GET',
headers = options.headers || {};
// Support dynamic data
if ('function' === typeof data) {
data = data.call(this, validator);
}
// Parse string data from HTML5 attribute
if ('string' === typeof data) {
data = JSON.parse(data);
}
// Support dynamic url
if ('function' === typeof url) {
url = url.call(this, validator);
}
data[options.name || name] = value;
function runCallback() {
var xhr = $.ajax({
type: type,
headers: headers,
url: url,
dataType: 'json',
data: data
});
xhr
.success(function(response) {
response.valid = response.valid === true || response.valid === 'true';
dfd.resolve($field, 'remote', response);
})
.error(function(response) {
dfd.resolve($field, 'remote', {
valid: false
});
});
dfd.fail(function() {
xhr.abort();
});
return dfd;
}
if (options.delay) {
// Since the form might have multiple fields with the same name
// I have to attach the timer to the field element
if ($field.data(ns + '.remote.timer')) {
clearTimeout($field.data(ns + '.remote.timer'));
}
$field.data(ns + '.remote.timer', setTimeout(runCallback, options.delay));
return dfd;
} else {
return runCallback();
}
}
};
return FormValidation.Validator.remote;
}));

View File

@ -1,37 +0,0 @@
/**
* usernameCheck validator
*/
(function(root, factory) {
"use strict";
// AMD module is defined
if (typeof define === "function" && define.amd) {
define("validator/usernameCheck", ["jquery", "base", "csrf"], factory);
} else {
// planted over the root!
factory(root.jQuery, root.FormValidation);
}
}(this, function ($, FormValidation, csrfHeader) {
FormValidation.I18n = $.extend(true, FormValidation.I18n || {}, {
'en_US': {
usernameCheck: {
'default': 'Please input the same value'
}
}
});
FormValidation.Validator.usernameCheck = {
validate: function(validator, $field, options) {
if ($field.val() == '')
return true;
return !$.ajax({
async: false,
beforeSend: csrfHeader,
url: "/api/username_check/",
data: {username: $field.val()},
dataType: "json",
method: "post",
}).responseJSON.data;
}
};
}));

View File

@ -9,6 +9,7 @@ define("validation",
'validator/integer', 'validator/integer',
'validator/between', 'validator/between',
'validator/confirm', 'validator/confirm',
'validator/usernameCheck'], 'validator/remote',
'validator/emailAddress'],
function () { function () {
}); });

122
template/admin/admin.html Normal file
View File

@ -0,0 +1,122 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="renderer" content="webkit">
<title>在线评测系统 - 后台管理</title>
<!-- custom css begin -->
{% block css_block %}{% endblock %}
<!-- custom css end -->
<!-- global css begin -->
<link href="/static/css/admin.css" rel="stylesheet">
<!-- global css end -->
</head>
<body>
<!-- nav begin -->
<nav class="navbar navbar-masthead navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
aria-expanded="false" aria-controls="navbar">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">qduoj admin</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">主页</a></li>
<li><a href="#about">题目</a></li>
<li><a href="#contact">提交</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">
李扬
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">我的提交</a></li>
<li><a href="#">我的资料</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">退出</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<!-- nav end -->
<!--browser happy begin -->
<!--[if lt IE 9]>
<div class="alert alert-danger text-center" role="alert">
当前网页 <strong>不支持</strong> 你正在使用的浏览器. 为了正常的访问, 请 <a href="http://browsehappy.com/">升级你的浏览器</a>.
</div>
<![endif]-->
<!-- browser happy end -->
<div class="container" ms-controller="admin">
<div class="row">
<!-- admin left begin-->
<div class="col-md-2">
<ul class="list-group">
<li class="list-group-header">List header</li>
<li class="list-group-item" id="li-index"><a href="#index">主页</a></li>
<li class="list-group-item" id="li-announcement"><a href="#announcement">公告</a></li>
<li class="list-group-item"><a href="#">Applications</a></li>
<li class="list-group-header">Another list header</li>
<li class="list-group-item"><a href="#">Help</a></li>
</ul>
</div>
<!-- admin left end -->
<!-- custom body begin -->
<div ms-include-src="template_url"></div>
<!-- custom body end -->
</div>
</div>
<div class="modal fade" id="modal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title">提示</h4>
</div>
<div class="modal-body">
<p id="modal-text"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<script src="/static/js/config.js"></script>
<script src="/static/js/require.js"></script>
<script>
require(["bootstrap", "admin"]);
</script>
<!-- footer begin -->
<div class="footer">
<p class="text-muted text-center">Copyright © 2015 青岛大学信息工程学院 创新实验室</p>
</div>
<!-- footer end -->
</body>
</html>

View File

@ -1,4 +0,0 @@
{% extends "admin_base.html" %}
{% block body %}
Hello world
{% endblock %}

View File

@ -0,0 +1 @@
<h1>Hello world</h1>

View File

@ -65,14 +65,14 @@
<![endif]--> <![endif]-->
<!-- browser happy end --> <!-- browser happy end -->
<div class="container"> <div class="container" ms-controller="admin">
<div class="row"> <div class="row">
<!-- admin left begin--> <!-- admin left begin-->
<div class="col-md-2"> <div class="col-md-2">
<ul class="list-group"> <ul class="list-group">
<li class="list-group-header">List header</li> <li class="list-group-header">List header</li>
<li class="list-group-item active"><a href="#">Home</a></li> <li class="list-group-item" id="li-index"><a href="#index">主页</a></li>
<li class="list-group-item"><a href="#">Library</a></li> <li class="list-group-item" id="li-announcement"><a href="#announcement">公告</a></li>
<li class="list-group-item"><a href="#">Applications</a></li> <li class="list-group-item"><a href="#">Applications</a></li>
<li class="list-group-header">Another list header</li> <li class="list-group-header">Another list header</li>
<li class="list-group-item"><a href="#">Help</a></li> <li class="list-group-item"><a href="#">Help</a></li>
@ -108,7 +108,7 @@
<script src="/static/js/config.js"></script> <script src="/static/js/config.js"></script>
<script src="/static/js/require.js"></script> <script src="/static/js/require.js"></script>
<script> <script>
require(["bootstrap"]); require(["bootstrap", "admin"]);
</script> </script>
{% block js_block %}{% endblock %} {% block js_block %}{% endblock %}
<!-- footer begin --> <!-- footer begin -->

View File

@ -14,6 +14,10 @@
<label for="real_name">真实姓名</label> <label for="real_name">真实姓名</label>
<input type="text" class="form-control input-lg" id="real_name" name="real_name" placeholder="真实姓名"> <input type="text" class="form-control input-lg" id="real_name" name="real_name" placeholder="真实姓名">
</div> </div>
<div class="form-group">
<label for="email">邮箱地址</label>
<input type="email" class="form-control input-lg" id="email" name="email" placeholder="邮箱地址">
</div>
<div class="form-group"> <div class="form-group">
<label for="password">密码</label> <label for="password">密码</label>
<input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码"> <input type="password" class="form-control input-lg" id="password" name="password" placeholder="密码">