Menci
7 years ago
14 changed files with 822 additions and 29 deletions
@ -0,0 +1,347 @@ |
|||||||
|
/* |
||||||
|
* This file is part of SYZOJ. |
||||||
|
* |
||||||
|
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com> |
||||||
|
* |
||||||
|
* SYZOJ is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* SYZOJ is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public |
||||||
|
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
let Problem = syzoj.model('problem'); |
||||||
|
let JudgeState = syzoj.model('judge_state'); |
||||||
|
let Article = syzoj.model('article'); |
||||||
|
let Contest = syzoj.model('contest'); |
||||||
|
let User = syzoj.model('user'); |
||||||
|
let UserPrivilege = syzoj.model('user_privilege'); |
||||||
|
|
||||||
|
let db = syzoj.db; |
||||||
|
|
||||||
|
app.get('/admin/info', async (req, res) => { |
||||||
|
try { |
||||||
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
||||||
|
|
||||||
|
let allSubmissionsCount = await JudgeState.count(); |
||||||
|
let todaySubmissionsCount = await JudgeState.count({ submit_time: { $gte: syzoj.utils.getCurrentDate(true) } }); |
||||||
|
let problemsCount = await Problem.count(); |
||||||
|
let articlesCount = await Article.count(); |
||||||
|
let contestsCount = await Contest.count(); |
||||||
|
let usersCount = await User.count(); |
||||||
|
|
||||||
|
res.render('admin_info', { |
||||||
|
allSubmissionsCount: allSubmissionsCount, |
||||||
|
todaySubmissionsCount: todaySubmissionsCount, |
||||||
|
problemsCount: problemsCount, |
||||||
|
articlesCount: articlesCount, |
||||||
|
contestsCount: contestsCount, |
||||||
|
usersCount: usersCount |
||||||
|
}); |
||||||
|
} catch (e) { |
||||||
|
syzoj.log(e); |
||||||
|
res.render('error', { |
||||||
|
err: e |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
let configItems = { |
||||||
|
'title': { name: '站点标题', type: String }, |
||||||
|
'邮箱验证': null, |
||||||
|
'register_mail.enabled': { name: '启用', type: Boolean }, |
||||||
|
'register_mail.address': { name: '发件人地址', type: String }, |
||||||
|
'register_mail.key': { name: '密钥', type: String }, |
||||||
|
'默认参数': null, |
||||||
|
'default.problem.time_limit': { name: '时间限制(单位:ms)', type: Number }, |
||||||
|
'default.problem.memory_limit': { name: '空间限制(单位:MiB)', type: Number }, |
||||||
|
'限制': null, |
||||||
|
'limit.time_limit': { name: '最大时间限制(单位:ms)', type: Number }, |
||||||
|
'limit.memory_limit': { name: '最大空间限制(单位:MiB)', type: Number }, |
||||||
|
'limit.data_size': { name: '所有数据包大小(单位:byte)', type: Number }, |
||||||
|
'limit.testdata': { name: '测试数据大小(单位:byte)', type: Number }, |
||||||
|
'limit.submit_code': { name: '代码长度(单位:byte)', type: Number }, |
||||||
|
'limit.submit_answer': { name: '提交答案题目答案大小(单位:byte)', type: Number }, |
||||||
|
'limit.custom_test_input': { name: '自定义测试输入文件大小(单位:byte)', type: Number }, |
||||||
|
'limit.testdata_filecount': { name: '测试数据文件数量(单位:byte)', type: Number }, |
||||||
|
'每页显示数量': null, |
||||||
|
'page.problem': { name: '题库', type: Number }, |
||||||
|
'page.judge_state': { name: '提交记录', type: Number }, |
||||||
|
'page.problem_statistics': { name: '题目统计', type: Number }, |
||||||
|
'page.ranklist': { name: '排行榜', type: Number }, |
||||||
|
'page.discussion': { name: '讨论', type: Number }, |
||||||
|
'page.article_comment': { name: '评论', type: Number }, |
||||||
|
'page.contest': { name: '比赛', type: Number }, |
||||||
|
'编译器版本': null, |
||||||
|
'languages.cpp.version': { name: 'C++', type: String }, |
||||||
|
'languages.cpp11.version': { name: 'C++11', type: String }, |
||||||
|
'languages.csharp.version': { name: 'C#', type: String }, |
||||||
|
'languages.c.version': { name: 'C', type: String }, |
||||||
|
'languages.vala.version': { name: 'Vala', type: String }, |
||||||
|
'languages.java.version': { name: 'Java', type: String }, |
||||||
|
'languages.pascal.version': { name: 'Pascal', type: String }, |
||||||
|
'languages.lua.version': { name: 'Lua', type: String }, |
||||||
|
'languages.luajit.version': { name: 'LuaJIT', type: String }, |
||||||
|
'languages.python2.version': { name: 'Python 2', type: String }, |
||||||
|
'languages.python3.version': { name: 'Python 3', type: String }, |
||||||
|
'languages.nodejs.version': { name: 'Node.js', type: String }, |
||||||
|
'languages.ruby.version': { name: 'Ruby', type: String }, |
||||||
|
'languages.haskell.version': { name: 'Haskell', type: String }, |
||||||
|
'languages.ocaml.version': { name: 'OCaml', type: String }, |
||||||
|
'languages.vbnet.version': { name: 'Visual Basic', type: String } |
||||||
|
}; |
||||||
|
|
||||||
|
app.get('/admin/config', async (req, res) => { |
||||||
|
try { |
||||||
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
||||||
|
|
||||||
|
for (let i in configItems) { |
||||||
|
if (!configItems[i]) continue; |
||||||
|
configItems[i].val = eval(`syzoj.config.${i}`); |
||||||
|
} |
||||||
|
|
||||||
|
res.render('admin_config', { |
||||||
|
items: configItems |
||||||
|
}); |
||||||
|
} catch (e) { |
||||||
|
syzoj.log(e); |
||||||
|
res.render('error', { |
||||||
|
err: e |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
app.post('/admin/config', async (req, res) => { |
||||||
|
try { |
||||||
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
||||||
|
|
||||||
|
for (let i in configItems) { |
||||||
|
if (!configItems[i]) continue; |
||||||
|
if (req.body[i]) { |
||||||
|
let val; |
||||||
|
if (configItems[i].type === Boolean) { |
||||||
|
val = req.body[i] === 'on'; |
||||||
|
} else { |
||||||
|
val = req.body[i]; |
||||||
|
} |
||||||
|
|
||||||
|
let f = new Function('val', `syzoj.config.${i} = val`); |
||||||
|
f(val); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
await syzoj.utils.saveConfig(); |
||||||
|
|
||||||
|
res.redirect(syzoj.utils.makeUrl(['admin', 'config'])); |
||||||
|
} catch (e) { |
||||||
|
syzoj.log(e); |
||||||
|
res.render('error', { |
||||||
|
err: e |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
app.get('/admin/privilege', async (req, res) => { |
||||||
|
try { |
||||||
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
||||||
|
|
||||||
|
let a = await UserPrivilege.query(); |
||||||
|
let users = {}; |
||||||
|
for (let p of a) { |
||||||
|
if (!users[p.user_id]) { |
||||||
|
users[p.user_id] = { |
||||||
|
user: await User.fromID(p.user_id), |
||||||
|
privileges: [] |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
users[p.user_id].privileges.push(p.privilege); |
||||||
|
} |
||||||
|
|
||||||
|
res.render('admin_privilege', { |
||||||
|
users: Object.values(users) |
||||||
|
}); |
||||||
|
} catch (e) { |
||||||
|
syzoj.log(e); |
||||||
|
res.render('error', { |
||||||
|
err: e |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
app.post('/admin/privilege', async (req, res) => { |
||||||
|
try { |
||||||
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
||||||
|
|
||||||
|
let data = JSON.parse(req.body.data); |
||||||
|
for (let id in data) { |
||||||
|
let user = await User.fromID(id); |
||||||
|
if (!user) throw new ErrorMessage(`不存在 ID 为 ${id} 的用户。`); |
||||||
|
await user.setPrivileges(data[id]); |
||||||
|
} |
||||||
|
|
||||||
|
res.redirect(syzoj.utils.makeUrl(['admin', 'privilege'])); |
||||||
|
} catch (e) { |
||||||
|
syzoj.log(e); |
||||||
|
res.render('error', { |
||||||
|
err: e |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
app.get('/admin/rejudge', async (req, res) => { |
||||||
|
try { |
||||||
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
||||||
|
|
||||||
|
res.render('admin_rejudge', { |
||||||
|
form: {}, |
||||||
|
count: null |
||||||
|
}); |
||||||
|
} catch (e) { |
||||||
|
syzoj.log(e); |
||||||
|
res.render('error', { |
||||||
|
err: e |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
app.post('/admin/rejudge', async (req, res) => { |
||||||
|
try { |
||||||
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
||||||
|
|
||||||
|
let user = await User.fromName(req.body.submitter || ''); |
||||||
|
let where = {}; |
||||||
|
if (user) where.user_id = user.id; |
||||||
|
else if (req.body.submitter) where.user_id = -1; |
||||||
|
|
||||||
|
let minID = parseInt(req.body.min_id); |
||||||
|
if (isNaN(minID)) minID = 0; |
||||||
|
let maxID = parseInt(req.body.max_id); |
||||||
|
if (isNaN(maxID)) maxID = 2147483647; |
||||||
|
|
||||||
|
where.id = { |
||||||
|
$and: { |
||||||
|
$gte: parseInt(minID), |
||||||
|
$lte: parseInt(maxID) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
let minScore = parseInt(req.body.min_score); |
||||||
|
if (isNaN(minScore)) minScore = 0; |
||||||
|
let maxScore = parseInt(req.body.max_score); |
||||||
|
if (isNaN(maxScore)) maxScore = 100; |
||||||
|
|
||||||
|
where.score = { |
||||||
|
$and: { |
||||||
|
$gte: parseInt(minScore), |
||||||
|
$lte: parseInt(maxScore) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
let minTime = syzoj.utils.parseDate(req.body.min_time); |
||||||
|
if (isNaN(minTime)) minTime = 0; |
||||||
|
let maxTime = syzoj.utils.parseDate(req.body.max_time); |
||||||
|
if (isNaN(maxTime)) maxTime = 2147483647; |
||||||
|
|
||||||
|
where.submit_time = { |
||||||
|
$and: { |
||||||
|
$gte: parseInt(minTime), |
||||||
|
$lte: parseInt(maxTime) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
if (req.body.language) { |
||||||
|
if (req.body.language === 'submit-answer') where.language = ''; |
||||||
|
else where.language = req.body.language; |
||||||
|
} |
||||||
|
if (req.body.status) where.status = { $like: req.body.status + '%' }; |
||||||
|
if (req.body.problem_id) where.problem_id = parseInt(req.body.problem_id) || -1; |
||||||
|
|
||||||
|
let count = await JudgeState.count(where); |
||||||
|
if (req.body.type === 'rejudge') { |
||||||
|
let submissions = await JudgeState.query(null, where); |
||||||
|
for (let submission of submissions) { |
||||||
|
await submission.rejudge(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
res.render('admin_rejudge', { |
||||||
|
form: req.body, |
||||||
|
count: count |
||||||
|
}); |
||||||
|
} catch (e) { |
||||||
|
syzoj.log(e); |
||||||
|
res.render('error', { |
||||||
|
err: e |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
app.get('/admin/links', async (req, res) => { |
||||||
|
try { |
||||||
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
||||||
|
|
||||||
|
res.render('admin_links', { |
||||||
|
links: syzoj.config.links || [] |
||||||
|
}); |
||||||
|
} catch (e) { |
||||||
|
syzoj.log(e); |
||||||
|
res.render('error', { |
||||||
|
err: e |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
app.post('/admin/links', async (req, res) => { |
||||||
|
try { |
||||||
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
||||||
|
|
||||||
|
syzoj.config.links = JSON.parse(req.body.data); |
||||||
|
await syzoj.utils.saveConfig(); |
||||||
|
|
||||||
|
res.redirect(syzoj.utils.makeUrl(['admin', 'links'])); |
||||||
|
} catch (e) { |
||||||
|
syzoj.log(e); |
||||||
|
res.render('error', { |
||||||
|
err: e |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
app.get('/admin/raw', async (req, res) => { |
||||||
|
try { |
||||||
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
||||||
|
|
||||||
|
res.render('admin_raw', { |
||||||
|
data: JSON.stringify(syzoj.config, null, 2) |
||||||
|
}); |
||||||
|
} catch (e) { |
||||||
|
syzoj.log(e); |
||||||
|
res.render('error', { |
||||||
|
err: e |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
app.post('/admin/raw', async (req, res) => { |
||||||
|
try { |
||||||
|
if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); |
||||||
|
|
||||||
|
syzoj.config = JSON.parse(req.body.data); |
||||||
|
await syzoj.utils.saveConfig(); |
||||||
|
|
||||||
|
res.redirect(syzoj.utils.makeUrl(['admin', 'raw'])); |
||||||
|
} catch (e) { |
||||||
|
syzoj.log(e); |
||||||
|
res.render('error', { |
||||||
|
err: e |
||||||
|
}) |
||||||
|
} |
||||||
|
}); |
@ -1,25 +0,0 @@ |
|||||||
<% include header %> |
|
||||||
<div class="ui grid"> |
|
||||||
<div class="four wide column"> |
|
||||||
<div class="ui vertical fluid tabular menu"> |
|
||||||
<a class="item"> |
|
||||||
; |
|
||||||
</a> |
|
||||||
<a class="item"> |
|
||||||
Pics |
|
||||||
</a> |
|
||||||
<a class="item"> |
|
||||||
Companies |
|
||||||
</a> |
|
||||||
<a class="item active"> |
|
||||||
Links |
|
||||||
</a> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<div class="twelve wide stretched column"> |
|
||||||
<div class="ui segment"> |
|
||||||
This is an stretched grid column. This segment will always match the tab height |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<% include footer %> |
|
@ -0,0 +1,54 @@ |
|||||||
|
<% this.adminPage = 'config'; %> |
||||||
|
<% include admin_header %> |
||||||
|
<form method="post" class="ui form"> |
||||||
|
<% |
||||||
|
function showTitle(title) { |
||||||
|
%><h4 class="ui dividing header"><%= title %></h4><% |
||||||
|
} |
||||||
|
|
||||||
|
function showStringItem(name, text, val) { |
||||||
|
%> |
||||||
|
<div class="ui fluid input inline field"> |
||||||
|
<label style="line-height: 37.8px; font-weight: normal; "><%= text %></label> |
||||||
|
<input type="text" name="<%= name %>" value="<%= val %>"> |
||||||
|
</div> |
||||||
|
<% |
||||||
|
} |
||||||
|
|
||||||
|
function showNumberItem(name, text, val) { |
||||||
|
%> |
||||||
|
<div class="ui fluid input inline field"> |
||||||
|
<label style="line-height: 37.8px; font-weight: normal; "><%= text %></label> |
||||||
|
<input type="number" name="<%= name %>" value="<%= val %>"> |
||||||
|
</div> |
||||||
|
<% |
||||||
|
} |
||||||
|
|
||||||
|
function showBooleanItem(name, text, val) { |
||||||
|
%> |
||||||
|
<div class="inline field"> |
||||||
|
<div class="ui toggle checkbox"> |
||||||
|
<input name="<%= name %>" type="checkbox" class=""<% if (val) { %> checked<% } %>> |
||||||
|
<label><%= text %></label> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<% |
||||||
|
} |
||||||
|
|
||||||
|
for (let item in items) { |
||||||
|
if (items[item] === null) { |
||||||
|
showTitle(item); |
||||||
|
} else if (items[item].type === String) { |
||||||
|
showStringItem(item, items[item].name, items[item].val); |
||||||
|
} else if (items[item].type === Number) { |
||||||
|
showNumberItem(item, items[item].name, items[item].val); |
||||||
|
} else { |
||||||
|
showBooleanItem(item, items[item].name, items[item].val); |
||||||
|
} |
||||||
|
} |
||||||
|
%> |
||||||
|
<div style="text-align: center; "> |
||||||
|
<button class="ui blue button">提交</button> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
<% include admin_footer %> |
@ -0,0 +1,24 @@ |
|||||||
|
<% |
||||||
|
let items = { |
||||||
|
info: '统计信息', |
||||||
|
config: '系统配置', |
||||||
|
privilege: '权限管理', |
||||||
|
rejudge: '一键重测', |
||||||
|
links: '友链管理', |
||||||
|
raw: '配置文件' |
||||||
|
}; |
||||||
|
%> |
||||||
|
<% this.title = items[this.adminPage] + ' - 后台管理'; %> |
||||||
|
<% include header %> |
||||||
|
<h1 style="text-align: center; margin-bottom: 27px; ">后台管理</h1> |
||||||
|
<div class="ui grid"> |
||||||
|
<div class="three wide column"> |
||||||
|
<div class="ui vertical fluid tabular menu" style="height: 100%; "> |
||||||
|
<% for (let x in items) { %> |
||||||
|
<a class="item<% if (this.adminPage === x) { %> active<% } %>"<% if (this.adminPage !== x) { %> href="<%= syzoj.utils.makeUrl(['admin', x]) %>"<% } %>> |
||||||
|
<%= items[x] %> |
||||||
|
</a> |
||||||
|
<% } %> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="thirteen wide stretched column"> |
@ -0,0 +1,79 @@ |
|||||||
|
<% this.adminPage = 'info'; %> |
||||||
|
<% include admin_header %> |
||||||
|
<style> |
||||||
|
.ui.statistics { |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.statistic { |
||||||
|
width: calc(100% / 3 - 3em); |
||||||
|
} |
||||||
|
|
||||||
|
.statistic .icon { |
||||||
|
font-size: 0.7em; |
||||||
|
display: inline-block; |
||||||
|
vertical-align: top; |
||||||
|
padding-top: 2px; |
||||||
|
} |
||||||
|
|
||||||
|
.statistic .label { |
||||||
|
font-weight: normal !important; |
||||||
|
} |
||||||
|
</style> |
||||||
|
<div class="ui statistics"> |
||||||
|
<div class="statistic"> |
||||||
|
<div class="value"> |
||||||
|
<i class="tasks icon"></i> |
||||||
|
<%= allSubmissionsCount %> |
||||||
|
</div> |
||||||
|
<div class="label"> |
||||||
|
总评测量 |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="statistic"> |
||||||
|
<div class="value"> |
||||||
|
<i class="tasks icon"></i> |
||||||
|
<%= todaySubmissionsCount %> |
||||||
|
</div> |
||||||
|
<div class="label"> |
||||||
|
今日评测量 |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="statistic"> |
||||||
|
<div class="value"> |
||||||
|
<i class="list icon"></i> |
||||||
|
<%= problemsCount %> |
||||||
|
</div> |
||||||
|
<div class="label"> |
||||||
|
题目数量 |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="statistic"> |
||||||
|
<div class="value"> |
||||||
|
<i class="comments icon"></i> |
||||||
|
<%= articlesCount %> |
||||||
|
</div> |
||||||
|
<div class="label"> |
||||||
|
讨论数量 |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="statistic"> |
||||||
|
<div class="value"> |
||||||
|
<i class="calendar icon"></i> |
||||||
|
<%= contestsCount %> |
||||||
|
</div> |
||||||
|
<div class="label"> |
||||||
|
比赛数量 |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="statistic"> |
||||||
|
<div class="value"> |
||||||
|
<i class="user icon"></i> |
||||||
|
<%= usersCount %> |
||||||
|
</div> |
||||||
|
<div class="label"> |
||||||
|
用户数量 |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<% include admin_footer %> |
@ -0,0 +1,78 @@ |
|||||||
|
<% |
||||||
|
this.adminPage = 'links'; |
||||||
|
%> |
||||||
|
<% include admin_header %> |
||||||
|
<table class="ui center aligned celled table"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th>名称</th> |
||||||
|
<th>链接</th> |
||||||
|
<th width="50px">删除</th> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
|
||||||
|
<tbody> |
||||||
|
<% for (let i = 0; i < links.length; i++) { %> |
||||||
|
<tr> |
||||||
|
<td><%= links[i].title %></td> |
||||||
|
<td><a href="<%= links[i].url %>"><%= links[i].url %></a></td> |
||||||
|
<td> |
||||||
|
<a onclick="$('#modal-remove-<%= i %>').modal('show');" style="color: #000; " href="#"><i class="remove icon"></i></a> |
||||||
|
<div class="ui basic modal" id="modal-remove-<%= i %>"> |
||||||
|
<div class="ui icon header"> |
||||||
|
<i class="remove icon"></i> |
||||||
|
<p style="margin-top: 15px; ">删除友链</p> |
||||||
|
</div> |
||||||
|
<div class="content" style="text-align: center; "> |
||||||
|
<p>确认删除该友链吗?</p> |
||||||
|
</div> |
||||||
|
<div class="actions"> |
||||||
|
<div class="ui red basic cancel inverted button"> |
||||||
|
<i class="remove icon"></i> |
||||||
|
否 |
||||||
|
</div> |
||||||
|
<a class="ui green ok inverted button remove_link" data-id="<%= i %>"> |
||||||
|
<i class="checkmark icon"></i> |
||||||
|
是 |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
<% } %> |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
|
||||||
|
<div class="ui form"> |
||||||
|
<div class="two fields"> |
||||||
|
<div class="field"> |
||||||
|
<label>名称</label> |
||||||
|
<input type="text" id="add_title"> |
||||||
|
</div> |
||||||
|
<div class="field"> |
||||||
|
<label>链接</label> |
||||||
|
<input type="text" id="add_url"> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div style="text-align: center; "><div id="add_button" class="ui submit button">添加</a></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<form method="post" id="submit_form"> |
||||||
|
<input type="hidden" name="data" id="submit_data"> |
||||||
|
</form> |
||||||
|
|
||||||
|
<script> |
||||||
|
var links = <%- JSON.stringify(links) %>; |
||||||
|
$('.remove_link').click(function () { |
||||||
|
links.splice($(this).data('id'), 1); |
||||||
|
$('#submit_data').val(JSON.stringify(links)); |
||||||
|
$('#submit_form').submit(); |
||||||
|
}); |
||||||
|
|
||||||
|
$('#add_button').click(function () { |
||||||
|
links.push({ title: $('#add_title').val(), url: $('#add_url').val() }); |
||||||
|
$('#submit_data').val(JSON.stringify(links)); |
||||||
|
$('#submit_form').submit(); |
||||||
|
}); |
||||||
|
</script> |
||||||
|
<% include admin_footer %> |
@ -0,0 +1,63 @@ |
|||||||
|
<% |
||||||
|
this.adminPage = 'privilege'; |
||||||
|
let privileges = { |
||||||
|
manage_problem: '管理题目', |
||||||
|
manage_problem_tag: '管理题目标签', |
||||||
|
manage_user: '管理用户', |
||||||
|
}; |
||||||
|
%> |
||||||
|
<% include admin_header %> |
||||||
|
<table class="ui center aligned celled table"> |
||||||
|
<thead> |
||||||
|
<tr> |
||||||
|
<th>ID</th> |
||||||
|
<th>用户名</th> |
||||||
|
<% for (let privilege in privileges) { %> |
||||||
|
<th width="20%"><%= privileges[privilege] %></th> |
||||||
|
<% } %> |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
|
||||||
|
<tbody> |
||||||
|
<% for (let user of users) { %> |
||||||
|
<tr> |
||||||
|
<td><%= user.user.id %></td> |
||||||
|
<td><%= user.user.username %></td> |
||||||
|
<% for (let privilege in privileges) { %> |
||||||
|
<td> |
||||||
|
<div class="ui fitted toggle checkbox checkbox_privilege" data-user="<%= user.user.id %>" data-name="<%= privilege %>"> |
||||||
|
<input type="checkbox"<% if (user.privileges.includes(privilege)) { %> checked<% } %>> |
||||||
|
<label></label> |
||||||
|
</div> |
||||||
|
</td> |
||||||
|
<% } %> |
||||||
|
</tr> |
||||||
|
<% } %> |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
|
||||||
|
<div style="text-align: center; "> |
||||||
|
<button class="ui button" onclick="submit()">提交</button> |
||||||
|
</div> |
||||||
|
|
||||||
|
<form method="post" id="submit_form"> |
||||||
|
<input type="hidden" name="data" id="submit_data"> |
||||||
|
</form> |
||||||
|
|
||||||
|
<script> |
||||||
|
function submit() { |
||||||
|
var users = {}; |
||||||
|
$('.checkbox_privilege').each(function () { |
||||||
|
var user = $(this).data('user'); |
||||||
|
var name = $(this).data('name'); |
||||||
|
if (!users[user]) users[user] = []; |
||||||
|
if ($(this).checkbox('is checked')) { |
||||||
|
users[user].push(name); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
$('#submit_data').val(JSON.stringify(users)); |
||||||
|
$('#submit_form').submit(); |
||||||
|
} |
||||||
|
</script> |
||||||
|
<% include admin_footer %> |
@ -0,0 +1,34 @@ |
|||||||
|
<% this.adminPage = 'raw'; %> |
||||||
|
<% include admin_header %> |
||||||
|
<form method="post" class="ui form"> |
||||||
|
|
||||||
|
<div id="editor" style="border: 1px solid #D4D4D5; height: 500px; margin-bottom: 20px; "><%= data %></div> |
||||||
|
<input type="hidden" name="data"> |
||||||
|
|
||||||
|
<script src="/libs/ace/ace.js"></script> |
||||||
|
<script type="text/javascript"> |
||||||
|
var editor = ace.edit("editor"); |
||||||
|
|
||||||
|
editor.setTheme("ace/theme/tomorrow"); |
||||||
|
editor.getSession().setMode("ace/mode/json"); |
||||||
|
editor.getSession().setUseSoftTabs(false); |
||||||
|
|
||||||
|
editor.container.name = 'data'; |
||||||
|
editor.container.style.lineHeight = 1.6; |
||||||
|
editor.container.style.fontSize = '14px'; |
||||||
|
editor.container.style.fontFamily = "'Roboto Mono', 'Bitstream Vera Sans Mono', 'Menlo', 'Consolas', 'Lucida Console', monospace"; |
||||||
|
editor.setShowPrintMargin(false); |
||||||
|
editor.renderer.updateFontSize(); |
||||||
|
|
||||||
|
var input = $('[name="data"]'); |
||||||
|
editor.getSession().on("change", function () { |
||||||
|
input.val(editor.getSession().getValue()); |
||||||
|
}); |
||||||
|
</script> |
||||||
|
|
||||||
|
|
||||||
|
<div style="text-align: center; "> |
||||||
|
<button class="ui blue button">提交</button> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
<% include admin_footer %> |
@ -0,0 +1,108 @@ |
|||||||
|
<% this.adminPage = 'rejudge'; %> |
||||||
|
<% include admin_header %> |
||||||
|
<form method="post" class="ui form"> |
||||||
|
|
||||||
|
<div class="four fields"> |
||||||
|
<div class="field"> |
||||||
|
<label>题目 ID</label> |
||||||
|
<input type="text" name="problem_id" placeholder="题目 ID" value="<%= form.problem_id %>"> |
||||||
|
</div> |
||||||
|
<div class="field"> |
||||||
|
<label>用户名</label> |
||||||
|
<input type="text" name="submitter" placeholder="用户名" value="<%= form.submitter %>"> |
||||||
|
</div> |
||||||
|
<div class="field"> |
||||||
|
<label>语言</label> |
||||||
|
<div class="ui selection dropdown"> |
||||||
|
<input type="hidden" name="language" value="<%= form.language %>"> |
||||||
|
<i class="dropdown icon"></i> |
||||||
|
<div class="default text"></div> |
||||||
|
<div class="menu"> |
||||||
|
<div class="item" data-value="">不限</div> |
||||||
|
<div class="item" data-value="submit-answer">提交答案</div> |
||||||
|
<% for (let lang in syzoj.config.languages) { %> |
||||||
|
<div class="item" data-value="<%= lang %>"><%= syzoj.config.languages[lang].show %></div> |
||||||
|
<% } %> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="field"> |
||||||
|
<label>状态</label> |
||||||
|
<div class="ui selection dropdown"> |
||||||
|
<input type="hidden" name="status" value="<%= form.status %>"> |
||||||
|
<i class="dropdown icon"></i> |
||||||
|
<div class="default text"></div> |
||||||
|
<div class="menu"> |
||||||
|
<div class="item" data-value="">不限<i class="dropdown icon" style="visibility: hidden; "></i></div> |
||||||
|
<% for (let status in this.icon) { %> |
||||||
|
<% if (this.iconHidden.includes(status)) continue; %> |
||||||
|
<div class="item" data-value="<%= status %>"><span class="status <%= status.toLowerCase().split(' ').join('_') %>"><i class="<%= this.icon[status] %> icon"></i> <%= status %></div> |
||||||
|
<% } %> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="three fields"> |
||||||
|
<div class="field"> |
||||||
|
<label>ID(最小值)</label> |
||||||
|
<input type="text" name="min_id" placeholder="ID(最小值)" value="<%= form.min_id %>"> |
||||||
|
</div> |
||||||
|
<div class="field"> |
||||||
|
<label>分数(最小值)</label> |
||||||
|
<input type="text" name="min_score" placeholder="分数(最小值)" value="<%= form.min_score %>"> |
||||||
|
</div> |
||||||
|
<div class="field"> |
||||||
|
<label>提交时间(最小值)</label> |
||||||
|
<input type="text" name="min_time" placeholder="提交时间(最小值)" value="<%= form.min_time || syzoj.utils.formatDate(0) %>"> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="three fields"> |
||||||
|
<div class="field"> |
||||||
|
<label>ID(最大值)</label> |
||||||
|
<input type="text" name="max_id" placeholder="ID(最大值)" value="<%= form.max_id %>"> |
||||||
|
</div> |
||||||
|
<div class="field"> |
||||||
|
<label>分数(最大值)</label> |
||||||
|
<input type="text" name="max_score" placeholder="分数(最大值)" value="<%= form.max_score %>"> |
||||||
|
</div> |
||||||
|
<div class="field"> |
||||||
|
<label>提交时间(最大值)</label> |
||||||
|
<input type="text" name="max_time" placeholder="提交时间(最大值)" value="<%= form.max_time || syzoj.utils.formatDate(2147483647) %>"> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div style="text-align: center; "> |
||||||
|
<button class="ui button" name="type" value="query">查询</button> |
||||||
|
<% if (form.type === 'rejudge') { %> |
||||||
|
<button class="ui blue disabled button" name="type" value="rejudge">已重测 <%= count %> 条记录</button> |
||||||
|
<% } else if (count !== null) { %> |
||||||
|
<a onclick="$('#modal-rejudge').modal('show');" class="ui blue<% if (count === 0) { %> disabled<% } %> button"><% if (count === 0) { %>没有符合条件的记录<% } else { %>重测 <%= count %> 条记录<% } %></a> |
||||||
|
<button id="submit_rejudge" name="type" value="rejudge" style="display: none; "></button> |
||||||
|
<div class="ui basic modal" id="modal-rejudge"> |
||||||
|
<div class="ui icon header"> |
||||||
|
<i class="refresh icon"></i> |
||||||
|
<p style="margin-top: 15px; ">重新评测</p> |
||||||
|
</div> |
||||||
|
<div class="content" style="text-align: center; "> |
||||||
|
<p>确认重新评测 <%= count %> 条记录吗?</p> |
||||||
|
</div> |
||||||
|
<div class="actions"> |
||||||
|
<div class="ui red basic cancel inverted button"> |
||||||
|
<i class="remove icon"></i> |
||||||
|
否 |
||||||
|
</div> |
||||||
|
<button class="ui green ok inverted button" onclick="$('#submit_rejudge').click()"> |
||||||
|
<i class="checkmark icon"></i> |
||||||
|
是 |
||||||
|
</button> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<% } %> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
<script> |
||||||
|
$(function () { |
||||||
|
$('.ui.dropdown').dropdown(); |
||||||
|
}); |
||||||
|
</script> |
||||||
|
<% include admin_footer %> |
Loading…
Reference in new issue