mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2025-02-16 22:26: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/
|
||||
.php-cs-fixer.cache
|
||||
docker-compose.local.yml
|
||||
.config.php
|
||||
.config.development.php
|
||||
.config.local.php
|
||||
|
@ -979,6 +979,17 @@ CREATE TABLE `upgrades` (
|
||||
|
||||
LOCK TABLES `upgrades` WRITE;
|
||||
/*!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 */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
|
@ -60,15 +60,6 @@ services:
|
||||
volumes:
|
||||
- ./uoj_data/web/data:/var/uoj_data
|
||||
- ./uoj_data/web/storage:/opt/uoj/web/app/storage
|
||||
- ./.config.development.php:/opt/uoj/web/app/.config.php
|
||||
ports:
|
||||
- "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:
|
||||
- ./uoj_data/web/data:/var/uoj_data
|
||||
- ./uoj_data/web/storage:/opt/uoj/web/app/storage
|
||||
- ./.config.php:/opt/uoj/web/app/.config.php
|
||||
ports:
|
||||
- "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,
|
||||
'main' => [
|
||||
'protocol' => 'http',
|
||||
'host' => '_httpHost_',
|
||||
'port' => '80/443'
|
||||
'host' => UOJContext::requestDomain(),
|
||||
'port' => '80/443',
|
||||
],
|
||||
'blog' => [
|
||||
'protocol' => 'http',
|
||||
'host' => '_httpHost_',
|
||||
'port' => '80/443'
|
||||
'host' => UOJContext::requestDomain(),
|
||||
'port' => '80/443',
|
||||
]
|
||||
],
|
||||
'security' => [
|
||||
'user' => [
|
||||
'client_salt' => 'salt0'
|
||||
'client_salt' => 'salt0',
|
||||
],
|
||||
'cookie' => [
|
||||
'checksum_salt' => ['salt1', 'salt2', 'salt3']
|
||||
'checksum_salt' => ['salt1', 'salt2', 'salt3'],
|
||||
],
|
||||
],
|
||||
'mail' => [
|
||||
@ -43,17 +43,17 @@ return [
|
||||
'password' => '_mail_noreply_password_',
|
||||
'host' => 'smtp.local_uoj.ac',
|
||||
'secure' => 'tls',
|
||||
'port' => 587
|
||||
'port' => 587,
|
||||
]
|
||||
],
|
||||
'judger' => [
|
||||
'socket' => [
|
||||
'port' => '233',
|
||||
'password' => '_judger_socket_password_'
|
||||
'password' => '_judger_socket_password_',
|
||||
]
|
||||
],
|
||||
'switch' => [
|
||||
'blog-domain-mode' => 3,
|
||||
'open-register' => false
|
||||
]
|
||||
'open-register' => false,
|
||||
],
|
||||
];
|
||||
|
@ -79,19 +79,20 @@ $time_form->succ_href = "/contests";
|
||||
$time_form->runAtServer();
|
||||
?>
|
||||
|
||||
<?php echoUOJPageHeader('添加比赛') ?>
|
||||
<?php echoUOJPageHeader(UOJLocale::get('contests::add new contest')) ?>
|
||||
|
||||
<div class="row">
|
||||
<!-- left col -->
|
||||
<div class="col-lg-9">
|
||||
<div class="card card-default mb-2">
|
||||
<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">
|
||||
<?php $time_form->printHTML(); ?>
|
||||
<?php $time_form->printHTML() ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -101,6 +102,7 @@ $time_form->runAtServer();
|
||||
<aside class="col-lg-3 mt-3 mt-lg-0">
|
||||
<?php uojIncludeView('sidebar') ?>
|
||||
</aside>
|
||||
<!-- end right col -->
|
||||
</div>
|
||||
|
||||
<?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>
|
||||
|
||||
<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', {
|
||||
delay: 2000
|
||||
});
|
@ -1,9 +1,15 @@
|
||||
<?php
|
||||
requirePHPLib('form');
|
||||
requirePHPLib('form');
|
||||
|
||||
$forgot_form = new UOJBs4Form('forgot');
|
||||
$forgot_form->addInput('username', 'text', '用户名', '',
|
||||
function($username, &$vdata) {
|
||||
use Gregwar\Captcha\PhraseBuilder;
|
||||
|
||||
$forgot_form = new UOJBs4Form('forgot');
|
||||
$forgot_form->addInput(
|
||||
'username',
|
||||
'text',
|
||||
'用户名',
|
||||
'',
|
||||
function ($username, &$vdata) {
|
||||
if (!validateUsername($username)) {
|
||||
return '用户名不合法';
|
||||
}
|
||||
@ -14,46 +20,104 @@
|
||||
return '';
|
||||
},
|
||||
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'];
|
||||
$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_short = UOJConfig::$data['profile']['oj-name-short'];
|
||||
$sufs = base64url_encode($user['username'] . "." . md5($user['username'] . "+" . $password));
|
||||
$url = HTML::url("/reset-password", array('params' => array('p' => $sufs)));
|
||||
$check_code = md5($user['username'] . "+" . $password . '+' . UOJTime::$time_now_str);
|
||||
$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
|
||||
<base target="_blank" />
|
||||
|
||||
<p>{$user['username']}您好,</p>
|
||||
<p>您刚刚启用了{$oj_name_short}密码找回功能,请进入下面的链接重设您的密码:</p>
|
||||
<p><a href="$url">$url</a></p>
|
||||
<p>{$oj_name}</p>
|
||||
<p>{$name} 您好,</p>
|
||||
|
||||
<style type="text/css">
|
||||
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}
|
||||
pre {white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}
|
||||
</style>
|
||||
<p>您最近告知我们需要重置您在 {$oj_name_short} 上账号的密码。请访问以下链接:<a href="{$url}">{$url}</a> (如果无法点击链接,请试着复制链接并粘贴至浏览器中打开。)</p>
|
||||
<p>如果您没有请求重置密码,则忽略此信息。该链接将在 72 小时后自动过期失效。</p>
|
||||
|
||||
<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;
|
||||
|
||||
$mailer = UOJMail::noreply();
|
||||
$mailer->addAddress($user['email'], $user['username']);
|
||||
$mailer->Subject = $oj_name_short."密码找回";
|
||||
$mailer->Subject = $oj_name_short . " 密码找回";
|
||||
$mailer->msgHTML($html);
|
||||
if (!$mailer->send()) {
|
||||
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 {
|
||||
becomeMsgPage('<div class="text-center"><h2>邮件发送成功 <span class="glyphicon glyphicon-ok"></span></h2></div>');
|
||||
}
|
||||
};
|
||||
$forgot_form->submit_button_config['align'] = 'offset';
|
||||
DB::update([
|
||||
"update user_info",
|
||||
"set", [
|
||||
'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('找回密码') ?>
|
||||
<h2 class="page-header">找回密码</h2>
|
||||
<h4>请输入需要找回密码的用户名:</h4>
|
||||
<?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() ?>
|
||||
|
@ -49,7 +49,7 @@ UOJGroup::cur()->userCanView(Auth::user(), ['ensure' => true]);
|
||||
</h2>
|
||||
<?php if (UOJGroup::info('announcement')) : ?>
|
||||
<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>
|
||||
<?php else : ?>
|
||||
<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('td', ['class' => 'text-center'], $problem->info['id']);
|
||||
echo HTML::tag_begin('td');
|
||||
echo $problem->getLink();
|
||||
echo $problem->getLink(['with' => 'none']);
|
||||
if ($problem->info['is_hidden']) {
|
||||
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();
|
||||
|
||||
if (UOJList::userCanCreateList(Auth::user())) {
|
||||
$new_list_form = new UOJBs4Form('new_list');
|
||||
$new_list_form = new UOJForm('new_list');
|
||||
$new_list_form->handle = function () {
|
||||
DB::insert("insert into lists (title, is_hidden) values ('未命名题单', 1)");
|
||||
$id = DB::insert_id();
|
||||
DB::insert("insert into lists_contents (id, content, content_md) values ($id, '', '')");
|
||||
DB::insert([
|
||||
"insert into lists",
|
||||
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->submit_button_config['class_str'] = 'btn btn-primary';
|
||||
$new_list_form->submit_button_config['text'] = UOJLocale::get('problems::add new list');
|
||||
$new_list_form->submit_button_config['smart_confirm'] = '';
|
||||
$new_list_form->config['submit_container']['class'] = 'text-end';
|
||||
$new_list_form->config['submit_button']['class'] = 'btn btn-primary';
|
||||
$new_list_form->config['submit_button']['text'] = UOJLocale::get('problems::add new list');
|
||||
$new_list_form->config['confirm']['smart'] = true;
|
||||
$new_list_form->runAtServer();
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ if (UOJContest::cur()) {
|
||||
|
||||
$submission_requirement = UOJProblem::cur()->getSubmissionRequirement();
|
||||
$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) {
|
||||
global $is_participating;
|
||||
@ -193,7 +193,7 @@ if ($pre_submit_check_ret === true && !$no_more_submission) {
|
||||
'FS::randomAvailableSubmissionFileName',
|
||||
'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->runAtServer();
|
||||
}
|
||||
@ -385,16 +385,27 @@ if (UOJContest::cur()) {
|
||||
<div class="card mb-2">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span class="flex-shrink-0">上传者</span>
|
||||
<span><?= UOJProblem::cur()->getUploaderLink() ?></span>
|
||||
<span class="flex-shrink-0">
|
||||
<?= UOJLocale::get('problems::uploader') ?>
|
||||
</span>
|
||||
<span>
|
||||
<?= UOJProblem::cur()->getUploaderLink() ?>
|
||||
</span>
|
||||
</li>
|
||||
<?php if (!UOJContest::cur() || UOJContest::cur()->progress() >= CONTEST_FINISHED) : ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span class="flex-shrink-0">难度</span>
|
||||
<span><?= UOJProblem::cur()->getDifficultyHTML() ?></span>
|
||||
<span class="flex-shrink-0">
|
||||
<?= UOJLocale::get('problems::difficulty') ?>
|
||||
</span>
|
||||
<span>
|
||||
<?= UOJProblem::cur()->getDifficultyHTML() ?>
|
||||
</span>
|
||||
</li>
|
||||
<?php if (Auth::check()) : ?>
|
||||
<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()]]) ?>
|
||||
|
||||
<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>
|
||||
<?php endif ?>
|
||||
<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>
|
||||
<?php if (UOJProblem::info('is_hidden')) : ?>
|
||||
<a href="<?= HTML::url('/problems', ['params' => ['is_hidden' => 'on']]) ?>">
|
||||
@ -422,9 +435,14 @@ if (UOJContest::cur()) {
|
||||
<?php endforeach ?>
|
||||
</span>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span class="flex-shrink-0">评价</span>
|
||||
<span><?= UOJProblem::cur()->getZanBlock() ?></span>
|
||||
<span class="flex-shrink-0">
|
||||
<?= UOJLocale::get('appraisal') ?>
|
||||
</span>
|
||||
<span>
|
||||
<?= UOJProblem::cur()->getZanBlock() ?>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -73,6 +73,9 @@ EOD;
|
||||
])
|
||||
]);
|
||||
dataNewProblem($id);
|
||||
|
||||
redirectTo("/problem/{$id}/manage/statement");
|
||||
die();
|
||||
};
|
||||
$new_problem_form->submit_button_config['align'] = 'right';
|
||||
$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();
|
||||
|
||||
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 = [
|
||||
@ -262,18 +285,16 @@ $pag = new Paginator($pag_config);
|
||||
您当前无法为本题新增题解。
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<?php if (isset($quick_add_new_solution_form)) : ?>
|
||||
<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') ?>">
|
||||
快速新建文章
|
||||
</a>
|
||||
<div class="small text-muted mt-1">发布文章后,请返回本页输入博客 ID</div>
|
||||
<?php $quick_add_new_solution_form->printHTML() ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
||||
<?php uojIncludeView('sidebar'); ?>
|
||||
<!-- End right col -->
|
||||
<?php uojIncludeView('sidebar') ?>
|
||||
</aside>
|
||||
|
||||
<!-- end right col -->
|
||||
</div>
|
||||
|
||||
<?php echoUOJPageFooter() ?>
|
||||
|
@ -174,6 +174,85 @@ $difficulty_form->runAtServer();
|
||||
</ul>
|
||||
</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-header fw-bold">
|
||||
题目难度
|
||||
@ -183,7 +262,6 @@ $difficulty_form->runAtServer();
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
|
||||
<?php echoUOJPageFooter() ?>
|
||||
|
@ -1,39 +1,55 @@
|
||||
<?php
|
||||
if (!isset($_GET['p'])) {
|
||||
if (!isset($_GET['p'])) {
|
||||
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'])) {
|
||||
return '操作失败,无效密码';
|
||||
}
|
||||
if (!isset($username) || !validateUsername($username)) {
|
||||
return '不明错误';
|
||||
}
|
||||
if (!isset($check_code)) {
|
||||
return '不明错误';
|
||||
}
|
||||
|
||||
$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']);
|
||||
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';
|
||||
}
|
||||
if (isset($_POST['reset'])) {
|
||||
}
|
||||
if (isset($_POST['reset'])) {
|
||||
die(resetPassword());
|
||||
}
|
||||
?>
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
$REQUIRE_LIB['dialog'] = '';
|
||||
$REQUIRE_LIB['md5'] = '';
|
||||
?>
|
||||
$REQUIRE_LIB['dialog'] = '';
|
||||
$REQUIRE_LIB['md5'] = '';
|
||||
?>
|
||||
<?php echoUOJPageHeader('更改密码') ?>
|
||||
<h2 class="page-header">更改密码</h2>
|
||||
<form id="form-reset" class="form-horizontal">
|
||||
@ -53,40 +69,40 @@
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
function validateResetPwPost() {
|
||||
function validateResetPwPost() {
|
||||
var ok = true;
|
||||
ok &= getFormErrorAndShowHelp('password', validateSettingPassword);
|
||||
return ok;
|
||||
}
|
||||
$(document).ready(function() {
|
||||
}
|
||||
$(document).ready(function() {
|
||||
$('#form-reset').submit(function(e) {
|
||||
if (!validateResetPwPost()) {
|
||||
return false;
|
||||
}
|
||||
$.post(<?= json_encode($_SERVER['REQUEST_URI']) ?>, {
|
||||
reset : '',
|
||||
newPW : md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>")
|
||||
reset: '',
|
||||
newPW: md5($('#input-password').val(), "<?= getPasswordClientSalt() ?>")
|
||||
}, function(res) {
|
||||
if (res == 'ok') {
|
||||
BootstrapDialog.show({
|
||||
title : '提示',
|
||||
message : '密码更改成功',
|
||||
type : BootstrapDialog.TYPE_SUCCESS,
|
||||
title: '提示',
|
||||
message: '密码更改成功',
|
||||
type: BootstrapDialog.TYPE_SUCCESS,
|
||||
buttons: [{
|
||||
label: '好的',
|
||||
action: function(dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}],
|
||||
onhidden : function(dialog) {
|
||||
onhidden: function(dialog) {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
});
|
||||
} else {
|
||||
BootstrapDialog.show({
|
||||
title : '提示',
|
||||
message : res,
|
||||
type : BootstrapDialog.TYPE_DANGER,
|
||||
title: '提示',
|
||||
message: res,
|
||||
type: BootstrapDialog.TYPE_DANGER,
|
||||
buttons: [{
|
||||
label: '好的',
|
||||
action: function(dialog) {
|
||||
@ -98,6 +114,6 @@ $(document).ready(function() {
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php echoUOJPageFooter() ?>
|
||||
|
@ -201,7 +201,9 @@ $comments_pag = new Paginator([
|
||||
"order by id"
|
||||
]);
|
||||
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_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 .= '</tr>';
|
||||
|
||||
$parsedown = HTML::parsedown();
|
||||
$parsedown = HTML::parsedown(['username_with_color' => true]);
|
||||
$purifier = HTML::purifier_inline();
|
||||
|
||||
$print_row = function ($row) use ($parsedown, $purifier) {
|
||||
|
@ -168,7 +168,9 @@ if ($perm['manager_view']) {
|
||||
?>
|
||||
|
||||
<?php if ($perm['content'] || $perm['manager_view']) : ?>
|
||||
<div class="copy-button-container">
|
||||
<?php UOJSubmission::cur()->echoContent() ?>
|
||||
</div>
|
||||
|
||||
<?php if (isset($hack_form)) : ?>
|
||||
<p class="text-center">
|
||||
@ -221,17 +223,17 @@ if (UOJSubmission::cur()->hasJudged()) {
|
||||
?>
|
||||
|
||||
<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 endif ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (isset($rejudge_form)) : ?>
|
||||
<?php if (isset($rejudge_form)) : ?>
|
||||
<?php $rejudge_form->printHTML() ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (isset($delete_form)) : ?>
|
||||
<?php if (isset($delete_form)) : ?>
|
||||
<?php $delete_form->printHTML() ?>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
||||
<?php echoUOJPageFooter() ?>
|
||||
|
@ -516,7 +516,7 @@ EOD);
|
||||
'new_tmp_expiration_time',
|
||||
'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) {
|
||||
try {
|
||||
$vdata['expiration_time'] = new DateTime($str);
|
||||
@ -657,6 +657,7 @@ EOD);
|
||||
);
|
||||
$change_usergroup_form->addVSelect('op_type', [
|
||||
'banneduser' => '设为封禁用户',
|
||||
'tempuser' => '设为临时用户',
|
||||
'normaluser' => '设为普通用户',
|
||||
'superuser' => '设为超级用户',
|
||||
], '操作类型', '');
|
||||
@ -666,15 +667,59 @@ EOD);
|
||||
|
||||
switch ($_POST['op_type']) {
|
||||
case 'banneduser':
|
||||
DB::update("update user_info set usergroup = 'B', usertype = 'banned' where username = '{$username}'");
|
||||
$usergroup = '被封禁的用户';
|
||||
DB::update([
|
||||
"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;
|
||||
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 = '普通用户';
|
||||
break;
|
||||
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 = '超级用户';
|
||||
break;
|
||||
}
|
||||
@ -1336,7 +1381,7 @@ EOD);
|
||||
EOD,
|
||||
function ($row) {
|
||||
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>';
|
||||
switch ($row['usergroup']) {
|
||||
@ -1429,7 +1474,8 @@ EOD);
|
||||
<h5>注意事项</h5>
|
||||
<ul class="mb-0">
|
||||
<li>用户被封禁后将不能再次登录系统。</li>
|
||||
<li>将当前用户移除权限后将无法再次访问本页面。</li>
|
||||
<li>将用户修改为临时用户后,请前往个人信息编辑页面修改过期时间。</li>
|
||||
<li>将当前用户移除管理权限后将无法再次访问本页面。</li>
|
||||
<li>在修改用户类别前请仔细核对用户名以免产生不必要的麻烦。</li>
|
||||
<li>如需为用户设置题目上传者、题目管理员等权限,请前往对应用户的个人资料编辑页面,点击「特权」选项卡修改。</li>
|
||||
</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) {
|
||||
$data = [
|
||||
'email' => $vdata['email'],
|
||||
@ -301,7 +338,9 @@ EOD);
|
||||
'$.social.codeforces',
|
||||
$vdata['codeforces'],
|
||||
'$.social.website',
|
||||
$vdata['website']
|
||||
$vdata['website'],
|
||||
'$.username_color',
|
||||
$_POST['username_color']
|
||||
),
|
||||
],
|
||||
"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', ['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->addCheckbox('problems__view', [
|
||||
'checked' => $extra['permissions']['problems']['view'],
|
||||
@ -707,6 +757,7 @@ EOD);
|
||||
DB::update([
|
||||
"update user_info",
|
||||
"set", [
|
||||
"usertype" => $_POST['user_type'],
|
||||
"extra" => json_encode($extra),
|
||||
],
|
||||
"where", [
|
||||
|
@ -87,14 +87,23 @@ function queryContestData($contest, $config = []) {
|
||||
$people = [];
|
||||
|
||||
if ($contest['extra_config']['individual_or_team'] == 'individual') {
|
||||
$people = DB::selectAll([
|
||||
"select contests_registrants.username, user_info.realname from contests_registrants",
|
||||
$res = DB::selectAll([
|
||||
"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",
|
||||
"where", [
|
||||
"contest_id" => $contest['id'],
|
||||
"has_participated" => 1
|
||||
]
|
||||
], 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') {
|
||||
$res = DB::selectAll([
|
||||
"select user_info.username, null, user_info.extra from contests_registrants, user_info",
|
||||
@ -106,11 +115,15 @@ function queryContestData($contest, $config = []) {
|
||||
], DB::NUM);
|
||||
foreach ($res as $row) {
|
||||
$extra = json_decode($row[2], true);
|
||||
$row[2] = [
|
||||
$people[] = [
|
||||
$row[0],
|
||||
null,
|
||||
[
|
||||
'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 = [];
|
||||
foreach ($contest_data['people'] as $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']);
|
||||
|
||||
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) {
|
||||
$zip_file->close();
|
||||
unlink(UOJContext::storagePath() . $zip_file_name);
|
||||
|
@ -1009,62 +1009,6 @@ function echoUOJPageFooter($config = array()) {
|
||||
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 =====
|
||||
|
||||
function echoJudgmentDetails($raw_details, $styler, $name) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
return [
|
||||
'new contest' => 'New Contest',
|
||||
'current or upcoming contests' => 'Current or upcoming contests',
|
||||
'ended contests' => 'Ended contests',
|
||||
'back to the contest' => 'Back to the contest',
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
return [
|
||||
'new contest' => '新建比赛',
|
||||
'current or upcoming contests' => '正在进行或即将到来的比赛',
|
||||
'ended contests' => '已结束的比赛',
|
||||
'back to the contest' => '返回比赛',
|
||||
|
@ -52,4 +52,7 @@ return [
|
||||
'hacks to me' => 'Hacks to me',
|
||||
'difficulty' => 'Difficulty',
|
||||
'show difficulty' => 'Show difficulty',
|
||||
'tags' => 'Tags',
|
||||
'historical score' => 'Historical Score',
|
||||
'uploader' => 'Uploader',
|
||||
];
|
||||
|
@ -52,4 +52,7 @@ return [
|
||||
'hacks to me' => '我的被Hack记录',
|
||||
'difficulty' => '难度',
|
||||
'show difficulty' => '显示难度',
|
||||
'tags' => '标签',
|
||||
'historical score' => '历史分数',
|
||||
'uploader' => '上传者',
|
||||
];
|
||||
|
@ -436,7 +436,11 @@ class HTML {
|
||||
$def->addElement('footer', 'Block', 'Flow', 'Common');
|
||||
|
||||
$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'],
|
||||
];
|
||||
|
||||
@ -463,7 +467,11 @@ class HTML {
|
||||
'small' => [],
|
||||
'del' => [],
|
||||
'br' => [],
|
||||
'span' => ['data-realname' => 'Text', 'data-uoj-username' => 'Number'],
|
||||
'span' => [
|
||||
'class' => 'Enum#uoj-username',
|
||||
'data-realname' => 'Text',
|
||||
'data-color' => 'Color',
|
||||
],
|
||||
];
|
||||
|
||||
$allowed_elements = [];
|
||||
@ -490,8 +498,8 @@ class HTML {
|
||||
return new HTMLPurifier($config);
|
||||
}
|
||||
|
||||
public static function parsedown() {
|
||||
return new UOJMarkdown([
|
||||
public static function parsedown($config = []) {
|
||||
return new UOJMarkdown($config + [
|
||||
'math' => [
|
||||
'enabled' => true,
|
||||
'matchSingleDollar' => true
|
||||
|
@ -258,7 +258,7 @@ class UOJForm {
|
||||
'options' => [],
|
||||
'default_value' => '',
|
||||
'label' => '',
|
||||
'label_class' => 'form-check-label',
|
||||
'label_class' => 'form-label',
|
||||
'help' => '',
|
||||
'help_class' => 'form-text',
|
||||
'disabled' => false,
|
||||
|
@ -1,10 +1,12 @@
|
||||
<?php
|
||||
class UOJMarkdown extends ParsedownMath {
|
||||
public function __construct($options = '') {
|
||||
if (method_exists(get_parent_class(),"__construct")) {
|
||||
if (method_exists(get_parent_class(), "__construct")) {
|
||||
parent::__construct($options);
|
||||
}
|
||||
|
||||
$this->options['username_with_color'] = $options['username_with_color'] ?: false;
|
||||
|
||||
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
|
||||
$this->InlineTypes['@'][] = 'UserMention';
|
||||
$this->inlineMarkerList .= '@';
|
||||
@ -54,16 +56,24 @@ class UOJMarkdown extends ParsedownMath {
|
||||
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
|
||||
protected function inlineUserMention($Excerpt) {
|
||||
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 [
|
||||
'extent' => strlen($matches[0]),
|
||||
'element' => [
|
||||
'name' => 'span',
|
||||
'text' => '@' . $user['username'],
|
||||
'text' => '@' . $mentioned_user['username'],
|
||||
'attributes' => [
|
||||
'class' => 'uoj-username',
|
||||
'data-realname' => $user['realname'],
|
||||
'data-uoj-username' => 1,
|
||||
'data-realname' => UOJUser::getRealname($mentioned_user),
|
||||
'data-color' => $color,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
@ -22,6 +22,7 @@ class UOJProblem {
|
||||
2300,
|
||||
2400,
|
||||
2500,
|
||||
2600,
|
||||
2700,
|
||||
2900,
|
||||
3100,
|
||||
@ -43,13 +44,254 @@ class UOJProblem {
|
||||
2300 => '#ff8000',
|
||||
2400 => '#ff8000',
|
||||
2500 => '#ff8000',
|
||||
2600 => '#ff0000',
|
||||
2700 => '#ff0000',
|
||||
2900 => '#ff0000',
|
||||
3100 => '#ff0000',
|
||||
3100 => '#aa0000',
|
||||
3300 => '#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) {
|
||||
if (!isset($id) || !validateUInt($id)) {
|
||||
return null;
|
||||
|
@ -22,7 +22,7 @@ class UOJRanklist {
|
||||
}
|
||||
|
||||
$last_user = null;
|
||||
$parsedown = HTML::parsedown();
|
||||
$parsedown = HTML::parsedown(['username_with_color' => true]);
|
||||
$purifier = HTML::purifier_inline();
|
||||
$print_row = function ($user, $now_cnt) use (&$last_user, &$conds, &$parsedown, &$purifier) {
|
||||
if ($last_user === null) {
|
||||
@ -138,7 +138,7 @@ class UOJRanklist {
|
||||
$header_row .= '</tr>';
|
||||
|
||||
$last_user = null;
|
||||
$parsedown = HTML::parsedown();
|
||||
$parsedown = HTML::parsedown(['username_with_color' => true]);
|
||||
$purifier = HTML::purifier_inline();
|
||||
$print_row = function ($user, $now_cnt) use (&$last_user, &$conds, &$parsedown, &$purifier) {
|
||||
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) {
|
||||
if (is_string($user)) {
|
||||
$info = UOJUser::query($user);
|
||||
@ -221,14 +258,18 @@ class UOJUser {
|
||||
}
|
||||
}
|
||||
|
||||
if ($user['usergroup'] == 'B') {
|
||||
return HTML::tag('a', ['class' => 'text-danger fw-bold', 'href' => "/user/{$user['username']}"], $user['username']);
|
||||
}
|
||||
$realname = UOJUser::getRealname($user);
|
||||
|
||||
// 未登录不可查看真实姓名
|
||||
$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) {
|
||||
@ -290,6 +331,7 @@ class UOJUser {
|
||||
'show_email' => 'all',
|
||||
'show_qq' => 'all',
|
||||
'avatar_source' => 'gravatar',
|
||||
'username_color' => isSuperUser($user) ? '#9d3dcf' : '#0d6efd',
|
||||
]);
|
||||
return $extra;
|
||||
}
|
||||
@ -340,6 +382,7 @@ class UOJUser {
|
||||
$extra = UOJUser::getExtra($user);
|
||||
$cur = [
|
||||
'addr' => $info['remote_addr'],
|
||||
'forwarded_addr' => $info['http_x_forwarded_for'],
|
||||
'ua' => substr($info['http_user_agent'], 0, UOJUser::MAX_UA_LEN),
|
||||
'last' => UOJTime::$time_now_str
|
||||
];
|
||||
|
@ -92,9 +92,9 @@ Route::group(
|
||||
Route::any('/click-zan', '/click_zan.php');
|
||||
|
||||
// Apps
|
||||
Route::any('/image_hosting', '/image_hosting/index.php');
|
||||
Route::get('/image_hosting/{image_name}.png', '/image_hosting/get_image.php');
|
||||
Route::any('/html2markdown', '/html2markdown.php');
|
||||
Route::any('/image_hosting', '/app/image_hosting/index.php');
|
||||
Route::get('/image_hosting/{image_name}.png', '/app/image_hosting/get_image.php');
|
||||
Route::any('/html2markdown', '/app/html2markdown.php');
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -19,6 +19,8 @@ class Parsedown
|
||||
|
||||
const version = '1.7.4';
|
||||
|
||||
protected $options = [];
|
||||
|
||||
# ~
|
||||
|
||||
function text($text)
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
$reviews = [];
|
||||
|
||||
$parsedown = HTML::parsedown();
|
||||
$parsedown = HTML::parsedown(['username_with_color' => true]);
|
||||
$purifier = HTML::purifier_inline();
|
||||
|
||||
foreach ($contest_data['people'] as $person) {
|
||||
|
@ -35,7 +35,7 @@
|
||||
function(row) {
|
||||
var col_tr = '<tr>';
|
||||
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>';
|
||||
for (var i = 0; i < problems.length; i++) {
|
||||
col = score[row[2][0]][i];
|
||||
|
@ -4,11 +4,6 @@ if (!isset($ShowPageFooter)) {
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
[...document.querySelectorAll('[data-bs-toggle="tooltip"]')].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
|
||||
});
|
||||
</script>
|
||||
<?php if ($ShowPageFooter) : ?>
|
||||
<?php if (UOJNotice::shouldConstantlyCheckNotice()) : ?>
|
||||
<script type="text/javascript">
|
||||
|
@ -102,6 +102,9 @@ if (!isset($ShowPageHeader)) {
|
||||
<!-- Color converter -->
|
||||
<?= HTML::js_src('/js/color-converter.min.js') ?>
|
||||
|
||||
<!-- Clipboard Polyfill -->
|
||||
<?= HTML::js_src('/js/clipboard-polyfill.overwrite-globals.es5.min.js') ?>
|
||||
|
||||
<!-- uoj -->
|
||||
<?= HTML::js_src('/js/uoj.js?v=' . UOJConfig::$data['profile']['s2oj-version']) ?>
|
||||
|
||||
@ -175,7 +178,7 @@ if (!isset($ShowPageHeader)) {
|
||||
}
|
||||
};
|
||||
</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 if (isset($REQUIRE_LIB['jquery.form'])) : ?>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
$purifier = HTML::purifier_inline();
|
||||
$parsedown = HTML::parsedown();
|
||||
$parsedown = HTML::parsedown(['username_with_color' => true]);
|
||||
?>
|
||||
|
||||
<?php if (Auth::check()) : ?>
|
||||
|
@ -21,7 +21,7 @@
|
||||
</span>
|
||||
</h3>
|
||||
<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>
|
||||
<ul class="list-group list-group-flush">
|
||||
|
@ -25,6 +25,11 @@ a {
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
|
||||
.uoj-realname {
|
||||
font-size: 90%;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h1,
|
||||
.h1 {
|
||||
/* font-size: 2.5rem; */
|
||||
@ -164,7 +169,7 @@ h6,
|
||||
|
||||
.card-uoj-tle > .card-header:hover,
|
||||
.card-uoj-tle > div.card-header > div > .uoj-status-text {
|
||||
color: sandybrown;
|
||||
color: #f4a460;
|
||||
}
|
||||
|
||||
.card-uoj-wrong > .card-header:hover,
|
||||
|
@ -60,14 +60,6 @@ setWebConf(){
|
||||
# Set webroot path
|
||||
ln -sf /opt/uoj/web /var/www/uoj
|
||||
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
|
||||
cd /opt/uoj/judger/uoj_judger
|
||||
cat >include/uoj_work_path.h <<UOJEOF
|
||||
@ -84,9 +76,6 @@ initProgress(){
|
||||
#Set uoj_data path
|
||||
mkdir -p /var/uoj_data/upload
|
||||
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
|
||||
service ntp 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