mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2025-02-17 01:06:41 +00:00
Merge branch 'master' into uoj_form_v2
This commit is contained in:
commit
e2c3a3eb4e
45
.config.php
Normal file
45
.config.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'profile' => [
|
||||||
|
'oj-name' => '石家庄二中信息学在线评测系统',
|
||||||
|
'oj-name-short' => 'S2OJ',
|
||||||
|
'administrator' => 'root',
|
||||||
|
'admin-email' => 'admin@sjzezoj.com',
|
||||||
|
'QQ-group' => '',
|
||||||
|
'ICP-license' => '冀ICP备2020028886号',
|
||||||
|
],
|
||||||
|
'database' => [
|
||||||
|
'database' => 'app_uoj233',
|
||||||
|
'username' => 'root',
|
||||||
|
'password' => 'root',
|
||||||
|
'host' => 'uoj-db',
|
||||||
|
'port' => '3306',
|
||||||
|
],
|
||||||
|
'security' => [
|
||||||
|
'user' => [
|
||||||
|
'client_salt' => 'salt_0',
|
||||||
|
],
|
||||||
|
'cookie' => [
|
||||||
|
'checksum_salt' => ['salt_1', 'salt_2', 'salt_3'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'mail' => [
|
||||||
|
'noreply' => [
|
||||||
|
'username' => 'noreply@local_uoj.ac',
|
||||||
|
'password' => '_mail_noreply_password_',
|
||||||
|
'host' => 'smtp.local_uoj.ac',
|
||||||
|
'secure' => 'tls',
|
||||||
|
'port' => 587,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'judger' => [
|
||||||
|
'socket' => [
|
||||||
|
'port' => '2333',
|
||||||
|
'password' => '_judger_socket_password_'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'switch' => [
|
||||||
|
'blog-domain-mode' => 3,
|
||||||
|
'open-register' => false,
|
||||||
|
],
|
||||||
|
];
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,3 +3,6 @@ uoj_data_1/
|
|||||||
uoj_data_2/
|
uoj_data_2/
|
||||||
.php-cs-fixer.cache
|
.php-cs-fixer.cache
|
||||||
docker-compose.local.yml
|
docker-compose.local.yml
|
||||||
|
.config.php
|
||||||
|
.config.development.php
|
||||||
|
.config.local.php
|
||||||
|
@ -979,6 +979,17 @@ CREATE TABLE `upgrades` (
|
|||||||
|
|
||||||
LOCK TABLES `upgrades` WRITE;
|
LOCK TABLES `upgrades` WRITE;
|
||||||
/*!40000 ALTER TABLE `upgrades` DISABLE KEYS */;
|
/*!40000 ALTER TABLE `upgrades` DISABLE KEYS */;
|
||||||
|
INSERT INTO `upgrades` (`name`, `status`, `updated_at`) VALUES
|
||||||
|
('3_parsedown', 'up', now()),
|
||||||
|
('4_image_hosting', 'up', now()),
|
||||||
|
('6_user_info_v2', 'up', now()),
|
||||||
|
('8_group_v2', 'up', now()),
|
||||||
|
('9_list_v2', 'up', now()),
|
||||||
|
('14_sync_from_uoj.ac', 'up', now()),
|
||||||
|
('16_list_v3', 'up', now()),
|
||||||
|
('18_user_permissions', 'up', now()),
|
||||||
|
('20_problem_difficulty', 'up', now()),
|
||||||
|
('21_problem_difficulty', 'up', now());
|
||||||
/*!40000 ALTER TABLE `upgrades` ENABLE KEYS */;
|
/*!40000 ALTER TABLE `upgrades` ENABLE KEYS */;
|
||||||
UNLOCK TABLES;
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
@ -60,15 +60,6 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./uoj_data/web/data:/var/uoj_data
|
- ./uoj_data/web/data:/var/uoj_data
|
||||||
- ./uoj_data/web/storage:/opt/uoj/web/app/storage
|
- ./uoj_data/web/storage:/opt/uoj/web/app/storage
|
||||||
|
- ./.config.development.php:/opt/uoj/web/app/.config.php
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
environment:
|
|
||||||
- UOJ_PROTOCOL=http
|
|
||||||
- DATABASE_HOST=uoj-db
|
|
||||||
- DATABASE_PASSWORD=root
|
|
||||||
- JUDGER_SOCKET_PORT=2333
|
|
||||||
- JUDGER_SOCKET_PASSWORD=_judger_socket_password_
|
|
||||||
- SALT_0=salt_0
|
|
||||||
- SALT_1=salt_1
|
|
||||||
- SALT_2=salt_2
|
|
||||||
- SALT_3=salt_3
|
|
||||||
|
@ -43,15 +43,6 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./uoj_data/web/data:/var/uoj_data
|
- ./uoj_data/web/data:/var/uoj_data
|
||||||
- ./uoj_data/web/storage:/opt/uoj/web/app/storage
|
- ./uoj_data/web/storage:/opt/uoj/web/app/storage
|
||||||
|
- ./.config.php:/opt/uoj/web/app/.config.php
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
environment:
|
|
||||||
- UOJ_PROTOCOL=https
|
|
||||||
- DATABASE_HOST=uoj-db
|
|
||||||
- DATABASE_PASSWORD=root
|
|
||||||
- JUDGER_SOCKET_PORT=2333
|
|
||||||
- JUDGER_SOCKET_PASSWORD=_judger_socket_password_
|
|
||||||
- SALT_0=salt_0
|
|
||||||
- SALT_1=salt_1
|
|
||||||
- SALT_2=salt_2
|
|
||||||
- SALT_3=salt_3
|
|
||||||
|
@ -20,21 +20,21 @@ return [
|
|||||||
'domain' => null,
|
'domain' => null,
|
||||||
'main' => [
|
'main' => [
|
||||||
'protocol' => 'http',
|
'protocol' => 'http',
|
||||||
'host' => '_httpHost_',
|
'host' => UOJContext::requestDomain(),
|
||||||
'port' => '80/443'
|
'port' => '80/443',
|
||||||
],
|
],
|
||||||
'blog' => [
|
'blog' => [
|
||||||
'protocol' => 'http',
|
'protocol' => 'http',
|
||||||
'host' => '_httpHost_',
|
'host' => UOJContext::requestDomain(),
|
||||||
'port' => '80/443'
|
'port' => '80/443',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'security' => [
|
'security' => [
|
||||||
'user' => [
|
'user' => [
|
||||||
'client_salt' => 'salt0'
|
'client_salt' => 'salt0',
|
||||||
],
|
],
|
||||||
'cookie' => [
|
'cookie' => [
|
||||||
'checksum_salt' => ['salt1', 'salt2', 'salt3']
|
'checksum_salt' => ['salt1', 'salt2', 'salt3'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'mail' => [
|
'mail' => [
|
||||||
@ -43,17 +43,17 @@ return [
|
|||||||
'password' => '_mail_noreply_password_',
|
'password' => '_mail_noreply_password_',
|
||||||
'host' => 'smtp.local_uoj.ac',
|
'host' => 'smtp.local_uoj.ac',
|
||||||
'secure' => 'tls',
|
'secure' => 'tls',
|
||||||
'port' => 587
|
'port' => 587,
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'judger' => [
|
'judger' => [
|
||||||
'socket' => [
|
'socket' => [
|
||||||
'port' => '233',
|
'port' => '233',
|
||||||
'password' => '_judger_socket_password_'
|
'password' => '_judger_socket_password_',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'switch' => [
|
'switch' => [
|
||||||
'blog-domain-mode' => 3,
|
'blog-domain-mode' => 3,
|
||||||
'open-register' => false
|
'open-register' => false,
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
@ -79,19 +79,20 @@ $time_form->succ_href = "/contests";
|
|||||||
$time_form->runAtServer();
|
$time_form->runAtServer();
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<?php echoUOJPageHeader('添加比赛') ?>
|
<?php echoUOJPageHeader(UOJLocale::get('contests::add new contest')) ?>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- left col -->
|
<!-- left col -->
|
||||||
<div class="col-lg-9">
|
<div class="col-lg-9">
|
||||||
<div class="card card-default mb-2">
|
<div class="card card-default mb-2">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h1 class="card-title">添加比赛</h1>
|
<h1 class="card-title">
|
||||||
|
<?= UOJLocale::get('contests::add new contest') ?>
|
||||||
|
</h1>
|
||||||
|
|
||||||
<div class="w-full" style="max-width: 400px">
|
<div class="w-full" style="max-width: 400px">
|
||||||
<?php $time_form->printHTML(); ?>
|
<?php $time_form->printHTML() ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -101,6 +102,7 @@ $time_form->runAtServer();
|
|||||||
<aside class="col-lg-3 mt-3 mt-lg-0">
|
<aside class="col-lg-3 mt-3 mt-lg-0">
|
||||||
<?php uojIncludeView('sidebar') ?>
|
<?php uojIncludeView('sidebar') ?>
|
||||||
</aside>
|
</aside>
|
||||||
|
<!-- end right col -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php echoUOJPageFooter() ?>
|
<?php echoUOJPageFooter() ?>
|
||||||
|
105
web/app/controllers/app/html2markdown.php
Normal file
105
web/app/controllers/app/html2markdown.php
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?php requireLib('bootstrap5') ?>
|
||||||
|
|
||||||
|
<?php echoUOJPageHeader(UOJLocale::get('html to markdown')) ?>
|
||||||
|
|
||||||
|
<h1>
|
||||||
|
<?= UOJLocale::get('html to markdown') ?>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#html,
|
||||||
|
#markdown {
|
||||||
|
font-family: Cascadia Mono, Ubuntu Mono, Roboto Mono, Jetbrains Mono, Fira Code, Consolas, '思源黑体 Regular', '思源宋体 Light', '宋体', 'Courier New', monospace;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row row-cols-1 row-cols-md-2">
|
||||||
|
<div class="col">
|
||||||
|
<textarea class="form-control" id="html" placeholder="input html here"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<textarea data-no-autosize readonly class="form-control" id="markdown" placeholder="output markdown here" style="height: 100%"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer bg-transparent text-end">
|
||||||
|
<a href="https://s2oj.github.io/#/user/apps/html2markdown" target="_blank">使用教程</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?= HTML::js_src('/js/turndown.js') ?>
|
||||||
|
<?= HTML::js_src('/js/turndown-plugin-gfm.js') ?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function mathjaxScriptBlockType(node) {
|
||||||
|
if (node.nodeName !== 'SCRIPT') return null;
|
||||||
|
|
||||||
|
const a = node.getAttribute('type');
|
||||||
|
if (!a || a.indexOf('math/tex') < 0) return null;
|
||||||
|
|
||||||
|
return a.indexOf('display') >= 0 ? 'block' : 'inline';
|
||||||
|
}
|
||||||
|
|
||||||
|
var turndownService = new TurndownService({
|
||||||
|
headingStyle: 'atx',
|
||||||
|
hr: '---',
|
||||||
|
bulletListMarker: '-',
|
||||||
|
codeBlockStyle: 'fenced',
|
||||||
|
fence: '```',
|
||||||
|
emDelimiter: '_',
|
||||||
|
strongDelimiter: '**',
|
||||||
|
linkStyle: 'inlined',
|
||||||
|
linkReferenceStyle: 'full',
|
||||||
|
preformattedCode: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
turndownService.use(turndownPluginGfm.gfm);
|
||||||
|
turndownService.addRule('mathjaxRendered', {
|
||||||
|
filter: function(node) {
|
||||||
|
return node.nodeName === 'SPAN' && node.getAttribute('class') === 'MathJax';
|
||||||
|
},
|
||||||
|
replacement: function(content) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
turndownService.addRule('mathjaxScriptInline', {
|
||||||
|
filter: function(node) {
|
||||||
|
return mathjaxScriptBlockType(node) === 'inline';
|
||||||
|
},
|
||||||
|
|
||||||
|
escapeContent: function() {
|
||||||
|
// We want the raw unescaped content since this is what Katex will need to render
|
||||||
|
// If we escape, it will double the \\ in particular.
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
replacement: function(content, node, options) {
|
||||||
|
return '$' + content + '$';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
turndownService.addRule('mathjaxScriptBlock', {
|
||||||
|
filter: function(node) {
|
||||||
|
return mathjaxScriptBlockType(node) === 'block';
|
||||||
|
},
|
||||||
|
|
||||||
|
escapeContent: function() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
replacement: function(content, node, options) {
|
||||||
|
return '$$\n' + content + '\n$$';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#html').on('input', function() {
|
||||||
|
$('#markdown').val(turndownService.turndown($('#html').val()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<?php echoUOJPageFooter() ?>
|
@ -466,6 +466,10 @@ $pag = new Paginator($pag_config);
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
[...document.querySelectorAll('[data-bs-toggle="tooltip"]')].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
|
||||||
|
});
|
||||||
|
|
||||||
var copy_url_toast = new bootstrap.Toast('#copy-url-toast', {
|
var copy_url_toast = new bootstrap.Toast('#copy-url-toast', {
|
||||||
delay: 2000
|
delay: 2000
|
||||||
});
|
});
|
@ -1,9 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
requirePHPLib('form');
|
requirePHPLib('form');
|
||||||
|
|
||||||
$forgot_form = new UOJBs4Form('forgot');
|
use Gregwar\Captcha\PhraseBuilder;
|
||||||
$forgot_form->addInput('username', 'text', '用户名', '',
|
|
||||||
function($username, &$vdata) {
|
$forgot_form = new UOJBs4Form('forgot');
|
||||||
|
$forgot_form->addInput(
|
||||||
|
'username',
|
||||||
|
'text',
|
||||||
|
'用户名',
|
||||||
|
'',
|
||||||
|
function ($username, &$vdata) {
|
||||||
if (!validateUsername($username)) {
|
if (!validateUsername($username)) {
|
||||||
return '用户名不合法';
|
return '用户名不合法';
|
||||||
}
|
}
|
||||||
@ -14,46 +20,104 @@
|
|||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
$forgot_form->handle = function(&$vdata) {
|
$forgot_form->appendHTML(<<<EOD
|
||||||
|
<div id="div-captcha" class="form-group">
|
||||||
|
<label for="input-captcha" class="col-sm-2 control-label">验证码</label>
|
||||||
|
<div class="col-sm-3" style="max-width: 60%">
|
||||||
|
<input type="text" class="form-control" id="input-captcha" name="captcha" placeholder="请输入验证码" maxlength="20" style="display: inline-block; width: 12em;" />
|
||||||
|
<div style="display: inline-block; margin-left: 8px; position: relative; top: -2px; cursor: pointer;">
|
||||||
|
<img id="captcha" src="" />
|
||||||
|
</div>
|
||||||
|
<span class="help-block" id="help-captcha" style="display: block"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
EOD);
|
||||||
|
$forgot_form->handle = function (&$vdata) {
|
||||||
$user = $vdata['user'];
|
$user = $vdata['user'];
|
||||||
$password = $user["password"];
|
$password = $user["password"];
|
||||||
|
|
||||||
|
if (!isset($_SESSION['phrase']) || !PhraseBuilder::comparePhrases($_SESSION['phrase'], $_POST['captcha'])) {
|
||||||
|
becomeMsgPage('验证码错误!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user['email']) {
|
||||||
|
becomeMsgPage('用户未填写邮件地址,请联系管理员重置!');
|
||||||
|
}
|
||||||
|
|
||||||
$oj_name = UOJConfig::$data['profile']['oj-name'];
|
$oj_name = UOJConfig::$data['profile']['oj-name'];
|
||||||
$oj_name_short = UOJConfig::$data['profile']['oj-name-short'];
|
$oj_name_short = UOJConfig::$data['profile']['oj-name-short'];
|
||||||
$sufs = base64url_encode($user['username'] . "." . md5($user['username'] . "+" . $password));
|
$check_code = md5($user['username'] . "+" . $password . '+' . UOJTime::$time_now_str);
|
||||||
$url = HTML::url("/reset-password", array('params' => array('p' => $sufs)));
|
$sufs = base64url_encode($user['username'] . "." . $check_code);
|
||||||
|
$url = HTML::url("/reset-password", ['params' => ['p' => $sufs]]);
|
||||||
|
$oj_url = HTML::url('/');
|
||||||
|
$name = $user['username'];
|
||||||
|
$remote_addr = UOJContext::remoteAddr();
|
||||||
|
$http_x_forwarded_for = UOJContext::httpXForwardedFor();
|
||||||
|
$user_agent = UOJContext::httpUserAgent();
|
||||||
|
|
||||||
|
if ($user['realname']) {
|
||||||
|
$name .= ' (' . $user['realname'] . ')';
|
||||||
|
}
|
||||||
|
|
||||||
$html = <<<EOD
|
$html = <<<EOD
|
||||||
<base target="_blank" />
|
<base target="_blank" />
|
||||||
|
|
||||||
<p>{$user['username']}您好,</p>
|
<p>{$name} 您好,</p>
|
||||||
<p>您刚刚启用了{$oj_name_short}密码找回功能,请进入下面的链接重设您的密码:</p>
|
|
||||||
<p><a href="$url">$url</a></p>
|
|
||||||
<p>{$oj_name}</p>
|
|
||||||
|
|
||||||
<style type="text/css">
|
<p>您最近告知我们需要重置您在 {$oj_name_short} 上账号的密码。请访问以下链接:<a href="{$url}">{$url}</a> (如果无法点击链接,请试着复制链接并粘贴至浏览器中打开。)</p>
|
||||||
body{font-size:14px;font-family:arial,verdana,sans-serif;line-height:1.666;padding:0;margin:0;overflow:auto;white-space:normal;word-wrap:break-word;min-height:100px}
|
<p>如果您没有请求重置密码,则忽略此信息。该链接将在 72 小时后自动过期失效。</p>
|
||||||
pre {white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}
|
|
||||||
</style>
|
<ul>
|
||||||
|
<li><small>请求 IP: {$remote_addr} (转发来源: {$http_x_forwarded_for})</small></li>
|
||||||
|
<li><small>用户代理: {$user_agent}</small></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>{$oj_name}</p>
|
||||||
|
<p><a href="{$oj_url}">{$oj_url}</a></p>
|
||||||
EOD;
|
EOD;
|
||||||
|
|
||||||
$mailer = UOJMail::noreply();
|
$mailer = UOJMail::noreply();
|
||||||
$mailer->addAddress($user['email'], $user['username']);
|
$mailer->addAddress($user['email'], $user['username']);
|
||||||
$mailer->Subject = $oj_name_short."密码找回";
|
$mailer->Subject = $oj_name_short . " 密码找回";
|
||||||
$mailer->msgHTML($html);
|
$mailer->msgHTML($html);
|
||||||
if (!$mailer->send()) {
|
if (!$mailer->send()) {
|
||||||
error_log($mailer->ErrorInfo);
|
error_log($mailer->ErrorInfo);
|
||||||
becomeMsgPage('<div class="text-center"><h2>邮件发送失败,请重试 <span class="glyphicon glyphicon-remove"></span></h2></div>');
|
becomeMsgPage('<div class="text-center"><h2>邮件发送失败,请重试!</h2></div>');
|
||||||
} else {
|
} else {
|
||||||
becomeMsgPage('<div class="text-center"><h2>邮件发送成功 <span class="glyphicon glyphicon-ok"></span></h2></div>');
|
DB::update([
|
||||||
}
|
"update user_info",
|
||||||
};
|
"set", [
|
||||||
$forgot_form->submit_button_config['align'] = 'offset';
|
'extra' => DB::json_set('extra', '$.reset_password_check_code', $check_code, '$.reset_password_time', UOJTime::$time_now_str),
|
||||||
|
],
|
||||||
|
"where", [
|
||||||
|
"username" => $user['username'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
$forgot_form->runAtServer();
|
becomeMsgPage('<div class="text-center"><h2>邮件发送成功,请检查收件箱!</h2><span>如果邮件未出现在收件箱中,请检查垃圾箱。</span></div>');
|
||||||
?>
|
}
|
||||||
|
};
|
||||||
|
$forgot_form->submit_button_config['align'] = 'offset';
|
||||||
|
|
||||||
|
$forgot_form->runAtServer();
|
||||||
|
?>
|
||||||
<?php echoUOJPageHeader('找回密码') ?>
|
<?php echoUOJPageHeader('找回密码') ?>
|
||||||
<h2 class="page-header">找回密码</h2>
|
<h2 class="page-header">找回密码</h2>
|
||||||
<h4>请输入需要找回密码的用户名:</h4>
|
<h4>请输入需要找回密码的用户名:</h4>
|
||||||
<?php $forgot_form->printHTML(); ?>
|
<?php $forgot_form->printHTML(); ?>
|
||||||
|
<script>
|
||||||
|
function refreshCaptcha() {
|
||||||
|
var timestamp = new Date().getTime();
|
||||||
|
$("#captcha").attr("src", "/captcha" + '?' + timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
refreshCaptcha();
|
||||||
|
|
||||||
|
$("#captcha").click(function(e) {
|
||||||
|
refreshCaptcha();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
<?php echoUOJPageFooter() ?>
|
<?php echoUOJPageFooter() ?>
|
||||||
|
@ -49,7 +49,7 @@ UOJGroup::cur()->userCanView(Auth::user(), ['ensure' => true]);
|
|||||||
</h2>
|
</h2>
|
||||||
<?php if (UOJGroup::info('announcement')) : ?>
|
<?php if (UOJGroup::info('announcement')) : ?>
|
||||||
<div class="text-break">
|
<div class="text-break">
|
||||||
<?= HTML::purifier_inline()->purify(HTML::parsedown()->line(UOJGroup::info('announcement'))) ?>
|
<?= HTML::purifier_inline()->purify(HTML::parsedown(['username_with_color' => true])->line(UOJGroup::info('announcement'))) ?>
|
||||||
</div>
|
</div>
|
||||||
<?php else : ?>
|
<?php else : ?>
|
||||||
<div class="text-muted">
|
<div class="text-muted">
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
<?php requireLib('bootstrap5') ?>
|
|
||||||
|
|
||||||
<?php echoUOJPageHeader(UOJLocale::get('html to markdown')) ?>
|
|
||||||
|
|
||||||
<h1>
|
|
||||||
<?= UOJLocale::get('html to markdown') ?>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
#html,
|
|
||||||
#markdown {
|
|
||||||
font-family: Cascadia Mono, Ubuntu Mono, Roboto Mono, Jetbrains Mono, Fira Code, Consolas, '思源黑体 Regular', '思源宋体 Light', '宋体', 'Courier New', monospace;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 300px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row row-cols-1 row-cols-md-2">
|
|
||||||
<div class="col">
|
|
||||||
<textarea class="form-control" id="html" placeholder="input html here"></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<textarea data-no-autosize readonly class="form-control" id="markdown" placeholder="output markdown here" style="height: 100%"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer bg-transparent text-end">
|
|
||||||
<a href="https://s2oj.github.io/#/user/apps/html2markdown" target="_blank">使用教程</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?= HTML::js_src('/js/h2m.js') ?>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
$(document).ready(function() {
|
|
||||||
$('#html').on('input', function() {
|
|
||||||
$('#markdown').val(h2m($('#html').val(), {
|
|
||||||
converter: 'Gfm'
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<?php echoUOJPageFooter() ?>
|
|
@ -318,7 +318,7 @@ EOD);
|
|||||||
echo HTML::tag_begin('tr');
|
echo HTML::tag_begin('tr');
|
||||||
echo HTML::tag('td', ['class' => 'text-center'], $problem->info['id']);
|
echo HTML::tag('td', ['class' => 'text-center'], $problem->info['id']);
|
||||||
echo HTML::tag_begin('td');
|
echo HTML::tag_begin('td');
|
||||||
echo $problem->getLink();
|
echo $problem->getLink(['with' => 'none']);
|
||||||
if ($problem->info['is_hidden']) {
|
if ($problem->info['is_hidden']) {
|
||||||
echo ' <span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ', UOJLocale::get('hidden'), '</span> ';
|
echo ' <span class="badge text-bg-danger"><i class="bi bi-eye-slash-fill"></i> ', UOJLocale::get('hidden'), '</span> ';
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,28 @@ Auth::check() || redirectToLogin();
|
|||||||
UOJUser::checkPermission(Auth::user(), 'lists.view') || UOJResponse::page403();
|
UOJUser::checkPermission(Auth::user(), 'lists.view') || UOJResponse::page403();
|
||||||
|
|
||||||
if (UOJList::userCanCreateList(Auth::user())) {
|
if (UOJList::userCanCreateList(Auth::user())) {
|
||||||
$new_list_form = new UOJBs4Form('new_list');
|
$new_list_form = new UOJForm('new_list');
|
||||||
$new_list_form->handle = function () {
|
$new_list_form->handle = function () {
|
||||||
DB::insert("insert into lists (title, is_hidden) values ('未命名题单', 1)");
|
DB::insert([
|
||||||
$id = DB::insert_id();
|
"insert into lists",
|
||||||
DB::insert("insert into lists_contents (id, content, content_md) values ($id, '', '')");
|
DB::bracketed_fields(['title', 'is_hidden']),
|
||||||
|
"values",
|
||||||
|
DB::tuple(['未命名题单', 1]),
|
||||||
|
]);
|
||||||
|
$list_id = DB::insert_id();
|
||||||
|
DB::insert([
|
||||||
|
"insert into lists_contents",
|
||||||
|
DB::bracketed_fields(['id', 'content', 'content_md']),
|
||||||
|
"values",
|
||||||
|
DB::tuple([$list_id, '', '']),
|
||||||
|
]);
|
||||||
|
redirectTo("/list/{$list_id}");
|
||||||
|
die();
|
||||||
};
|
};
|
||||||
$new_list_form->submit_button_config['align'] = 'right';
|
$new_list_form->config['submit_container']['class'] = 'text-end';
|
||||||
$new_list_form->submit_button_config['class_str'] = 'btn btn-primary';
|
$new_list_form->config['submit_button']['class'] = 'btn btn-primary';
|
||||||
$new_list_form->submit_button_config['text'] = UOJLocale::get('problems::add new list');
|
$new_list_form->config['submit_button']['text'] = UOJLocale::get('problems::add new list');
|
||||||
$new_list_form->submit_button_config['smart_confirm'] = '';
|
$new_list_form->config['confirm']['smart'] = true;
|
||||||
$new_list_form->runAtServer();
|
$new_list_form->runAtServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ if (UOJContest::cur()) {
|
|||||||
|
|
||||||
$submission_requirement = UOJProblem::cur()->getSubmissionRequirement();
|
$submission_requirement = UOJProblem::cur()->getSubmissionRequirement();
|
||||||
$custom_test_requirement = UOJProblem::cur()->getCustomTestRequirement();
|
$custom_test_requirement = UOJProblem::cur()->getCustomTestRequirement();
|
||||||
$custom_test_enabled = false; // $custom_test_requirement && $pre_submit_check_ret === true;
|
$custom_test_enabled = $custom_test_requirement && $pre_submit_check_ret === true;
|
||||||
|
|
||||||
function handleUpload($zip_file_name, $content, $tot_size) {
|
function handleUpload($zip_file_name, $content, $tot_size) {
|
||||||
global $is_participating;
|
global $is_participating;
|
||||||
@ -193,7 +193,7 @@ if ($pre_submit_check_ret === true && !$no_more_submission) {
|
|||||||
'FS::randomAvailableSubmissionFileName',
|
'FS::randomAvailableSubmissionFileName',
|
||||||
'handleUpload'
|
'handleUpload'
|
||||||
);
|
);
|
||||||
$zip_answer_form->extra_validators[] = $submission_extra_validator;
|
$zip_answer_form->extra_validator = $submission_extra_validator;
|
||||||
$zip_answer_form->succ_href = $is_participating ? '/contest/' . UOJContest::info('id') . '/submissions' : '/submissions';
|
$zip_answer_form->succ_href = $is_participating ? '/contest/' . UOJContest::info('id') . '/submissions' : '/submissions';
|
||||||
$zip_answer_form->runAtServer();
|
$zip_answer_form->runAtServer();
|
||||||
}
|
}
|
||||||
@ -385,16 +385,27 @@ if (UOJContest::cur()) {
|
|||||||
<div class="card mb-2">
|
<div class="card mb-2">
|
||||||
<ul class="list-group list-group-flush">
|
<ul class="list-group list-group-flush">
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
<span class="flex-shrink-0">上传者</span>
|
<span class="flex-shrink-0">
|
||||||
<span><?= UOJProblem::cur()->getUploaderLink() ?></span>
|
<?= UOJLocale::get('problems::uploader') ?>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<?= UOJProblem::cur()->getUploaderLink() ?>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
<?php if (!UOJContest::cur() || UOJContest::cur()->progress() >= CONTEST_FINISHED) : ?>
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
<span class="flex-shrink-0">难度</span>
|
<span class="flex-shrink-0">
|
||||||
<span><?= UOJProblem::cur()->getDifficultyHTML() ?></span>
|
<?= UOJLocale::get('problems::difficulty') ?>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<?= UOJProblem::cur()->getDifficultyHTML() ?>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<?php if (Auth::check()) : ?>
|
<?php if (Auth::check()) : ?>
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
<span class="flex-shrink-0">历史分数</span>
|
<span class="flex-shrink-0">
|
||||||
|
<?= UOJLocale::get('problems::historical score') ?>
|
||||||
|
</span>
|
||||||
<?php $his_score = DB::selectSingle(["select max(score)", "from submissions", "where", ["problem_id" => UOJProblem::info('id'), "submitter" => Auth::id()]]) ?>
|
<?php $his_score = DB::selectSingle(["select max(score)", "from submissions", "where", ["problem_id" => UOJProblem::info('id'), "submitter" => Auth::id()]]) ?>
|
||||||
|
|
||||||
<a class="<?= is_null($his_score) ? '' : 'uoj-score' ?>" href="<?= HTML::url('/submissions', ['params' => ['problem_id' => UOJProblem::info('id'), 'submitter' => Auth::id()]]) ?>">
|
<a class="<?= is_null($his_score) ? '' : 'uoj-score' ?>" href="<?= HTML::url('/submissions', ['params' => ['problem_id' => UOJProblem::info('id'), 'submitter' => Auth::id()]]) ?>">
|
||||||
@ -403,7 +414,9 @@ if (UOJContest::cur()) {
|
|||||||
</li>
|
</li>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
<span class="flex-shrink-0">标签</span>
|
<span class="flex-shrink-0">
|
||||||
|
<?= UOJLocale::get('problems::tags') ?>
|
||||||
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<?php if (UOJProblem::info('is_hidden')) : ?>
|
<?php if (UOJProblem::info('is_hidden')) : ?>
|
||||||
<a href="<?= HTML::url('/problems', ['params' => ['is_hidden' => 'on']]) ?>">
|
<a href="<?= HTML::url('/problems', ['params' => ['is_hidden' => 'on']]) ?>">
|
||||||
@ -422,9 +435,14 @@ if (UOJContest::cur()) {
|
|||||||
<?php endforeach ?>
|
<?php endforeach ?>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
<?php endif ?>
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
<span class="flex-shrink-0">评价</span>
|
<span class="flex-shrink-0">
|
||||||
<span><?= UOJProblem::cur()->getZanBlock() ?></span>
|
<?= UOJLocale::get('appraisal') ?>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<?= UOJProblem::cur()->getZanBlock() ?>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -73,6 +73,9 @@ EOD;
|
|||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
dataNewProblem($id);
|
dataNewProblem($id);
|
||||||
|
|
||||||
|
redirectTo("/problem/{$id}/manage/statement");
|
||||||
|
die();
|
||||||
};
|
};
|
||||||
$new_problem_form->submit_button_config['align'] = 'right';
|
$new_problem_form->submit_button_config['align'] = 'right';
|
||||||
$new_problem_form->submit_button_config['class_str'] = 'btn btn-primary';
|
$new_problem_form->submit_button_config['class_str'] = 'btn btn-primary';
|
||||||
|
@ -101,6 +101,29 @@ if (UOJProblem::cur()->userCanManage(Auth::user()) || UOJProblem::cur()->userPer
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
$add_new_solution_form->runAtServer();
|
$add_new_solution_form->runAtServer();
|
||||||
|
|
||||||
|
if (UOJUser::checkPermission(Auth::user(), 'blogs.create')) {
|
||||||
|
$quick_add_new_solution_form = new UOJForm('quick_add_new_solution');
|
||||||
|
$quick_add_new_solution_form->config['submit_container']['class'] = '';
|
||||||
|
$quick_add_new_solution_form->config['submit_button']['class'] = 'btn btn-link text-decoration-none p-0';
|
||||||
|
$quick_add_new_solution_form->config['submit_button']['text'] = '快速新建文章';
|
||||||
|
$quick_add_new_solution_form->handle = function () {
|
||||||
|
DB::insert([
|
||||||
|
"insert into blogs",
|
||||||
|
"(title, content, content_md, poster, is_hidden, post_time, active_time)",
|
||||||
|
"values", DB::tuple([
|
||||||
|
'【题解】' . UOJProblem::cur()->getTitle(), '', '',
|
||||||
|
Auth::id(), false, DB::now(), DB::now()
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
|
||||||
|
$blog_id = DB::insert_id();
|
||||||
|
|
||||||
|
redirectTo(HTML::blog_url(Auth::id(), "/post/{$blog_id}/write"));
|
||||||
|
die();
|
||||||
|
};
|
||||||
|
$quick_add_new_solution_form->runAtServer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$pag_config = [
|
$pag_config = [
|
||||||
@ -262,18 +285,16 @@ $pag = new Paginator($pag_config);
|
|||||||
您当前无法为本题新增题解。
|
您当前无法为本题新增题解。
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</div>
|
</div>
|
||||||
|
<?php if (isset($quick_add_new_solution_form)) : ?>
|
||||||
<div class="card-footer bg-transparent">
|
<div class="card-footer bg-transparent">
|
||||||
<a target="_blank" class="text-decoration-none" href="<?= HTML::blog_url(Auth::id(), '/post/new/write?title=' . urlencode('【题解】#' . UOJProblem::info('id') . '. ' . UOJProblem::info('title')) . '&is_hidden=0') ?>">
|
<?php $quick_add_new_solution_form->printHTML() ?>
|
||||||
快速新建文章
|
|
||||||
</a>
|
|
||||||
<div class="small text-muted mt-1">发布文章后,请返回本页输入博客 ID</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php uojIncludeView('sidebar'); ?>
|
<?php uojIncludeView('sidebar') ?>
|
||||||
<!-- End right col -->
|
|
||||||
</aside>
|
</aside>
|
||||||
|
<!-- end right col -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php echoUOJPageFooter() ?>
|
<?php echoUOJPageFooter() ?>
|
||||||
|
@ -174,6 +174,85 @@ $difficulty_form->runAtServer();
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card mt-3">
|
||||||
|
<div class="card-header fw-bold">
|
||||||
|
标签填充
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<script>
|
||||||
|
function fillTag(tags) {
|
||||||
|
if (typeof tags === 'string') tags = [tags];
|
||||||
|
|
||||||
|
tags = tags.map(tag => tag.trim()).filter(Boolean);
|
||||||
|
|
||||||
|
var originalTags = $('#input-problem_tags')
|
||||||
|
.val()
|
||||||
|
.replace(/,/g, ',')
|
||||||
|
.split(',')
|
||||||
|
.map(tag => tag.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
var newTagsSet = new Set(originalTags.concat(tags));
|
||||||
|
|
||||||
|
$('#input-problem_tags').val(Array.from(newTagsSet.values()).join(', '));
|
||||||
|
$('#input-problem_tags').trigger('input');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="row row-cols-4 row-cols-lg-2 g-2">
|
||||||
|
<?php foreach (UOJProblem::$categories as $category => $tags) : ?>
|
||||||
|
<?php $category_id = uniqid('category-'); ?>
|
||||||
|
|
||||||
|
<div class="d-inline-block" id="category-container-<?= $category_id ?>">
|
||||||
|
<button id="category-button-<?= $category_id ?>" class="btn btn-sm btn-light w-100" type="button"><?= $category ?></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
bootstrap.Popover.jQueryInterface.call($('#category-button-<?= $category_id ?>'), {
|
||||||
|
container: $('#category-container-<?= $category_id ?>'),
|
||||||
|
html: true,
|
||||||
|
placement: 'left',
|
||||||
|
animation: false,
|
||||||
|
trigger: 'manual',
|
||||||
|
fallbackPlacements: ['bottom', 'right'],
|
||||||
|
content: [
|
||||||
|
<?php foreach ($tags as $tag) : ?> '<?= $tag ?>', <?php endforeach ?>
|
||||||
|
].map(tag => ('<button class="btn btn-sm btn-light d-inline-block mr-1 mb-1" onclick="fillTag([\'<?= $category ?>\', \'' + tag + '\'])">' + tag + '</button>')).join(' '),
|
||||||
|
sanitizeFn(content) {
|
||||||
|
return content;
|
||||||
|
},
|
||||||
|
}).on("mouseenter", function() {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
$(this).popover("show");
|
||||||
|
$(this).siblings(".popover").on("mouseleave", function() {
|
||||||
|
$(_this).popover('hide');
|
||||||
|
});
|
||||||
|
}).on("mouseleave", function() {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
var check_popover_status = function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
if (!$(".popover:hover").length) {
|
||||||
|
$(_this).popover("hide")
|
||||||
|
} else {
|
||||||
|
check_popover_status();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
};
|
||||||
|
|
||||||
|
check_popover_status();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-muted small bg-transparent">
|
||||||
|
将鼠标悬浮至主分类上,点击弹出框中的对应标签即可将其填充至题目标签中。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card mt-3">
|
<div class="card mt-3">
|
||||||
<div class="card-header fw-bold">
|
<div class="card-header fw-bold">
|
||||||
题目难度
|
题目难度
|
||||||
@ -183,7 +262,6 @@ $difficulty_form->runAtServer();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php echoUOJPageFooter() ?>
|
<?php echoUOJPageFooter() ?>
|
||||||
|
@ -1,39 +1,55 @@
|
|||||||
<?php
|
<?php
|
||||||
if (!isset($_GET['p'])) {
|
if (!isset($_GET['p'])) {
|
||||||
become404Page();
|
become404Page();
|
||||||
}
|
}
|
||||||
function resetPassword() {
|
|
||||||
list($username, $check_code) = explode('.', base64url_decode($_GET['p']));
|
list($username, $check_code) = explode('.', base64url_decode($_GET['p']));
|
||||||
|
$user = UOJUser::query($username);
|
||||||
|
|
||||||
|
if (!$user) become404Page();
|
||||||
|
if (!isset($check_code) || strlen($check_code) != 32) become404Page();
|
||||||
|
|
||||||
|
$extra = UOJUser::getExtra($user);
|
||||||
|
|
||||||
|
if ($check_code !== $extra['reset_password_check_code']) {
|
||||||
|
become404Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UOJTime::str2time($extra['reset_password_time'])->add(new DateInterval('P3D')) < UOJTime::$time_now) {
|
||||||
|
becomeMsgPage('链接已过期');
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetPassword() {
|
||||||
|
global $user;
|
||||||
|
|
||||||
if (!isset($_POST['newPW']) || !validatePassword($_POST['newPW'])) {
|
if (!isset($_POST['newPW']) || !validatePassword($_POST['newPW'])) {
|
||||||
return '操作失败,无效密码';
|
return '操作失败,无效密码';
|
||||||
}
|
}
|
||||||
if (!isset($username) || !validateUsername($username)) {
|
|
||||||
return '不明错误';
|
|
||||||
}
|
|
||||||
if (!isset($check_code)) {
|
|
||||||
return '不明错误';
|
|
||||||
}
|
|
||||||
|
|
||||||
$newPW = $_POST['newPW'];
|
$newPW = $_POST['newPW'];
|
||||||
$user = UOJUser::query($username);
|
|
||||||
if ($user == null) {
|
|
||||||
return '不明错误';
|
|
||||||
}
|
|
||||||
if ($check_code !== md5($user['username'] . '+' . $user['password'])) {
|
|
||||||
return '不明错误';
|
|
||||||
}
|
|
||||||
$newPW = getPasswordToStore($newPW, $user['username']);
|
$newPW = getPasswordToStore($newPW, $user['username']);
|
||||||
DB::update("update user_info set password = '$newPW' where username = '{$user['username']}'");
|
|
||||||
|
DB::update([
|
||||||
|
"update user_info",
|
||||||
|
"set", [
|
||||||
|
"password" => $newPW,
|
||||||
|
"extra" => DB::json_remove('extra', '$.reset_password_check_code', '$.reset_password_time'),
|
||||||
|
],
|
||||||
|
"where", [
|
||||||
|
"username" => $user['username'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
return 'ok';
|
return 'ok';
|
||||||
}
|
}
|
||||||
if (isset($_POST['reset'])) {
|
if (isset($_POST['reset'])) {
|
||||||
die(resetPassword());
|
die(resetPassword());
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<?php
|
<?php
|
||||||
$REQUIRE_LIB['dialog'] = '';
|
$REQUIRE_LIB['dialog'] = '';
|
||||||
$REQUIRE_LIB['md5'] = '';
|
$REQUIRE_LIB['md5'] = '';
|
||||||
?>
|
?>
|
||||||
<?php echoUOJPageHeader('更改密码') ?>
|
<?php echoUOJPageHeader('更改密码') ?>
|
||||||
<h2 class="page-header">更改密码</h2>
|
<h2 class="page-header">更改密码</h2>
|
||||||
<form id="form-reset" class="form-horizontal">
|
<form id="form-reset" class="form-horizontal">
|
||||||
@ -53,40 +69,40 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function validateResetPwPost() {
|
function validateResetPwPost() {
|
||||||
var ok = true;
|
var ok = true;
|
||||||
ok &= getFormErrorAndShowHelp('password', validateSettingPassword);
|
ok &= getFormErrorAndShowHelp('password', validateSettingPassword);
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#form-reset').submit(function(e) {
|
$('#form-reset').submit(function(e) {
|
||||||
if (!validateResetPwPost()) {
|
if (!validateResetPwPost()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$.post(<?= json_encode($_SERVER['REQUEST_URI']) ?>, {
|
$.post(<?= json_encode($_SERVER['REQUEST_URI']) ?>, {
|
||||||
reset : '',
|
reset: '',
|
||||||
newPW : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>")
|
newPW: md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>")
|
||||||
}, function(res) {
|
}, function(res) {
|
||||||
if (res == 'ok') {
|
if (res == 'ok') {
|
||||||
BootstrapDialog.show({
|
BootstrapDialog.show({
|
||||||
title : '提示',
|
title: '提示',
|
||||||
message : '密码更改成功',
|
message: '密码更改成功',
|
||||||
type : BootstrapDialog.TYPE_SUCCESS,
|
type: BootstrapDialog.TYPE_SUCCESS,
|
||||||
buttons: [{
|
buttons: [{
|
||||||
label: '好的',
|
label: '好的',
|
||||||
action: function(dialog) {
|
action: function(dialog) {
|
||||||
dialog.close();
|
dialog.close();
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
onhidden : function(dialog) {
|
onhidden: function(dialog) {
|
||||||
window.location.href = '/login';
|
window.location.href = '/login';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
BootstrapDialog.show({
|
BootstrapDialog.show({
|
||||||
title : '提示',
|
title: '提示',
|
||||||
message : res,
|
message: res,
|
||||||
type : BootstrapDialog.TYPE_DANGER,
|
type: BootstrapDialog.TYPE_DANGER,
|
||||||
buttons: [{
|
buttons: [{
|
||||||
label: '好的',
|
label: '好的',
|
||||||
action: function(dialog) {
|
action: function(dialog) {
|
||||||
@ -98,6 +114,6 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<?php echoUOJPageFooter() ?>
|
<?php echoUOJPageFooter() ?>
|
||||||
|
@ -201,7 +201,9 @@ $comments_pag = new Paginator([
|
|||||||
"order by id"
|
"order by id"
|
||||||
]);
|
]);
|
||||||
foreach ($replies as $idx => $reply) {
|
foreach ($replies as $idx => $reply) {
|
||||||
$replies[$idx]['poster_realname'] = UOJUser::query($reply['poster'])['realname'];
|
$reply_user = UOJUser::query($reply['poster']);
|
||||||
|
$replies[$idx]['poster_realname'] = $reply_user['realname'];
|
||||||
|
$replies[$idx]['poster_username_color'] = UOJUser::getUserColor($reply_user);
|
||||||
$replies[$idx]['content'] = getCommentContentToDisplay($reply);
|
$replies[$idx]['content'] = getCommentContentToDisplay($reply);
|
||||||
}
|
}
|
||||||
$replies_json = json_encode($replies);
|
$replies_json = json_encode($replies);
|
||||||
|
@ -32,7 +32,7 @@ $header_row .= '<th style="width:35em">' . UOJLocale::get('contests::problem sel
|
|||||||
$header_row .= '<th style="width:35em">' . UOJLocale::get('contests::contest self review') . '</th>';
|
$header_row .= '<th style="width:35em">' . UOJLocale::get('contests::contest self review') . '</th>';
|
||||||
$header_row .= '</tr>';
|
$header_row .= '</tr>';
|
||||||
|
|
||||||
$parsedown = HTML::parsedown();
|
$parsedown = HTML::parsedown(['username_with_color' => true]);
|
||||||
$purifier = HTML::purifier_inline();
|
$purifier = HTML::purifier_inline();
|
||||||
|
|
||||||
$print_row = function ($row) use ($parsedown, $purifier) {
|
$print_row = function ($row) use ($parsedown, $purifier) {
|
||||||
|
@ -168,7 +168,9 @@ if ($perm['manager_view']) {
|
|||||||
?>
|
?>
|
||||||
|
|
||||||
<?php if ($perm['content'] || $perm['manager_view']) : ?>
|
<?php if ($perm['content'] || $perm['manager_view']) : ?>
|
||||||
|
<div class="copy-button-container">
|
||||||
<?php UOJSubmission::cur()->echoContent() ?>
|
<?php UOJSubmission::cur()->echoContent() ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php if (isset($hack_form)) : ?>
|
<?php if (isset($hack_form)) : ?>
|
||||||
<p class="text-center">
|
<p class="text-center">
|
||||||
@ -221,17 +223,17 @@ if (UOJSubmission::cur()->hasJudged()) {
|
|||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="d-flex gap-2 justify-content-end">
|
<div class="d-flex gap-2 justify-content-end">
|
||||||
<?php if (isset($minor_rejudge_form)) : ?>
|
<?php if (isset($minor_rejudge_form)) : ?>
|
||||||
<?php $minor_rejudge_form->printHTML() ?>
|
<?php $minor_rejudge_form->printHTML() ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (isset($rejudge_form)) : ?>
|
<?php if (isset($rejudge_form)) : ?>
|
||||||
<?php $rejudge_form->printHTML() ?>
|
<?php $rejudge_form->printHTML() ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (isset($delete_form)) : ?>
|
<?php if (isset($delete_form)) : ?>
|
||||||
<?php $delete_form->printHTML() ?>
|
<?php $delete_form->printHTML() ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php echoUOJPageFooter() ?>
|
<?php echoUOJPageFooter() ?>
|
||||||
|
@ -516,7 +516,7 @@ EOD);
|
|||||||
'new_tmp_expiration_time',
|
'new_tmp_expiration_time',
|
||||||
'text',
|
'text',
|
||||||
'过期时间',
|
'过期时间',
|
||||||
(new DateTime())->add(new DateInterval('P7D'))->format('Y-m-d H:i:s'),
|
UOJTime::time2str((new DateTime())->add(new DateInterval('P7D'))->setTime(0, 0, 0)),
|
||||||
function ($str, &$vdata) {
|
function ($str, &$vdata) {
|
||||||
try {
|
try {
|
||||||
$vdata['expiration_time'] = new DateTime($str);
|
$vdata['expiration_time'] = new DateTime($str);
|
||||||
@ -657,6 +657,7 @@ EOD);
|
|||||||
);
|
);
|
||||||
$change_usergroup_form->addVSelect('op_type', [
|
$change_usergroup_form->addVSelect('op_type', [
|
||||||
'banneduser' => '设为封禁用户',
|
'banneduser' => '设为封禁用户',
|
||||||
|
'tempuser' => '设为临时用户',
|
||||||
'normaluser' => '设为普通用户',
|
'normaluser' => '设为普通用户',
|
||||||
'superuser' => '设为超级用户',
|
'superuser' => '设为超级用户',
|
||||||
], '操作类型', '');
|
], '操作类型', '');
|
||||||
@ -666,15 +667,59 @@ EOD);
|
|||||||
|
|
||||||
switch ($_POST['op_type']) {
|
switch ($_POST['op_type']) {
|
||||||
case 'banneduser':
|
case 'banneduser':
|
||||||
DB::update("update user_info set usergroup = 'B', usertype = 'banned' where username = '{$username}'");
|
DB::update([
|
||||||
$usergroup = '被封禁的用户';
|
"update user_info",
|
||||||
|
"set", [
|
||||||
|
"usergroup" => "B",
|
||||||
|
"usertype" => "banned",
|
||||||
|
"expiration_time" => null,
|
||||||
|
],
|
||||||
|
"where", [
|
||||||
|
"username" => $username,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$usergroup = '被封禁的用户,该用户将无法再次登录系统';
|
||||||
|
break;
|
||||||
|
case 'tempuser':
|
||||||
|
DB::update([
|
||||||
|
"update user_info",
|
||||||
|
"set", [
|
||||||
|
"usergroup" => "T",
|
||||||
|
"usertype" => "student",
|
||||||
|
"expiration_time" => DB::now(),
|
||||||
|
],
|
||||||
|
"where", [
|
||||||
|
"username" => $username,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$usergroup = '临时用户,请前往个人信息编辑页面修改过期时间';
|
||||||
break;
|
break;
|
||||||
case 'normaluser':
|
case 'normaluser':
|
||||||
DB::update("update user_info set usergroup = 'U', usertype = 'student' where username = '{$username}'");
|
DB::update([
|
||||||
|
"update user_info",
|
||||||
|
"set", [
|
||||||
|
"usergroup" => "U",
|
||||||
|
"usertype" => "student",
|
||||||
|
"expiration_time" => null,
|
||||||
|
],
|
||||||
|
"where", [
|
||||||
|
"username" => $username,
|
||||||
|
],
|
||||||
|
]);
|
||||||
$usergroup = '普通用户';
|
$usergroup = '普通用户';
|
||||||
break;
|
break;
|
||||||
case 'superuser':
|
case 'superuser':
|
||||||
DB::update("update user_info set usergroup = 'S', usertype = 'student' where username = '{$username}'");
|
DB::update([
|
||||||
|
"update user_info",
|
||||||
|
"set", [
|
||||||
|
"usergroup" => "S",
|
||||||
|
"usertype" => "student",
|
||||||
|
"expiration_time" => null,
|
||||||
|
],
|
||||||
|
"where", [
|
||||||
|
"username" => $username,
|
||||||
|
],
|
||||||
|
]);
|
||||||
$usergroup = '超级用户';
|
$usergroup = '超级用户';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1336,7 +1381,7 @@ EOD);
|
|||||||
EOD,
|
EOD,
|
||||||
function ($row) {
|
function ($row) {
|
||||||
echo '<tr>';
|
echo '<tr>';
|
||||||
echo '<td>', '<span class="uoj-username" data-realname="', HTML::escape($row['realname']), '">', $row['username'], '</span>', '</td>';
|
echo '<td>', UOJUser::getLink($row), '</td>';
|
||||||
echo '<td>', HTML::escape($row['school']), '</td>';
|
echo '<td>', HTML::escape($row['school']), '</td>';
|
||||||
echo '<td>';
|
echo '<td>';
|
||||||
switch ($row['usergroup']) {
|
switch ($row['usergroup']) {
|
||||||
@ -1429,7 +1474,8 @@ EOD);
|
|||||||
<h5>注意事项</h5>
|
<h5>注意事项</h5>
|
||||||
<ul class="mb-0">
|
<ul class="mb-0">
|
||||||
<li>用户被封禁后将不能再次登录系统。</li>
|
<li>用户被封禁后将不能再次登录系统。</li>
|
||||||
<li>将当前用户移除权限后将无法再次访问本页面。</li>
|
<li>将用户修改为临时用户后,请前往个人信息编辑页面修改过期时间。</li>
|
||||||
|
<li>将当前用户移除管理权限后将无法再次访问本页面。</li>
|
||||||
<li>在修改用户类别前请仔细核对用户名以免产生不必要的麻烦。</li>
|
<li>在修改用户类别前请仔细核对用户名以免产生不必要的麻烦。</li>
|
||||||
<li>如需为用户设置题目上传者、题目管理员等权限,请前往对应用户的个人资料编辑页面,点击「特权」选项卡修改。</li>
|
<li>如需为用户设置题目上传者、题目管理员等权限,请前往对应用户的个人资料编辑页面,点击「特权」选项卡修改。</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -266,6 +266,43 @@ EOD);
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
if ($user['usergroup'] == 'B') {
|
||||||
|
$update_profile_form->appendHTML(<<<EOD
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="input-username_color" class="form-label">用户名颜色</label>
|
||||||
|
<input type="text" class="form-control" id="input-username_color" aria-describedby="help-username_color" value="棕色 - #996600" disabled>
|
||||||
|
<div id="help-username_color" class="form-text">被封禁的用户无法修改用户名颜色。</div>
|
||||||
|
</div>
|
||||||
|
EOD);
|
||||||
|
} else if ($user['usergroup'] == 'T') {
|
||||||
|
$update_profile_form->appendHTML(<<<EOD
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="input-username_color" class="form-label">用户名颜色</label>
|
||||||
|
<input type="text" class="form-control" id="input-username_color" aria-describedby="help-username_color" value="灰色 - #707070" disabled>
|
||||||
|
<div id="help-username_color" class="form-text">临时用户无法修改用户名颜色。</div>
|
||||||
|
</div>
|
||||||
|
EOD);
|
||||||
|
} else {
|
||||||
|
$additional_colors = [];
|
||||||
|
|
||||||
|
if (isSuperUser($user)) {
|
||||||
|
$additional_colors['#9d3dcf'] = '紫色 - #9d3dcf';
|
||||||
|
}
|
||||||
|
|
||||||
|
$update_profile_form->addSelect('username_color', [
|
||||||
|
'div_class' => 'mb-3',
|
||||||
|
'label' => '用户名颜色',
|
||||||
|
'default_value' => $extra['username_color'],
|
||||||
|
'options' => $additional_colors + [
|
||||||
|
'#0d6efd' => '蓝色 - #0d6efd',
|
||||||
|
'#2da44e' => '绿色 - #2da44e',
|
||||||
|
'#e85aad' => '粉色 - #e85aad',
|
||||||
|
'#f32a38' => '红色 - #f32a38',
|
||||||
|
'#f57c00' => '橙色 - #f57c00',
|
||||||
|
'#00acc1' => '青色 - #00acc1',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
$update_profile_form->handle = function (&$vdata) use ($user) {
|
$update_profile_form->handle = function (&$vdata) use ($user) {
|
||||||
$data = [
|
$data = [
|
||||||
'email' => $vdata['email'],
|
'email' => $vdata['email'],
|
||||||
@ -301,7 +338,9 @@ EOD);
|
|||||||
'$.social.codeforces',
|
'$.social.codeforces',
|
||||||
$vdata['codeforces'],
|
$vdata['codeforces'],
|
||||||
'$.social.website',
|
'$.social.website',
|
||||||
$vdata['website']
|
$vdata['website'],
|
||||||
|
'$.username_color',
|
||||||
|
$_POST['username_color']
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
"where", ["username" => $user['username']]
|
"where", ["username" => $user['username']]
|
||||||
@ -376,6 +415,17 @@ EOD);
|
|||||||
}
|
}
|
||||||
$update_user_permissions_form->appendHTML(HTML::tag('span', [], UOJLocale::get('user::user group')));
|
$update_user_permissions_form->appendHTML(HTML::tag('span', [], UOJLocale::get('user::user group')));
|
||||||
$update_user_permissions_form->appendHTML(HTML::tag('span', ['class' => 'd-inline-block ms-3'], $type_text));
|
$update_user_permissions_form->appendHTML(HTML::tag('span', ['class' => 'd-inline-block ms-3'], $type_text));
|
||||||
|
$update_user_permissions_form->addSelect('user_type', [
|
||||||
|
'label' => '账号类型',
|
||||||
|
'options' => [
|
||||||
|
'student' => '学生',
|
||||||
|
'teacher' => '老师',
|
||||||
|
'system' => '系统',
|
||||||
|
],
|
||||||
|
'div_class' => 'my-3 row gy-2 gx-3 align-items-center',
|
||||||
|
'label_class' => 'form-label col-auto',
|
||||||
|
'select_class' => 'form-select w-auto col-auto',
|
||||||
|
]);
|
||||||
$update_user_permissions_form->appendHTML(HTML::tag('h3', ['class' => 'h5 mt-3'], '题目'));
|
$update_user_permissions_form->appendHTML(HTML::tag('h3', ['class' => 'h5 mt-3'], '题目'));
|
||||||
$update_user_permissions_form->addCheckbox('problems__view', [
|
$update_user_permissions_form->addCheckbox('problems__view', [
|
||||||
'checked' => $extra['permissions']['problems']['view'],
|
'checked' => $extra['permissions']['problems']['view'],
|
||||||
@ -707,6 +757,7 @@ EOD);
|
|||||||
DB::update([
|
DB::update([
|
||||||
"update user_info",
|
"update user_info",
|
||||||
"set", [
|
"set", [
|
||||||
|
"usertype" => $_POST['user_type'],
|
||||||
"extra" => json_encode($extra),
|
"extra" => json_encode($extra),
|
||||||
],
|
],
|
||||||
"where", [
|
"where", [
|
||||||
|
@ -87,14 +87,23 @@ function queryContestData($contest, $config = []) {
|
|||||||
$people = [];
|
$people = [];
|
||||||
|
|
||||||
if ($contest['extra_config']['individual_or_team'] == 'individual') {
|
if ($contest['extra_config']['individual_or_team'] == 'individual') {
|
||||||
$people = DB::selectAll([
|
$res = DB::selectAll([
|
||||||
"select contests_registrants.username, user_info.realname from contests_registrants",
|
"select contests_registrants.username, user_info.realname, user_info.extra, user_info.usergroup from contests_registrants",
|
||||||
"inner join user_info on contests_registrants.username = user_info.username",
|
"inner join user_info on contests_registrants.username = user_info.username",
|
||||||
"where", [
|
"where", [
|
||||||
"contest_id" => $contest['id'],
|
"contest_id" => $contest['id'],
|
||||||
"has_participated" => 1
|
"has_participated" => 1
|
||||||
]
|
]
|
||||||
], DB::NUM);
|
], DB::NUM);
|
||||||
|
foreach ($res as $row) {
|
||||||
|
$extra = json_decode($row[2], true);
|
||||||
|
$people[] = [
|
||||||
|
$row[0],
|
||||||
|
trim(HTML::escape($row[1])),
|
||||||
|
null,
|
||||||
|
UOJUser::getUserColor2($row[3], $extra['username_color']),
|
||||||
|
];
|
||||||
|
}
|
||||||
} elseif ($contest['extra_config']['individual_or_team'] == 'team') {
|
} elseif ($contest['extra_config']['individual_or_team'] == 'team') {
|
||||||
$res = DB::selectAll([
|
$res = DB::selectAll([
|
||||||
"select user_info.username, null, user_info.extra from contests_registrants, user_info",
|
"select user_info.username, null, user_info.extra from contests_registrants, user_info",
|
||||||
@ -106,11 +115,15 @@ function queryContestData($contest, $config = []) {
|
|||||||
], DB::NUM);
|
], DB::NUM);
|
||||||
foreach ($res as $row) {
|
foreach ($res as $row) {
|
||||||
$extra = json_decode($row[2], true);
|
$extra = json_decode($row[2], true);
|
||||||
$row[2] = [
|
$people[] = [
|
||||||
|
$row[0],
|
||||||
|
null,
|
||||||
|
[
|
||||||
'team_name' => $extra['acm']['team_name'],
|
'team_name' => $extra['acm']['team_name'],
|
||||||
'members' => $extra['acm']['members']
|
'members' => $extra['acm']['members'],
|
||||||
|
],
|
||||||
|
null,
|
||||||
];
|
];
|
||||||
$people[] = $row;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,7 +386,7 @@ function calcStandings($contest, $contest_data, &$score, &$standings, $cfg = [])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// standings: rank => score, penalty, [username, realname], virtual_rank, ?review
|
// standings: rank => score, penalty, [username, realname, null|array, null|color], virtual_rank, ?review
|
||||||
$standings = [];
|
$standings = [];
|
||||||
foreach ($contest_data['people'] as $person) {
|
foreach ($contest_data['people'] as $person) {
|
||||||
$cur = array(0, 0, $person);
|
$cur = array(0, 0, $person);
|
||||||
|
@ -735,7 +735,7 @@ function newSubmissionForm($form_name, $requirement, $zip_file_name_gen, $handle
|
|||||||
$stat = $zip_file->statName($req['file_name']);
|
$stat = $zip_file->statName($req['file_name']);
|
||||||
|
|
||||||
if ($req['type'] == 'source code') {
|
if ($req['type'] == 'source code') {
|
||||||
$max_size = isset($req['size']) ? (int)$req['size'] : 50;
|
$max_size = isset($req['size']) ? (int)$req['size'] : 100;
|
||||||
if ($stat['size'] > $max_size * 1024) {
|
if ($stat['size'] > $max_size * 1024) {
|
||||||
$zip_file->close();
|
$zip_file->close();
|
||||||
unlink(UOJContext::storagePath() . $zip_file_name);
|
unlink(UOJContext::storagePath() . $zip_file_name);
|
||||||
|
@ -1009,62 +1009,6 @@ function echoUOJPageFooter($config = array()) {
|
|||||||
uojIncludeView('page-footer', $config);
|
uojIncludeView('page-footer', $config);
|
||||||
}
|
}
|
||||||
|
|
||||||
function echoRanklist($config = []) {
|
|
||||||
$header_row = '';
|
|
||||||
$header_row .= '<tr>';
|
|
||||||
$header_row .= '<th style="width: 5em;">#</th>';
|
|
||||||
$header_row .= '<th style="width: 14em;">' . UOJLocale::get('username') . '</th>';
|
|
||||||
$header_row .= '<th style="width: 50em;">' . UOJLocale::get('motto') . '</th>';
|
|
||||||
$header_row .= '<th style="width: 5em;">' . UOJLocale::get('solved') . '</th>';
|
|
||||||
$header_row .= '</tr>';
|
|
||||||
|
|
||||||
$parsedown = HTML::parsedown();
|
|
||||||
$purifier = HTML::purifier_inline();
|
|
||||||
$users = [];
|
|
||||||
$print_row = function ($user, $now_cnt) use (&$users, $config, $purifier, $parsedown) {
|
|
||||||
if (!$users) {
|
|
||||||
if ($now_cnt == 1) {
|
|
||||||
$rank = 1;
|
|
||||||
} else {
|
|
||||||
$rank = DB::selectCount("select count(*) from (select b.username as username, count(*) as accepted from best_ac_submissions a inner join user_info b on a.submitter = b.username group by username) as derived where accepted > {$user['ac_num']}") + 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$rank = $now_cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
$user['rank'] = $rank;
|
|
||||||
|
|
||||||
echo '<tr>';
|
|
||||||
echo '<td>' . $user['rank'] . '</td>';
|
|
||||||
echo '<td>' . UOJUser::getLink($user['username']) . '</td>';
|
|
||||||
echo "<td>";
|
|
||||||
echo $purifier->purify($parsedown->line($user['motto']));
|
|
||||||
echo "</td>";
|
|
||||||
echo '<td>' . $user['ac_num'] . '</td>';
|
|
||||||
echo '</tr>';
|
|
||||||
|
|
||||||
$users[] = $user;
|
|
||||||
};
|
|
||||||
|
|
||||||
$from = 'user_info';
|
|
||||||
$col_names = ['user_info.username as username', 'ac_num', 'motto'];
|
|
||||||
$cond = '1';
|
|
||||||
$tail = 'group by user_info.username order by ac_num desc, user_info.username asc';
|
|
||||||
|
|
||||||
if (isset($config['group_id'])) {
|
|
||||||
$group_id = $config['group_id'];
|
|
||||||
$from = "user_info inner join groups_users on (user_info.username = groups_users.username and groups_users.group_id = {$group_id})";
|
|
||||||
$config['pagination_cond'] = "group_id = {$group_id}";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($config['top10'])) {
|
|
||||||
$tail .= ' limit 10';
|
|
||||||
}
|
|
||||||
|
|
||||||
$config['get_row_index'] = '';
|
|
||||||
echoLongTable($col_names, $from, $cond, $tail, $header_row, $print_row, $config);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== uoj.ac =====
|
// ===== uoj.ac =====
|
||||||
|
|
||||||
function echoJudgmentDetails($raw_details, $styler, $name) {
|
function echoJudgmentDetails($raw_details, $styler, $name) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
|
'new contest' => 'New Contest',
|
||||||
'current or upcoming contests' => 'Current or upcoming contests',
|
'current or upcoming contests' => 'Current or upcoming contests',
|
||||||
'ended contests' => 'Ended contests',
|
'ended contests' => 'Ended contests',
|
||||||
'back to the contest' => 'Back to the contest',
|
'back to the contest' => 'Back to the contest',
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
|
'new contest' => '新建比赛',
|
||||||
'current or upcoming contests' => '正在进行或即将到来的比赛',
|
'current or upcoming contests' => '正在进行或即将到来的比赛',
|
||||||
'ended contests' => '已结束的比赛',
|
'ended contests' => '已结束的比赛',
|
||||||
'back to the contest' => '返回比赛',
|
'back to the contest' => '返回比赛',
|
||||||
|
@ -52,4 +52,7 @@ return [
|
|||||||
'hacks to me' => 'Hacks to me',
|
'hacks to me' => 'Hacks to me',
|
||||||
'difficulty' => 'Difficulty',
|
'difficulty' => 'Difficulty',
|
||||||
'show difficulty' => 'Show difficulty',
|
'show difficulty' => 'Show difficulty',
|
||||||
|
'tags' => 'Tags',
|
||||||
|
'historical score' => 'Historical Score',
|
||||||
|
'uploader' => 'Uploader',
|
||||||
];
|
];
|
||||||
|
@ -52,4 +52,7 @@ return [
|
|||||||
'hacks to me' => '我的被Hack记录',
|
'hacks to me' => '我的被Hack记录',
|
||||||
'difficulty' => '难度',
|
'difficulty' => '难度',
|
||||||
'show difficulty' => '显示难度',
|
'show difficulty' => '显示难度',
|
||||||
|
'tags' => '标签',
|
||||||
|
'historical score' => '历史分数',
|
||||||
|
'uploader' => '上传者',
|
||||||
];
|
];
|
||||||
|
@ -436,7 +436,11 @@ class HTML {
|
|||||||
$def->addElement('footer', 'Block', 'Flow', 'Common');
|
$def->addElement('footer', 'Block', 'Flow', 'Common');
|
||||||
|
|
||||||
$extra_allowed_html = [
|
$extra_allowed_html = [
|
||||||
'span' => ['data-realname' => 'Text', 'data-uoj-username' => 'Number'],
|
'span' => [
|
||||||
|
'class' => 'Enum#uoj-username',
|
||||||
|
'data-realname' => 'Text',
|
||||||
|
'data-color' => 'Color',
|
||||||
|
],
|
||||||
'img' => ['width' => 'Text'],
|
'img' => ['width' => 'Text'],
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -463,7 +467,11 @@ class HTML {
|
|||||||
'small' => [],
|
'small' => [],
|
||||||
'del' => [],
|
'del' => [],
|
||||||
'br' => [],
|
'br' => [],
|
||||||
'span' => ['data-realname' => 'Text', 'data-uoj-username' => 'Number'],
|
'span' => [
|
||||||
|
'class' => 'Enum#uoj-username',
|
||||||
|
'data-realname' => 'Text',
|
||||||
|
'data-color' => 'Color',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
$allowed_elements = [];
|
$allowed_elements = [];
|
||||||
@ -490,8 +498,8 @@ class HTML {
|
|||||||
return new HTMLPurifier($config);
|
return new HTMLPurifier($config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function parsedown() {
|
public static function parsedown($config = []) {
|
||||||
return new UOJMarkdown([
|
return new UOJMarkdown($config + [
|
||||||
'math' => [
|
'math' => [
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
'matchSingleDollar' => true
|
'matchSingleDollar' => true
|
||||||
|
@ -258,7 +258,7 @@ class UOJForm {
|
|||||||
'options' => [],
|
'options' => [],
|
||||||
'default_value' => '',
|
'default_value' => '',
|
||||||
'label' => '',
|
'label' => '',
|
||||||
'label_class' => 'form-check-label',
|
'label_class' => 'form-label',
|
||||||
'help' => '',
|
'help' => '',
|
||||||
'help_class' => 'form-text',
|
'help_class' => 'form-text',
|
||||||
'disabled' => false,
|
'disabled' => false,
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
class UOJMarkdown extends ParsedownMath {
|
class UOJMarkdown extends ParsedownMath {
|
||||||
public function __construct($options = '') {
|
public function __construct($options = '') {
|
||||||
if (method_exists(get_parent_class(),"__construct")) {
|
if (method_exists(get_parent_class(), "__construct")) {
|
||||||
parent::__construct($options);
|
parent::__construct($options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->options['username_with_color'] = $options['username_with_color'] ?: false;
|
||||||
|
|
||||||
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
|
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
|
||||||
$this->InlineTypes['@'][] = 'UserMention';
|
$this->InlineTypes['@'][] = 'UserMention';
|
||||||
$this->inlineMarkerList .= '@';
|
$this->inlineMarkerList .= '@';
|
||||||
@ -54,16 +56,24 @@ class UOJMarkdown extends ParsedownMath {
|
|||||||
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
|
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
|
||||||
protected function inlineUserMention($Excerpt) {
|
protected function inlineUserMention($Excerpt) {
|
||||||
if (preg_match('/^@([^\s]+)/', $Excerpt['text'], $matches)) {
|
if (preg_match('/^@([^\s]+)/', $Excerpt['text'], $matches)) {
|
||||||
if (($user = UOJUser::query($matches[1])) && $user['usergroup'] != 'B') {
|
$mentioned_user = UOJUser::query($matches[1]);
|
||||||
|
|
||||||
|
if ($mentioned_user) {
|
||||||
|
$color = '#0d6efd';
|
||||||
|
|
||||||
|
if ($this->options['username_with_color']) {
|
||||||
|
$color = UOJUser::getUserColor($mentioned_user);
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'extent' => strlen($matches[0]),
|
'extent' => strlen($matches[0]),
|
||||||
'element' => [
|
'element' => [
|
||||||
'name' => 'span',
|
'name' => 'span',
|
||||||
'text' => '@' . $user['username'],
|
'text' => '@' . $mentioned_user['username'],
|
||||||
'attributes' => [
|
'attributes' => [
|
||||||
'class' => 'uoj-username',
|
'class' => 'uoj-username',
|
||||||
'data-realname' => $user['realname'],
|
'data-realname' => UOJUser::getRealname($mentioned_user),
|
||||||
'data-uoj-username' => 1,
|
'data-color' => $color,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -22,6 +22,7 @@ class UOJProblem {
|
|||||||
2300,
|
2300,
|
||||||
2400,
|
2400,
|
||||||
2500,
|
2500,
|
||||||
|
2600,
|
||||||
2700,
|
2700,
|
||||||
2900,
|
2900,
|
||||||
3100,
|
3100,
|
||||||
@ -43,13 +44,254 @@ class UOJProblem {
|
|||||||
2300 => '#ff8000',
|
2300 => '#ff8000',
|
||||||
2400 => '#ff8000',
|
2400 => '#ff8000',
|
||||||
2500 => '#ff8000',
|
2500 => '#ff8000',
|
||||||
|
2600 => '#ff0000',
|
||||||
2700 => '#ff0000',
|
2700 => '#ff0000',
|
||||||
2900 => '#ff0000',
|
2900 => '#ff0000',
|
||||||
3100 => '#ff0000',
|
3100 => '#aa0000',
|
||||||
3300 => '#aa0000',
|
3300 => '#aa0000',
|
||||||
3500 => '#aa0000',
|
3500 => '#aa0000',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public static array $categories = [
|
||||||
|
'算法基础' => [
|
||||||
|
'暴力',
|
||||||
|
'枚举',
|
||||||
|
'模拟',
|
||||||
|
'递归与分治',
|
||||||
|
'贪心',
|
||||||
|
'排序',
|
||||||
|
'前缀和与差分',
|
||||||
|
'二分',
|
||||||
|
'倍增',
|
||||||
|
'构造',
|
||||||
|
'打表',
|
||||||
|
],
|
||||||
|
'搜索' => [
|
||||||
|
'深度优先搜索',
|
||||||
|
'广度优先搜索',
|
||||||
|
'双向搜索',
|
||||||
|
'启发式搜索',
|
||||||
|
'A*',
|
||||||
|
'IDA*',
|
||||||
|
'迭代加深',
|
||||||
|
'回溯法',
|
||||||
|
'Dancing Links',
|
||||||
|
],
|
||||||
|
'动态规划' => [
|
||||||
|
'记忆化搜索',
|
||||||
|
'线性 DP',
|
||||||
|
'背包 DP',
|
||||||
|
'区间 DP',
|
||||||
|
'树形 DP',
|
||||||
|
'状压 DP',
|
||||||
|
'数位 DP',
|
||||||
|
'DAG 上 DP',
|
||||||
|
'插头 DP',
|
||||||
|
'概率 DP',
|
||||||
|
'单调队列优化 DP',
|
||||||
|
'斜率优化 DP',
|
||||||
|
'四边形不等式优化 DP',
|
||||||
|
],
|
||||||
|
'计算几何' => [
|
||||||
|
'Pick 定理',
|
||||||
|
'三角剖分',
|
||||||
|
'凸包',
|
||||||
|
'扫描线',
|
||||||
|
'旋转卡壳',
|
||||||
|
'半平面交',
|
||||||
|
'平面最近点对',
|
||||||
|
'随机增量法',
|
||||||
|
'反演变换',
|
||||||
|
],
|
||||||
|
'数学' => [
|
||||||
|
'位运算',
|
||||||
|
'快速幂',
|
||||||
|
'高精度',
|
||||||
|
'生成函数',
|
||||||
|
'指数生成函数',
|
||||||
|
'向量',
|
||||||
|
'矩阵',
|
||||||
|
'高斯消元',
|
||||||
|
'线性基',
|
||||||
|
'线性规划',
|
||||||
|
'容斥',
|
||||||
|
'组合计数',
|
||||||
|
'离散对数',
|
||||||
|
'单纯形算法',
|
||||||
|
'概率',
|
||||||
|
'置换群',
|
||||||
|
'斐波那契数列',
|
||||||
|
'牛顿迭代法',
|
||||||
|
'数值积分',
|
||||||
|
'分块打表',
|
||||||
|
],
|
||||||
|
'数论' => [
|
||||||
|
'最大公约数',
|
||||||
|
'分解质因数',
|
||||||
|
'欧拉函数',
|
||||||
|
'筛法',
|
||||||
|
'欧拉定理',
|
||||||
|
'费马小定理',
|
||||||
|
'类欧几里得算法',
|
||||||
|
'翡蜀定理',
|
||||||
|
'乘法逆元',
|
||||||
|
'线性同余方程',
|
||||||
|
'Meissel-Lehmer 算法',
|
||||||
|
'二次剩余',
|
||||||
|
'BSGS',
|
||||||
|
'原根',
|
||||||
|
'卢卡斯定理',
|
||||||
|
'莫比乌斯反演',
|
||||||
|
'拉格朗日反演',
|
||||||
|
'杜教筛',
|
||||||
|
'Powerful Number 筛',
|
||||||
|
'Min_25 筛',
|
||||||
|
'洲阁筛',
|
||||||
|
'连分数',
|
||||||
|
'Stern-Brocot 数与 Farey 序列',
|
||||||
|
'Pell 方程',
|
||||||
|
],
|
||||||
|
'字符串' => [
|
||||||
|
'字符串哈希',
|
||||||
|
'字典树',
|
||||||
|
'KMP',
|
||||||
|
'Boyer-Moore',
|
||||||
|
'Z 函数(扩展 KMP)',
|
||||||
|
'AC 自动机',
|
||||||
|
'后缀数组',
|
||||||
|
'后缀自动机',
|
||||||
|
'后缀平衡树',
|
||||||
|
'广义后缀自动机',
|
||||||
|
'Manacher',
|
||||||
|
'回文树',
|
||||||
|
'序列自动机',
|
||||||
|
'最小表示法',
|
||||||
|
'Lyndon 分解',
|
||||||
|
],
|
||||||
|
'图论' => [
|
||||||
|
'拓扑排序',
|
||||||
|
'最短路',
|
||||||
|
'K 短路',
|
||||||
|
'同余最短路',
|
||||||
|
'虚树',
|
||||||
|
'树分治',
|
||||||
|
'动态树分治',
|
||||||
|
'树哈希',
|
||||||
|
'树上启发式合并',
|
||||||
|
'AHU 算法',
|
||||||
|
'矩阵树定理',
|
||||||
|
'最小生成树',
|
||||||
|
'最小树形图',
|
||||||
|
'最小直径生成树',
|
||||||
|
'斯坦纳树',
|
||||||
|
'拆点',
|
||||||
|
'差分约束',
|
||||||
|
'强连通分量',
|
||||||
|
'双连通分量',
|
||||||
|
'割点与桥',
|
||||||
|
'圆方树',
|
||||||
|
'2-SAT',
|
||||||
|
'欧拉图',
|
||||||
|
'哈密顿图',
|
||||||
|
'最小环',
|
||||||
|
'平面图',
|
||||||
|
'网络流',
|
||||||
|
'最大流',
|
||||||
|
'最小割',
|
||||||
|
'费用流',
|
||||||
|
'上下界网络流',
|
||||||
|
'Stoer-Wagner 算法',
|
||||||
|
'二分图',
|
||||||
|
'二分图最大匹配',
|
||||||
|
'二分图最大权匹配',
|
||||||
|
'一般图最大匹配',
|
||||||
|
'一般图最大权匹配',
|
||||||
|
'Prufer 序列',
|
||||||
|
'LGV 引理',
|
||||||
|
'弦图',
|
||||||
|
],
|
||||||
|
'组合数学' => [
|
||||||
|
'排列组合',
|
||||||
|
'卡特兰数',
|
||||||
|
'斯特林数',
|
||||||
|
'贝尔数',
|
||||||
|
'伯努利数',
|
||||||
|
'康托展开',
|
||||||
|
'容斥原理',
|
||||||
|
'抽屉原理',
|
||||||
|
'欧拉数',
|
||||||
|
],
|
||||||
|
'数据结构' => [
|
||||||
|
'栈',
|
||||||
|
'队列',
|
||||||
|
'链表',
|
||||||
|
'哈希表',
|
||||||
|
'并查集',
|
||||||
|
'二叉堆',
|
||||||
|
'配对堆',
|
||||||
|
'树状数组',
|
||||||
|
'线段树',
|
||||||
|
'平衡树',
|
||||||
|
'左偏树',
|
||||||
|
'块状数组',
|
||||||
|
'块状链表',
|
||||||
|
'树分块',
|
||||||
|
'Sqrt Tree',
|
||||||
|
'可持久化数据结构',
|
||||||
|
'单调栈',
|
||||||
|
'单调队列',
|
||||||
|
'ST 表',
|
||||||
|
'树套树',
|
||||||
|
'李超线段树',
|
||||||
|
'区间最值操作与区间历史最值',
|
||||||
|
'划分树',
|
||||||
|
'跳表',
|
||||||
|
'K-D Tree',
|
||||||
|
'珂朵莉树',
|
||||||
|
'动态树',
|
||||||
|
'析合树',
|
||||||
|
],
|
||||||
|
'多项式' => [
|
||||||
|
'拉格朗日插值',
|
||||||
|
'快速傅里叶变换',
|
||||||
|
'快速数论变换',
|
||||||
|
'快速沃尔什变换',
|
||||||
|
'多项式求逆',
|
||||||
|
'多项式开方',
|
||||||
|
'多项式除法与取模',
|
||||||
|
'多项式对数函数与指数函数',
|
||||||
|
'多项式牛顿迭代',
|
||||||
|
'多项式多点求值与快速插值',
|
||||||
|
'多项式三角函数',
|
||||||
|
'多项式反三角函数',
|
||||||
|
'常系数齐次线性递推',
|
||||||
|
],
|
||||||
|
'博弈论' => [
|
||||||
|
'不平等博弈',
|
||||||
|
'SG 函数',
|
||||||
|
'Nim 游戏',
|
||||||
|
'Anti-Nim',
|
||||||
|
'纳什均衡',
|
||||||
|
],
|
||||||
|
'杂项' => [
|
||||||
|
'构造',
|
||||||
|
'离散化',
|
||||||
|
'CDQ 分治',
|
||||||
|
'整体二分',
|
||||||
|
'分块',
|
||||||
|
'莫队',
|
||||||
|
'分数规划',
|
||||||
|
'随机化',
|
||||||
|
'模拟退火',
|
||||||
|
'爬山法',
|
||||||
|
'悬线法',
|
||||||
|
'编译原理',
|
||||||
|
'复杂度分析',
|
||||||
|
'语义分析',
|
||||||
|
'底层优化',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
public static function query($id) {
|
public static function query($id) {
|
||||||
if (!isset($id) || !validateUInt($id)) {
|
if (!isset($id) || !validateUInt($id)) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -22,7 +22,7 @@ class UOJRanklist {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$last_user = null;
|
$last_user = null;
|
||||||
$parsedown = HTML::parsedown();
|
$parsedown = HTML::parsedown(['username_with_color' => true]);
|
||||||
$purifier = HTML::purifier_inline();
|
$purifier = HTML::purifier_inline();
|
||||||
$print_row = function ($user, $now_cnt) use (&$last_user, &$conds, &$parsedown, &$purifier) {
|
$print_row = function ($user, $now_cnt) use (&$last_user, &$conds, &$parsedown, &$purifier) {
|
||||||
if ($last_user === null) {
|
if ($last_user === null) {
|
||||||
@ -138,7 +138,7 @@ class UOJRanklist {
|
|||||||
$header_row .= '</tr>';
|
$header_row .= '</tr>';
|
||||||
|
|
||||||
$last_user = null;
|
$last_user = null;
|
||||||
$parsedown = HTML::parsedown();
|
$parsedown = HTML::parsedown(['username_with_color' => true]);
|
||||||
$purifier = HTML::purifier_inline();
|
$purifier = HTML::purifier_inline();
|
||||||
$print_row = function ($user, $now_cnt) use (&$last_user, &$conds, &$parsedown, &$purifier) {
|
$print_row = function ($user, $now_cnt) use (&$last_user, &$conds, &$parsedown, &$purifier) {
|
||||||
if ($last_user === null) {
|
if ($last_user === null) {
|
||||||
|
@ -210,6 +210,43 @@ class UOJUser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getRealname($user) {
|
||||||
|
$realname = $user['realname'];
|
||||||
|
|
||||||
|
if ($user['usertype'] == 'teacher') {
|
||||||
|
$realname .= '老师';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $realname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getUserColor($user) {
|
||||||
|
$extra = UOJUser::getExtra($user);
|
||||||
|
|
||||||
|
return UOJUser::getUserColor2($user['usergroup'], $extra['username_color']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getUserColor2($usergroup, $custom_color = null) {
|
||||||
|
if ($usergroup == 'B') {
|
||||||
|
return '#996600';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($usergroup == 'T') {
|
||||||
|
return '#707070';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($usergroup == 'S') {
|
||||||
|
return $custom_color ?: '#9d3dcf';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 前管理员设置颜色为紫色的,颜色改为蓝色
|
||||||
|
if ($custom_color == '#9d3dcf') {
|
||||||
|
return '#0d6efd';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $custom_color ?: '#0d6efd';
|
||||||
|
}
|
||||||
|
|
||||||
public static function getLink($user) {
|
public static function getLink($user) {
|
||||||
if (is_string($user)) {
|
if (is_string($user)) {
|
||||||
$info = UOJUser::query($user);
|
$info = UOJUser::query($user);
|
||||||
@ -221,14 +258,18 @@ class UOJUser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($user['usergroup'] == 'B') {
|
$realname = UOJUser::getRealname($user);
|
||||||
return HTML::tag('a', ['class' => 'text-danger fw-bold', 'href' => "/user/{$user['username']}"], $user['username']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 未登录不可查看真实姓名
|
// 未登录不可查看真实姓名
|
||||||
$realname = Auth::check() ? $user['realname'] : '';
|
if (!Auth::check()) {
|
||||||
|
$realname = '';
|
||||||
|
}
|
||||||
|
|
||||||
return HTML::tag('span', ['class' => 'uoj-username', 'data-realname' => trim(HTML::escape($realname))], $user['username']);
|
return HTML::tag('span', [
|
||||||
|
'class' => 'uoj-username',
|
||||||
|
'data-color' => UOJUser::getUserColor($user),
|
||||||
|
'data-realname' => trim(HTML::escape($realname)),
|
||||||
|
], $user['username']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getUpdatedExtraVisitHistory($history, $cur) {
|
public static function getUpdatedExtraVisitHistory($history, $cur) {
|
||||||
@ -290,6 +331,7 @@ class UOJUser {
|
|||||||
'show_email' => 'all',
|
'show_email' => 'all',
|
||||||
'show_qq' => 'all',
|
'show_qq' => 'all',
|
||||||
'avatar_source' => 'gravatar',
|
'avatar_source' => 'gravatar',
|
||||||
|
'username_color' => isSuperUser($user) ? '#9d3dcf' : '#0d6efd',
|
||||||
]);
|
]);
|
||||||
return $extra;
|
return $extra;
|
||||||
}
|
}
|
||||||
@ -340,6 +382,7 @@ class UOJUser {
|
|||||||
$extra = UOJUser::getExtra($user);
|
$extra = UOJUser::getExtra($user);
|
||||||
$cur = [
|
$cur = [
|
||||||
'addr' => $info['remote_addr'],
|
'addr' => $info['remote_addr'],
|
||||||
|
'forwarded_addr' => $info['http_x_forwarded_for'],
|
||||||
'ua' => substr($info['http_user_agent'], 0, UOJUser::MAX_UA_LEN),
|
'ua' => substr($info['http_user_agent'], 0, UOJUser::MAX_UA_LEN),
|
||||||
'last' => UOJTime::$time_now_str
|
'last' => UOJTime::$time_now_str
|
||||||
];
|
];
|
||||||
|
@ -92,9 +92,9 @@ Route::group(
|
|||||||
Route::any('/click-zan', '/click_zan.php');
|
Route::any('/click-zan', '/click_zan.php');
|
||||||
|
|
||||||
// Apps
|
// Apps
|
||||||
Route::any('/image_hosting', '/image_hosting/index.php');
|
Route::any('/image_hosting', '/app/image_hosting/index.php');
|
||||||
Route::get('/image_hosting/{image_name}.png', '/image_hosting/get_image.php');
|
Route::get('/image_hosting/{image_name}.png', '/app/image_hosting/get_image.php');
|
||||||
Route::any('/html2markdown', '/html2markdown.php');
|
Route::any('/html2markdown', '/app/html2markdown.php');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -19,6 +19,8 @@ class Parsedown
|
|||||||
|
|
||||||
const version = '1.7.4';
|
const version = '1.7.4';
|
||||||
|
|
||||||
|
protected $options = [];
|
||||||
|
|
||||||
# ~
|
# ~
|
||||||
|
|
||||||
function text($text)
|
function text($text)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
$reviews = [];
|
$reviews = [];
|
||||||
|
|
||||||
$parsedown = HTML::parsedown();
|
$parsedown = HTML::parsedown(['username_with_color' => true]);
|
||||||
$purifier = HTML::purifier_inline();
|
$purifier = HTML::purifier_inline();
|
||||||
|
|
||||||
foreach ($contest_data['people'] as $person) {
|
foreach ($contest_data['people'] as $person) {
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
function(row) {
|
function(row) {
|
||||||
var col_tr = '<tr>';
|
var col_tr = '<tr>';
|
||||||
col_tr += '<td>' + row[3] + '</td>';
|
col_tr += '<td>' + row[3] + '</td>';
|
||||||
col_tr += '<td>' + getUserLink(row[2][0], row[2][1]) + '</td>';
|
col_tr += '<td>' + getUserLink(row[2][0], row[2][1], row[2][3]) + '</td>';
|
||||||
col_tr += '<td>' + '<div><span class="uoj-score" data-max="' + problems.length * 100 + '" style="color:' + getColOfScore(row[0] / problems.length) + '">' + row[0] + '</span></div>' + '<div>' + getPenaltyTimeStr(row[1]) + '</div></td>';
|
col_tr += '<td>' + '<div><span class="uoj-score" data-max="' + problems.length * 100 + '" style="color:' + getColOfScore(row[0] / problems.length) + '">' + row[0] + '</span></div>' + '<div>' + getPenaltyTimeStr(row[1]) + '</div></td>';
|
||||||
for (var i = 0; i < problems.length; i++) {
|
for (var i = 0; i < problems.length; i++) {
|
||||||
col = score[row[2][0]][i];
|
col = score[row[2][0]][i];
|
||||||
|
@ -4,11 +4,6 @@ if (!isset($ShowPageFooter)) {
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
|
||||||
$(document).ready(function() {
|
|
||||||
[...document.querySelectorAll('[data-bs-toggle="tooltip"]')].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<?php if ($ShowPageFooter) : ?>
|
<?php if ($ShowPageFooter) : ?>
|
||||||
<?php if (UOJNotice::shouldConstantlyCheckNotice()) : ?>
|
<?php if (UOJNotice::shouldConstantlyCheckNotice()) : ?>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
@ -102,6 +102,9 @@ if (!isset($ShowPageHeader)) {
|
|||||||
<!-- Color converter -->
|
<!-- Color converter -->
|
||||||
<?= HTML::js_src('/js/color-converter.min.js') ?>
|
<?= HTML::js_src('/js/color-converter.min.js') ?>
|
||||||
|
|
||||||
|
<!-- Clipboard Polyfill -->
|
||||||
|
<?= HTML::js_src('/js/clipboard-polyfill.overwrite-globals.es5.min.js') ?>
|
||||||
|
|
||||||
<!-- uoj -->
|
<!-- uoj -->
|
||||||
<?= HTML::js_src('/js/uoj.js?v=' . UOJConfig::$data['profile']['s2oj-version']) ?>
|
<?= HTML::js_src('/js/uoj.js?v=' . UOJConfig::$data['profile']['s2oj-version']) ?>
|
||||||
|
|
||||||
@ -175,7 +178,7 @@ if (!isset($ShowPageHeader)) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script id="MathJax-script" src="<?= HTML::url('/lib/MathJax/tex-mml-chtml.js') ?>"></script>
|
<script id="MathJax-script" src="<?= HTML::url('/js/mathjax3/tex-mml-chtml.js') ?>"></script>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
<?php if (isset($REQUIRE_LIB['jquery.form'])) : ?>
|
<?php if (isset($REQUIRE_LIB['jquery.form'])) : ?>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
$purifier = HTML::purifier_inline();
|
$purifier = HTML::purifier_inline();
|
||||||
$parsedown = HTML::parsedown();
|
$parsedown = HTML::parsedown(['username_with_color' => true]);
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<?php if (Auth::check()) : ?>
|
<?php if (Auth::check()) : ?>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="card-text">
|
<div class="card-text">
|
||||||
<?= HTML::purifier_inline()->purify(HTML::parsedown()->line($user['motto'])) ?>
|
<?= HTML::purifier_inline()->purify(HTML::parsedown(['username_with_color' => true])->line($user['motto'])) ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="list-group list-group-flush">
|
<ul class="list-group list-group-flush">
|
||||||
|
@ -25,6 +25,11 @@ a {
|
|||||||
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.uoj-realname {
|
||||||
|
font-size: 90%;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
.h1 {
|
.h1 {
|
||||||
/* font-size: 2.5rem; */
|
/* font-size: 2.5rem; */
|
||||||
@ -164,7 +169,7 @@ h6,
|
|||||||
|
|
||||||
.card-uoj-tle > .card-header:hover,
|
.card-uoj-tle > .card-header:hover,
|
||||||
.card-uoj-tle > div.card-header > div > .uoj-status-text {
|
.card-uoj-tle > div.card-header > div > .uoj-status-text {
|
||||||
color: sandybrown;
|
color: #f4a460;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-uoj-wrong > .card-header:hover,
|
.card-uoj-wrong > .card-header:hover,
|
||||||
|
@ -60,14 +60,6 @@ setWebConf(){
|
|||||||
# Set webroot path
|
# Set webroot path
|
||||||
ln -sf /opt/uoj/web /var/www/uoj
|
ln -sf /opt/uoj/web /var/www/uoj
|
||||||
chown -R www-data /var/www/uoj/app/storage
|
chown -R www-data /var/www/uoj/app/storage
|
||||||
# Set web config file
|
|
||||||
php7.4 -a <<UOJEOF
|
|
||||||
\$config = include '/var/www/uoj/app/.default-config.php';
|
|
||||||
\$config['database']['host']='$_database_host_';
|
|
||||||
\$config['database']['password']='$_database_password_';
|
|
||||||
\$config['judger']['socket']['port']='$_judger_socket_port_';
|
|
||||||
file_put_contents('/var/www/uoj/app/.config.php', "<?php\nreturn ".str_replace('\'_httpHost_\'','UOJContext::requestDomain()',var_export(\$config, true)).";\n");
|
|
||||||
UOJEOF
|
|
||||||
# Prepare local sandbox
|
# Prepare local sandbox
|
||||||
cd /opt/uoj/judger/uoj_judger
|
cd /opt/uoj/judger/uoj_judger
|
||||||
cat >include/uoj_work_path.h <<UOJEOF
|
cat >include/uoj_work_path.h <<UOJEOF
|
||||||
@ -84,9 +76,6 @@ initProgress(){
|
|||||||
#Set uoj_data path
|
#Set uoj_data path
|
||||||
mkdir -p /var/uoj_data/upload
|
mkdir -p /var/uoj_data/upload
|
||||||
chown -R www-data:www-data /var/uoj_data
|
chown -R www-data:www-data /var/uoj_data
|
||||||
#Replace password placeholders
|
|
||||||
sed -i -e "s/salt0/$_salt0_/g" -e "s/salt1/$_salt1_/g" -e "s/salt2/$_salt2_/g" -e "s/salt3/$_salt3_/g" -e "s/_judger_socket_password_/$_judger_socket_password_/g" /var/www/uoj/app/.config.php
|
|
||||||
sed -i -e "s/'protocol' => 'http'/'protocol' => '$_uoj_protocol_'/g" /var/www/uoj/app/.config.php
|
|
||||||
#Start services
|
#Start services
|
||||||
service ntp restart
|
service ntp restart
|
||||||
service apache2 restart
|
service apache2 restart
|
||||||
|
2
web/js/clipboard-polyfill.overwrite-globals.es5.min.js
vendored
Normal file
2
web/js/clipboard-polyfill.overwrite-globals.es5.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/*! clipboard-polyfill v4.0.0-rc8 | MIT License | github.com/zenorocha/clipboard.js */
|
||||||
|
"use strict";!function(){var e="text/plain";(function(){(console.warn||console.log).apply(console,arguments)}).bind("[clipboard-polyfill]");var n,t,r,o="undefined"==typeof window?void 0:window,i="undefined"==typeof globalThis?void 0:globalThis,a=null!=(r=null==(n=o)?void 0:n.Promise)?r:null==(t=i)?void 0:t.Promise;var u,l,c,d,f,v="undefined"==typeof navigator?void 0:navigator,s=null==v?void 0:v.clipboard,p=null==(u=null==s?void 0:s.read)?void 0:u.bind(s),b=null==(l=null==s?void 0:s.readText)?void 0:l.bind(s),m=null==(c=null==s?void 0:s.write)?void 0:c.bind(s),y=null==(d=null==s?void 0:s.writeText)?void 0:d.bind(s),w=null==(f=o)?void 0:f.ClipboardItem,h=function(){if(!a)throw new Error("No `Promise` implementation available for `clipboard-polyfill`. Consider using: https://github.com/lgarron/clipboard-polyfill#flat-file-version-with-promise-included");return a}(),g=o;function x(){return"undefined"==typeof ClipboardEvent&&void 0!==(null==g?void 0:g.clipboardData)&&void 0!==(null==g?void 0:g.clipboardData.setData)}function E(n,t,r){for(var o in n.success=!0,t){var i=t[o],a=r.clipboardData;a.setData(o,i),o===e&&a.getData(o)!==i&&(n.success=!1)}r.preventDefault()}function C(e){var n={success:!1},t=E.bind(this,n,e);document.addEventListener("copy",t);try{document.execCommand("copy")}finally{document.removeEventListener("copy",t)}return n.success}function T(e,n){D(e);var t=C(n);return S(),t}function D(e){var n=document.getSelection();if(n){var t=document.createRange();t.selectNodeContents(e),n.removeAllRanges(),n.addRange(t)}}function S(){var e=document.getSelection();e&&e.removeAllRanges()}function A(n){var t,r=e in n;if(x()){if(!r)throw new Error("No `text/plain` value was specified.");if(t=n[e],g.clipboardData.setData("Text",t))return!0;throw new Error("Copying failed, possibly because the user rejected it.")}return!!C(n)||(navigator.userAgent.indexOf("Edge")>-1||(!!T(document.body,n)||(!!function(e){var n=document.createElement("div");n.setAttribute("style","-webkit-user-select: text !important"),n.textContent="temporary element",document.body.appendChild(n);var t=T(n,e);return document.body.removeChild(n),t}(n)||!!function(e){var n=document.createElement("div");n.setAttribute("style","-webkit-user-select: text !important");var t=n;n.attachShadow&&(t=n.attachShadow({mode:"open"}));var r=document.createElement("span");r.innerText=e,t.appendChild(r),document.body.appendChild(n),D(r);var o=document.execCommand("copy");return S(),document.body.removeChild(n),o}(n[e]))))}function R(e,n){var t=[];for(var r in e){var o=e[r];t.push(n(o))}return h.all(t).then((function(n){for(var t={},r=0;r<e.length;r++)t[e[r]]=n[r];return t}))}var k=h.resolve(),L=function(){return h.resolve(!0)},N=h.resolve(!1);function O(e){return new h((function(n,t){try{n(e())}catch(e){t(e)}}))}function P(n){if(!A(function(n){var t={};return t[e]=n,t}(n)))throw new Error("writeText() failed")}function j(){return O((function(){if(b)return b();if(x()){var e=function(){var e=g.clipboardData.getData("Text");if(""===e)throw new Error("Empty clipboard or could not read plain text from clipboard");return e}();return h.resolve(e)}throw new Error("Read is not supported in your browser.")}))}function I(e,n){for(var t in e){if(-1!==e[t].types.indexOf(n))return!0}return!1}var B=function(e,n){var t,r=Object.keys(e),o={};for(var i in e){var a=e[i];o[i]="string"==typeof a?F(i,a):a}return{types:r,presentationStyle:null!=(t=null==n?void 0:n.presentationStyle)?t:"unspecified",getType:function(e){return h.resolve(o[e])}}};function F(e,n){return new Blob([n],{type:e})}function q(e){return R(e.types,(function(n){return e.getType(n)})).then((function(n){var t={};return e.presentationStyle&&(t.presentationStyle=e.presentationStyle),new w(n,t)}))}function z(n){var t={};return t[e]=F(n,e),new B(t)}function G(e,n){return e.getType(n).then((function(e){return n=e,new h((function(e,t){var r=new FileReader;r.addEventListener("load",(function(){var n=r.result;"string"==typeof n?e(n):t("could not convert blob to string")})),r.readAsText(n)}));var n}))}navigator.clipboard||(navigator.clipboard={}),navigator.clipboard.read=function(){return O((function(){return p?p():j().then((function(e){return[z(e)]}))}))},navigator.clipboard.readText=j,navigator.clipboard.write=function(n){return O((function(){if(m&&w){var t=m;return h.all(n.map(q)).then((function(r){return t(r).then(L).catch((function(t){if(!I(n,e)&&!I(n,"text/html"))throw t;return N}))}))}return N})).then((function(t){if(t)return k;I(n,e);return function(e){return R(e.types,(function(n){return G(e,n)}))}(n[0]).then((function(e){if(!A(e))throw new Error("write() failed")}))}))},navigator.clipboard.writeText=function(e){return O((function(){return y?y(e).catch(P):h.resolve(P(e))}))},window.ClipboardItem=B}();
|
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user