增加部分基础组件
This commit is contained in:
@@ -6,7 +6,7 @@ import functools
|
|||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from utils.shortcuts import error_response, error_page
|
from utils.shortcuts import error_response, error_page, redirect_to_login
|
||||||
from .models import AdminType
|
from .models import AdminType
|
||||||
|
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ class BasePermissionDecorator(object):
|
|||||||
if self.request.is_ajax():
|
if self.request.is_ajax():
|
||||||
return error_response(_("Please login in first"))
|
return error_response(_("Please login in first"))
|
||||||
else:
|
else:
|
||||||
return HttpResponseRedirect("/login/?__from=" + urllib.quote(self.request.path))
|
return redirect_to_login(self.request)
|
||||||
|
|
||||||
def check_permission(self):
|
def check_permission(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
@@ -47,9 +47,11 @@ class login_required(BasePermissionDecorator):
|
|||||||
|
|
||||||
class super_admin_required(BasePermissionDecorator):
|
class super_admin_required(BasePermissionDecorator):
|
||||||
def check_permission(self):
|
def check_permission(self):
|
||||||
return self.request.user.is_authenticated() and self.request.user.admin_type == AdminType.SUPER_ADMIN
|
return self.request.user.is_authenticated() and \
|
||||||
|
self.request.user.admin_type == AdminType.SUPER_ADMIN
|
||||||
|
|
||||||
|
|
||||||
class admin_required(BasePermissionDecorator):
|
class admin_required(BasePermissionDecorator):
|
||||||
def check_permission(self):
|
def check_permission(self):
|
||||||
return self.request.user.is_authenticated() and self.request.user.admin_type in [AdminType.SUPER_ADMIN, AdminType.ADMIN]
|
return self.request.user.is_authenticated() and \
|
||||||
|
self.request.user.admin_type in [AdminType.SUPER_ADMIN, AdminType.ADMIN]
|
||||||
|
|||||||
@@ -3,27 +3,24 @@ import time
|
|||||||
import json
|
import json
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.contrib import auth
|
|
||||||
|
|
||||||
from .models import AdminType, User
|
from utils.shortcuts import redirect_to_login
|
||||||
|
from .models import AdminType
|
||||||
|
|
||||||
|
|
||||||
# todo remove this
|
|
||||||
from django.contrib import auth
|
|
||||||
|
|
||||||
class SessionSecurityMiddleware(object):
|
class SessionSecurityMiddleware(object):
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
if request.user.is_authenticated() and request.user.admin_type in [AdminType.ADMIN, AdminType.SUPER_ADMIN]:
|
if request.user.is_authenticated() and request.user.admin_type in [AdminType.ADMIN, AdminType.SUPER_ADMIN]:
|
||||||
if "last_activity" in request.session:
|
if "last_activity" in request.session:
|
||||||
# 24 hours passwd since last visit
|
# 24 hours passed since last visit
|
||||||
if time.time() - request.session["last_activity"] >= 24 * 60 * 60:
|
if time.time() - request.session["last_activity"] >= 24 * 60 * 60:
|
||||||
auth.logout(request)
|
auth.logout(request)
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
return HttpResponse(json.dumps({"code": 1, "data": _("Please login in first")}),
|
return HttpResponse(json.dumps({"code": 1, "data": _("Please login in first")}),
|
||||||
content_type="application/json")
|
content_type="application/json")
|
||||||
else:
|
else:
|
||||||
return HttpResponseRedirect("/login/?__from=" + urllib.quote(request.path))
|
return redirect_to_login(request)
|
||||||
# 更新最后活动日期
|
# 更新最后活动日期
|
||||||
request.session["last_activity"] = time.time()
|
request.session["last_activity"] = time.time()
|
||||||
|
|||||||
@@ -1,38 +1,35 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
import os
|
|
||||||
import codecs
|
|
||||||
import qrcode
|
|
||||||
import StringIO
|
import StringIO
|
||||||
|
import codecs
|
||||||
|
import os
|
||||||
|
|
||||||
from django import http
|
import qrcode
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.shortcuts import render
|
from django.core.exceptions import MultipleObjectsReturned
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.conf import settings
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.http import HttpResponse
|
from django.shortcuts import render
|
||||||
from django.core.exceptions import MultipleObjectsReturned
|
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from rest_framework.views import APIView
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from utils.shortcuts import (serializer_invalid_response, error_response,
|
|
||||||
success_response, error_page, paginate, rand_str)
|
|
||||||
from utils.captcha import Captcha
|
from utils.captcha import Captcha
|
||||||
from utils.otp_auth import OtpAuth
|
from utils.otp_auth import OtpAuth
|
||||||
|
from utils.shortcuts import (serializer_invalid_response, error_response,
|
||||||
from .tasks import _send_email
|
success_response, error_page, paginate, rand_str)
|
||||||
from .decorators import login_required
|
from .decorators import login_required
|
||||||
from .models import User, UserProfile, AdminExtraPermission, AdminType
|
from .decorators import super_admin_required
|
||||||
|
from .models import User, UserProfile, AdminType
|
||||||
from .serializers import (UserLoginSerializer, UserRegisterSerializer,
|
from .serializers import (UserLoginSerializer, UserRegisterSerializer,
|
||||||
UserChangePasswordSerializer,
|
UserChangePasswordSerializer,
|
||||||
UserSerializer, EditUserSerializer,
|
UserSerializer, EditUserSerializer,
|
||||||
ApplyResetPasswordSerializer, ResetPasswordSerializer,
|
ApplyResetPasswordSerializer, ResetPasswordSerializer,
|
||||||
SSOSerializer, EditUserProfileSerializer,
|
SSOSerializer, EditUserProfileSerializer,
|
||||||
TwoFactorAuthCodeSerializer)
|
TwoFactorAuthCodeSerializer)
|
||||||
from .decorators import super_admin_required
|
from .tasks import _send_email
|
||||||
|
|
||||||
|
|
||||||
class UserLoginAPIView(APIView):
|
class UserLoginAPIView(APIView):
|
||||||
@@ -223,7 +220,7 @@ class UserAdminAPIView(APIView):
|
|||||||
|
|
||||||
def logout(request):
|
def logout(request):
|
||||||
auth.logout(request)
|
auth.logout(request)
|
||||||
return http.HttpResponseRedirect("/")
|
return HttpResponseRedirect("/")
|
||||||
|
|
||||||
|
|
||||||
def index_page(request):
|
def index_page(request):
|
||||||
@@ -233,7 +230,7 @@ def index_page(request):
|
|||||||
if request.META.get('HTTP_REFERER') or request.GET.get("index"):
|
if request.META.get('HTTP_REFERER') or request.GET.get("index"):
|
||||||
return render(request, "oj/index.html")
|
return render(request, "oj/index.html")
|
||||||
else:
|
else:
|
||||||
return http.HttpResponseRedirect('/problems/')
|
return HttpResponseRedirect('/problems/')
|
||||||
|
|
||||||
|
|
||||||
class UsernameCheckAPIView(APIView):
|
class UsernameCheckAPIView(APIView):
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<app></app>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"ora": "^0.2.0",
|
"ora": "^0.2.0",
|
||||||
"shelljs": "^0.6.0",
|
"shelljs": "^0.6.0",
|
||||||
|
"simditor-markdown": "^1.1.2",
|
||||||
"url-loader": "^0.5.7",
|
"url-loader": "^0.5.7",
|
||||||
"vue-hot-reload-api": "^1.2.0",
|
"vue-hot-reload-api": "^1.2.0",
|
||||||
"vue-html-loader": "^1.0.0",
|
"vue-html-loader": "^1.0.0",
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
"webpack": "^1.12.2",
|
"webpack": "^1.12.2",
|
||||||
"webpack-dev-middleware": "^1.4.0",
|
"webpack-dev-middleware": "^1.4.0",
|
||||||
"webpack-hot-middleware": "^2.6.0",
|
"webpack-hot-middleware": "^2.6.0",
|
||||||
"webpack-merge": "^0.8.3"
|
"webpack-merge": "^0.8.3",
|
||||||
|
"webuploader": "^0.1.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import "../../static/css/bootstrap.css";
|
@import "../../static/css/bootstrap.css";
|
||||||
@import "../../static/css/todc-bootstrap.css";
|
@import "../../static/css/todc-bootstrap.css";
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<back url="/user"></back>
|
<back></back>
|
||||||
<h3>修改用户信息</h3>
|
<h3>修改用户信息</h3>
|
||||||
<form v-on:submit="submit">
|
<form v-on:submit="submit">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
this.request({
|
this.request({
|
||||||
url: "/api/admin/user/?user_id=" + this.$route.params["userId"],
|
url: "/api/admin/user/?user_id=" + this.$route.params["userId"],
|
||||||
method: "GET",
|
method: "GET",
|
||||||
success:(data)=> {
|
success: (data)=> {
|
||||||
this.user = data.data;
|
this.user = data.data;
|
||||||
for (var p of data.data.admin_extra_permission) {
|
for (var p of data.data.admin_extra_permission) {
|
||||||
if (this.userPermissionNum2Str[p]) {
|
if (this.userPermissionNum2Str[p]) {
|
||||||
|
|||||||
19
frontend/admin/src/components/problem/problem.vue
Normal file
19
frontend/admin/src/components/problem/problem.vue
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<simditor editorid="problem_detail"></simditor>
|
||||||
|
|
||||||
|
<uploader uploaderid="uploader"></uploader>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import simditor from "../utils/simditor.vue"
|
||||||
|
import uploader from "../utils/uploader.vue"
|
||||||
|
|
||||||
|
export default({
|
||||||
|
components: {
|
||||||
|
simditor,
|
||||||
|
uploader
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -39,10 +39,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
"showModal":(content) => {
|
"showModal": (content) => {
|
||||||
this.showModal = true;
|
this.showModal = true;
|
||||||
this.content = true;
|
this.content = true;
|
||||||
setTimeout(()=>{
|
setTimeout(()=> {
|
||||||
this.showModal = false;
|
this.showModal = false;
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
adminNav: [{}, {}]
|
adminNav: [{}, {}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
25
frontend/admin/src/components/utils/simditor.vue
Normal file
25
frontend/admin/src/components/utils/simditor.vue
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<textarea id="{{ editorid }}"></textarea>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import Simditor from 'simditor'
|
||||||
|
import SimditorMakrdown from "simditor-markdown"
|
||||||
|
|
||||||
|
export default{
|
||||||
|
props: ["editorid"],
|
||||||
|
attached() {
|
||||||
|
var self = this;
|
||||||
|
var editor = new Simditor({
|
||||||
|
textarea: document.getElementById(self.editorid),
|
||||||
|
upload: {url: "/", fileKey: "file"},
|
||||||
|
toolbar: ['bold', 'italic', 'underline', 'color', 'image', 'ol', 'ul', 'markdown']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@import "../../../../static/css/simditor.css";
|
||||||
|
@import "../../../../static/css/simditor-markdown.css";
|
||||||
|
@import "../../../../static/css/webuploader.css";
|
||||||
|
</style>
|
||||||
52
frontend/admin/src/components/utils/uploader.vue
Normal file
52
frontend/admin/src/components/utils/uploader.vue
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<div id="{{ uploaderid }}">
|
||||||
|
<div class="btns">
|
||||||
|
<div id="picker"> picker</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import WebUploader from "webuploader"
|
||||||
|
|
||||||
|
export default ({
|
||||||
|
props: {
|
||||||
|
uploaderid: {
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
uploadpath: {
|
||||||
|
required: false,
|
||||||
|
default: "/server"
|
||||||
|
},
|
||||||
|
accept: {
|
||||||
|
required: false,
|
||||||
|
default(){
|
||||||
|
return {
|
||||||
|
title: 'Images',
|
||||||
|
extensions: 'gif,jpg,jpeg,bmp,png',
|
||||||
|
mimeTypes: 'image/*'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
attached() {
|
||||||
|
var self = this;
|
||||||
|
var uploader = WebUploader.create({
|
||||||
|
dnd: '#' + self.uploaderid,
|
||||||
|
runtimeOrder: "html5",
|
||||||
|
server: self.uploadpath,
|
||||||
|
pick: '#' + self.uploaderid,
|
||||||
|
resize: false,
|
||||||
|
auto: true,
|
||||||
|
accept: self.accept
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -7,6 +7,8 @@ import locale from "./locales"
|
|||||||
|
|
||||||
import userList from "./components/account/userList.vue"
|
import userList from "./components/account/userList.vue"
|
||||||
import editUser from "./components/account/editUser.vue"
|
import editUser from "./components/account/editUser.vue"
|
||||||
|
import problem from "./components/problem/problem.vue"
|
||||||
|
import uploader from "./components/utils/uploader.vue"
|
||||||
|
|
||||||
|
|
||||||
var request = {
|
var request = {
|
||||||
@@ -83,6 +85,10 @@ router.map({
|
|||||||
"/user/edit/:userId": {
|
"/user/edit/:userId": {
|
||||||
name: "editUser",
|
name: "editUser",
|
||||||
component: editUser
|
component: editUser
|
||||||
|
},
|
||||||
|
"/problem/create": {
|
||||||
|
name: "createProblem",
|
||||||
|
component: problem
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
28
frontend/static/css/simditor-markdown.css
Normal file
28
frontend/static/css/simditor-markdown.css
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
.simditor .markdown-editor {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.simditor .markdown-editor textarea {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 200px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 22px 15px 40px;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid #dfdfdf;
|
||||||
|
resize: none;
|
||||||
|
outline: none;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.simditor.simditor-markdown .markdown-editor {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.simditor.simditor-markdown .simditor-body {
|
||||||
|
min-height: 100px;
|
||||||
|
background: #f3f3f3;
|
||||||
|
}
|
||||||
|
.simditor.simditor-markdown .simditor-placeholder {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.simditor .simditor-toolbar .toolbar-item.toolbar-item-markdown .simditor-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
746
frontend/static/css/simditor.css
Normal file
746
frontend/static/css/simditor.css
Normal file
File diff suppressed because one or more lines are too long
28
frontend/static/css/webuploader.css
Normal file
28
frontend/static/css/webuploader.css
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
.webuploader-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.webuploader-element-invisible {
|
||||||
|
position: absolute !important;
|
||||||
|
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
|
||||||
|
clip: rect(1px,1px,1px,1px);
|
||||||
|
}
|
||||||
|
.webuploader-pick {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #00b7ee;
|
||||||
|
padding: 10px 15px;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 3px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.webuploader-pick-hover {
|
||||||
|
background: #00a2d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.webuploader-pick-disable {
|
||||||
|
opacity: 0.6;
|
||||||
|
pointer-events:none;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
import os
|
import os
|
||||||
import hashlib
|
import hashlib
|
||||||
import time
|
|
||||||
import random
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
@@ -125,3 +124,7 @@ def build_query_string(kv_data, ignore_none=True):
|
|||||||
query_string = "?"
|
query_string = "?"
|
||||||
query_string += (k + "=" + str(v))
|
query_string += (k + "=" + str(v))
|
||||||
return query_string
|
return query_string
|
||||||
|
|
||||||
|
|
||||||
|
def redirect_to_login(request):
|
||||||
|
return HttpResponseRedirect("/login/?__from=" + urllib.quote(request.path))
|
||||||
Reference in New Issue
Block a user