feat(web,db): add groups

This commit is contained in:
Baoshuo Ren 2022-03-20 08:07:46 +08:00
parent ee13bbcc29
commit 8703281ab3
Signed by: baoshuo
GPG Key ID: 00CB9680AB29F51A
12 changed files with 340 additions and 5 deletions

View File

@ -1,4 +1,4 @@
FROM mysql:latest FROM mysql:5.7
ADD . /opt/uoj_db ADD . /opt/uoj_db
WORKDIR /opt/uoj_db WORKDIR /opt/uoj_db

View File

@ -361,6 +361,33 @@ LOCK TABLES `custom_test_submissions` WRITE;
/*!40000 ALTER TABLE `custom_test_submissions` ENABLE KEYS */; /*!40000 ALTER TABLE `custom_test_submissions` ENABLE KEYS */;
UNLOCK TABLES; UNLOCK TABLES;
--
-- Table structure for table `groups`
--
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `groups` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` text NOT NULL,
`is_hidden` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `groups_users`
--
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8mb4 */;
CREATE TABLE `groups_users` (
`group_id` int(11) NOT NULL,
`username` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`group_id`, `username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
-- --
-- Table structure for table `hacks` -- Table structure for table `hacks`
-- --

View File

@ -10,6 +10,68 @@
} }
genMoreContestInfo($contest); genMoreContestInfo($contest);
if (isSuperUser($myUser)) {
$add_new_contestant_form = new UOJForm('add_new_contestant_form');
$add_new_contestant_form->addInput('new_username', 'text', '用户名', '',
function ($x) {
global $contest;
if (!validateUsername($x)) return '用户名不合法';
$user = queryUser($x);
if (!$user) return '用户不存在';
if (hasRegistered($user, $contest)) {
return '该用户已经报名';
}
return '';
},
null
);
$add_new_contestant_form->submit_button_config['align'] = 'compressed';
$add_new_contestant_form->submit_button_config['text'] = '注册该用户';
$add_new_contestant_form->handle = function() {
global $contest;
$username = $_POST['new_username'];
$user = queryUser($username);
if (!$user) return;
DB::query("replace into contests_registrants (username, user_rating, contest_id, has_participated) values ('{$user['username']}', {$user['rating']}, {$contest['id']}, 0)");
updateContestPlayerNum($contest);
};
$add_new_contestant_form->runAtServer();
$add_group_to_contest_form = new UOJForm('add_group_to_contest');
$add_group_to_contest_form->addInput('group_id', 'text', '小组 ID', '',
function ($x) {
global $contest;
if (!validateUInt($x)) return '小组 ID 不合法';
$group = queryGroup($x);
if (!$group) return '小组不存在';
return '';
},
null
);
$add_group_to_contest_form->submit_button_config['align'] = 'compressed';
$add_group_to_contest_form->submit_button_config['text'] = '注册该小组中的用户';
$add_group_to_contest_form->handle = function() {
global $contest;
$group_id = $_POST['group_id'];
$users = DB::selectAll("select b.username as username, b.rating as rating from groups_users a inner join user_info b on a.username = b.username where a.group_id = $group_id");
foreach ($users as $user) {
DB::query("replace into contests_registrants (username, user_rating, contest_id, has_participated) values ('{$user['username']}', {$user['rating']}, {$contest['id']}, 0)");
}
updateContestPlayerNum($contest);
};
$add_group_to_contest_form->runAtServer();
}
$has_contest_permission = hasContestPermission($myUser, $contest); $has_contest_permission = hasContestPermission($myUser, $contest);
$show_ip = $has_contest_permission; $show_ip = $has_contest_permission;
@ -86,6 +148,14 @@
array('page_len' => 100, array('page_len' => 100,
'get_row_index' => '', 'get_row_index' => '',
'print_after_table' => function() { 'print_after_table' => function() {
global $add_new_contestant_form, $add_group_to_contest_form;
if (isset($add_new_contestant_form)) {
$add_new_contestant_form->printHTML();
}
if (isset($add_group_to_contest_form)) {
$add_group_to_contest_form->printHTML();
}
} }
) )
); );

View File

@ -15,7 +15,7 @@
$register_form = new UOJForm('register'); $register_form = new UOJForm('register');
$register_form->handle = function() { $register_form->handle = function() {
global $myUser, $contest; global $myUser, $contest;
DB::query("insert into contests_registrants (username, contest_id, has_participated) values ('{$myUser['username']}', {$contest['id']}, 0)"); DB::query("replace into contests_registrants (username, contest_id, has_participated) values ('{$myUser['username']}', {$contest['id']}, 0)");
updateContestPlayerNum($contest); updateContestPlayerNum($contest);
}; };
$register_form->submit_button_config['class_str'] = 'btn btn-primary'; $register_form->submit_button_config['class_str'] = 'btn btn-primary';
@ -28,11 +28,11 @@
<h1 class="page-header">比赛规则</h1> <h1 class="page-header">比赛规则</h1>
<ul> <ul>
<li>比赛报名后不算正式参赛,报名后进了比赛页面也不算参赛,<strong>看了题目才算正式参赛</strong></li> <li>比赛报名后不算正式参赛,报名后进了比赛页面也不算参赛,<strong>看了题目才算正式参赛</strong></li>
<li>比赛中途可以提交,若同一题有多次提交按<strong>最后一次不是Compile Error的提交</strong>算成绩。其实UOJ会自动无视你所有Compile Error的提交当作没看见</li> <li>比赛中途可以提交,若同一题有多次提交按<strong>最后一次不是 Compile Error 的提交</strong>算成绩。其实UOJ会自动无视你所有 Compile Error 的提交)</li>
<li>比赛中途提交后,可以看到<strong>测样例</strong>的结果。(若为提交答案题则对于每个测试点,该测试点有分则该测试点为满分)</li> <li>比赛中途提交后,可以看到<strong>测样例</strong>的结果。(若为提交答案题则对于每个测试点,该测试点有分则该测试点为满分)</li>
<li>比赛结束后会进行最终测试,最终测试后的排名为最终排名。</li> <li>比赛结束后会进行最终测试,最终测试后的排名为最终排名。</li>
<li>比赛排名按分数为第一关键字,完成题目的总时间为第二关键字。完成题目的总时间等于完成每道题所花时间之和(无视掉爆零的题目)。</li> <li>比赛排名按分数为第一关键字,完成题目的总时间为第二关键字。完成题目的总时间等于完成每道题所花时间之和(无视掉爆零的题目)。</li>
<li>请遵守比赛规则,一位选手在一场比赛内不得报名多个账号,选手之间不能交流或者抄袭代码,如果被检测到将以0分处理或者封禁。</li> <li>请遵守比赛规则,一位选手在一场比赛内不得报名多个账号,选手之间不能交流或者抄袭代码,如果被检测到将以 0 分处理或者封禁。</li>
</ul> </ul>
<?php $register_form->printHTML(); ?> <?php $register_form->printHTML(); ?>
<?php echoUOJPageFooter() ?> <?php echoUOJPageFooter() ?>

View File

@ -0,0 +1,128 @@
<?php
requirePHPLib('form');
requirePHPLib('judger');
requirePHPLib('data');
$group_id = $_GET['id'];
$group = queryGroup($group_id);
if (isSuperUser($myUser)) {
$group_editor = new UOJBlogEditor();
$group_editor->name = 'group';
$group_editor->blog_url = null;
$group_editor->cur_data = array(
'title' => $group['title'],
'tags' => array(),
'is_hidden' => $group['is_hidden']
);
$group_editor->label_text = array_merge($group_editor->label_text, array(
'view blog' => '保存小组信息',
'blog visibility' => '小组可见性'
));
$group_editor->show_editor = false;
$group_editor->show_tags = false;
$group_editor->save = function($data) {
global $group_id, $group;
DB::update("update `groups` set title = '".DB::escape($data['title'])."' where id = {$group_id}");
if ($data['is_hidden'] != $group['is_hidden'] ) {
DB::update("update `groups` set is_hidden = {$data['is_hidden']} where id = {$group_id}");
}
};
$group_editor->runAtServer();
$add_new_user_form = new UOJForm('add_new_user');
$add_new_user_form->addInput('new_username', 'text', '用户名', '',
function ($x) {
global $group_id;
if (!validateUsername($x)) return '用户名不合法';
$user = queryUser($x);
if (!$user) return '用户不存在';
if (queryUserInGroup($group_id, $x)) {
return '该用户已经在小组中';
}
return '';
},
null
);
$add_new_user_form->submit_button_config['align'] = 'compressed';
$add_new_user_form->submit_button_config['text'] = '添加到小组';
$add_new_user_form->handle = function() {
global $group_id, $myUser;
$username = $_POST['new_username'];
DB::insert("insert into groups_users (group_id, username) values ({$group_id}, '{$username}')");
};
$add_new_user_form->runAtServer();
$delete_user_form = new UOJForm('delete_user');
$delete_user_form->addInput('del_username', 'text', '用户名', '',
function ($x) {
global $group_id;
if (!validateUsername($x)) return '用户名不合法';
if (!queryUserInGroup($group_id, $x)) {
return '该用户不在小组中';
}
return '';
},
null
);
$delete_user_form->submit_button_config['align'] = 'compressed';
$delete_user_form->submit_button_config['text'] = '从小组中删除';
$delete_user_form->handle = function() {
global $group_id, $myUser;
$username = $_POST['del_username'];
DB::query("delete from groups_users where username='{$username}' and group_id={$group_id}");
};
$delete_user_form->runAtServer();
}
?>
<?php echoUOJPageHeader(UOJLocale::get('groups')) ?>
<h2 style="margin-top: 24px"><?= $group['title'] ?></h2>
<p>(<b>小组 ID</b>: <?= $group['id'] ?>)</p>
<div class="row">
<div class="col-sm-12 mt-4">
<h5><?= UOJLocale::get('news') ?></h5>
<ul>
<?php
$current_ac = queryGroupCurrentAC($group['id']);
foreach ($current_ac as $ac) {
echo '<li>';
echo getUserLink($ac['submitter']);
echo ' 解决了问题 ';
echo '<a href="/problem/', $ac['problem_id'], '">', $ac['problem_title'], '</a> ';
echo '<time class="time">(', $ac['submit_time'], ')</time>';
echo '</li>';
}
if (!$current_ac) {
echo '(无)';
}
?>
</ul>
</div>
</div>
<?php if (isSuperUser($myUser)): ?>
<h5>编辑小组信息</h5>
<div class="mb-4">
<?php $group_editor->printHTML(); ?>
</div>
<h5>添加用户到小组</h5>
<?php $add_new_user_form->printHTML(); ?>
<h5>从小组中删除用户</h5>
<?php $delete_user_form->printHTML(); ?>
<?php endif ?>
<?php echoUOJPageFooter() ?>

View File

@ -0,0 +1,78 @@
<?php
requirePHPLib('form');
requirePHPLib('judger');
requirePHPLib('data');
if (isSuperUser($myUser)) {
$new_group_form = new UOJForm('new_group');
$new_group_form->handle = function() {
DB::query("insert into `groups` (title, is_hidden) values ('新小组', 1)");
};
$new_group_form->submit_button_config['align'] = 'right';
$new_group_form->submit_button_config['class_str'] = 'btn btn-primary';
$new_group_form->submit_button_config['text'] = UOJLocale::get('add new group');
$new_group_form->submit_button_config['smart_confirm'] = '';
$new_group_form->runAtServer();
}
function echoGroup($group) {
global $myUser;
echo '<tr class="text-center">';
echo '<td>';
echo '#', $group['group_id'], '</td>';
echo '<td class="text-left">';
if ($group['is_hidden']) {
echo ' <span class="text-danger">[隐藏]</span> ';
}
echo '<a href="/group/', $group['group_id'], '">', $group['title'], '</a>';
echo '</td>';
echo "<td>{$group['user_count']}</td>";
echo '</tr>';
}
?>
<?php echoUOJPageHeader(UOJLocale::get('groups')) ?>
<?php
$groups_caption = UOJLocale::get('groups');
$users_caption = UOJLocale::get('users count');
$header = <<<EOD
<tr>
<th class="text-center" style="width:5em;">ID</th>
<th>{$groups_caption}</th>
<th class="text-center" style="width:8em;">{$users_caption}</th>
</tr>
EOD;
if (isSuperUser($myUser)) {
$cond = "1";
} else {
$cond = 'is_hidden = 0';
}
$from = "`groups` a left join groups_users b on a.id = b.group_id";
echoLongTable(
array('a.id as group_id', 'a.title as title', 'a.is_hidden as is_hidden', 'count(b.username) as user_count'),
$from, $cond, 'group by a.id order by a.id asc',
$header,
'echoGroup',
array('page_len' => 100,
'table_classes' => array('table', 'table-bordered', 'table-hover', 'table-striped'),
'print_after_table' => function() {
global $myUser;
if (isSuperUser($myUser)) {
global $new_group_form;
$new_group_form->printHTML();
}
},
'head_pagination' => true
)
);
?>
<?php echoUOJPageFooter() ?>

View File

@ -82,6 +82,22 @@ function queryContestProblem($id) {
return DB::selectFirst("select * from contest_problems where contest_id = $id", MYSQLI_ASSOC); return DB::selectFirst("select * from contest_problems where contest_id = $id", MYSQLI_ASSOC);
} }
function queryGroup($id) {
return DB::selectFirst("select * from groups where id = $id", MYSQLI_ASSOC);
}
function queryUserInGroup($group_id, $username) {
return DB::selectFirst("select * from groups_users where username='$username' and group_id='$group_id'", MYSQLI_ASSOC);
}
function queryGroupOfUser($username) {
return DB::selectAll("select b.title as title, b.id as id from groups_users a inner join groups b on a.group_id = b.id where a.username = '$username' and b.is_hidden = 0 order by id", MYSQLI_ASSOC);
}
function queryGroupmateCurrentAC($username) {
return DB::selectAll("select a.problem_id as problem_id, a.submitter as submitter, a.submission_id as submission_id, b.submit_time as submit_time, c.group_id as group_id, c.group_name as group_name, d.title as problem_title, b.submit_time as submit_time, e.realname as realname from best_ac_submissions a inner join submissions b on (a.submission_id = b.id) inner join (select a.username as username, any_value(a.group_id) as group_id, any_value(c.title) as group_name from groups_users a inner join (select a.group_id as group_id from groups_users a inner join groups b on a.group_id = b.id where a.username = '$username' and b.is_hidden = 0) b on a.group_id = b.group_id inner join groups c on a.group_id = c.id group by a.username) c on a.submitter = c.username inner join problems d on (a.problem_id = d.id and d.is_hidden = 0) inner join user_info e on a.submitter = e.username where b.submit_time > addtime(now(), '-360:00:00') order by b.submit_time desc limit 10", MYSQLI_ASSOC);
}
function queryGroupCurrentAC($group_id) {
return DB::selectAll("select a.problem_id as problem_id, a.submitter as submitter, a.submission_id as submission_id, b.submit_time as submit_time, d.title as problem_title, b.submit_time as submit_time, e.realname as realname from best_ac_submissions a inner join submissions b on (a.submission_id = b.id) inner join groups_users c on (a.submitter = c.username and c.group_id = $group_id) inner join problems d on (a.problem_id = d.id and d.is_hidden = 0) inner join user_info e on (a.submitter = e.username) where b.submit_time > addtime(now(), '-360:00:00') order by b.submit_time desc limit 10", MYSQLI_ASSOC);
}
function queryZanVal($id, $type, $user) { function queryZanVal($id, $type, $user) {
if ($user == null) { if ($user == null) {
return 0; return 0;

View File

@ -11,6 +11,9 @@ return [
'system manage' => 'System Manage', 'system manage' => 'System Manage',
'contests' => 'Contests', 'contests' => 'Contests',
'problems' => 'Problems', 'problems' => 'Problems',
'groups' => 'Groups',
'add new group' => 'Add new group',
'users count' => 'Users',
'submissions' => 'Submissions', 'submissions' => 'Submissions',
'hacks' => 'Hack!', 'hacks' => 'Hack!',
'blogs' => 'Blogs', 'blogs' => 'Blogs',
@ -18,6 +21,7 @@ return [
'all the announcements' => 'All the Announcements……', 'all the announcements' => 'All the Announcements……',
'help' => 'Help', 'help' => 'Help',
'search' => 'Search', 'search' => 'Search',
'news' => 'News',
'username' => 'Username', 'username' => 'Username',
'password' => 'Password', 'password' => 'Password',
'new password' => 'New password', 'new password' => 'New password',

View File

@ -11,6 +11,9 @@ return [
'system manage' => '系统管理', 'system manage' => '系统管理',
'contests' => '比赛', 'contests' => '比赛',
'problems' => '题库', 'problems' => '题库',
'groups' => '小组',
'add new group' => '添加新小组',
'users count' => '用户数量',
'submissions' => '提交记录', 'submissions' => '提交记录',
'hacks' => 'Hack!', 'hacks' => 'Hack!',
'blogs' => '博客', 'blogs' => '博客',
@ -18,6 +21,7 @@ return [
'all the announcements' => '所有公告……', 'all the announcements' => '所有公告……',
'help' => '帮助', 'help' => '帮助',
'search' => '搜索', 'search' => '搜索',
'news' => '最新动态',
'username' => '用户名', 'username' => '用户名',
'password' => '密码', 'password' => '密码',
'new password' => '新密码', 'new password' => '新密码',

View File

@ -38,6 +38,9 @@ Route::group([
Route::any('/hacks', '/hack_list.php'); Route::any('/hacks', '/hack_list.php');
Route::any('/hack/{id}', '/hack.php'); Route::any('/hack/{id}', '/hack.php');
Route::any('/groups', '/groups.php');
Route::any('/group/{id}', '/group.php');
Route::any('/blogs', '/blogs.php'); Route::any('/blogs', '/blogs.php');
if (UOJConfig::$data['switch']['blog-domain-mode'] != 3) { if (UOJConfig::$data['switch']['blog-domain-mode'] != 3) {
Route::any('/blog/{id}', '/blog_show.php'); Route::any('/blog/{id}', '/blog_show.php');

View File

@ -4,11 +4,15 @@
<div class="col-sm-6"> <div class="col-sm-6">
<?= HTML::div_vinput("{$editor->name}_title", 'text', $editor->label_text['title'], html_entity_decode($editor->cur_data['title'])) ?> <?= HTML::div_vinput("{$editor->name}_title", 'text', $editor->label_text['title'], html_entity_decode($editor->cur_data['title'])) ?>
</div> </div>
<?php if ($editor->show_tags): ?>
<div class="col-sm-6"> <div class="col-sm-6">
<?= HTML::div_vinput("{$editor->name}_tags", 'text', $editor->label_text['tags'], join(', ', $editor->cur_data['tags'])) ?> <?= HTML::div_vinput("{$editor->name}_tags", 'text', $editor->label_text['tags'], join(', ', $editor->cur_data['tags'])) ?>
</div> </div>
<?php endif ?>
</div> </div>
<?php if ($editor->show_editor): ?>
<?= HTML::div_vtextarea("{$editor->name}_content_md", $editor->label_text['content'], $editor->cur_data['content_md']) ?> <?= HTML::div_vtextarea("{$editor->name}_content_md", $editor->label_text['content'], $editor->cur_data['content_md']) ?>
<?php endif ?>
<div class="row mt-2"> <div class="row mt-2">
<div class="col-sm-6"> <div class="col-sm-6">
<?php if ($editor->blog_url): ?> <?php if ($editor->blog_url): ?>

View File

@ -7,6 +7,7 @@
<ul class="nav navbar-nav mr-auto"> <ul class="nav navbar-nav mr-auto">
<li class="nav-item"><a class="nav-link" href="<?= HTML::url('/contests') ?>"><span class="glyphicon glyphicon-stats"></span> <?= UOJLocale::get('contests') ?></a></li> <li class="nav-item"><a class="nav-link" href="<?= HTML::url('/contests') ?>"><span class="glyphicon glyphicon-stats"></span> <?= UOJLocale::get('contests') ?></a></li>
<li class="nav-item"><a class="nav-link" href="<?= HTML::url('/problems') ?>"><span class="glyphicon glyphicon-list-alt"></span> <?= UOJLocale::get('problems') ?></a></li> <li class="nav-item"><a class="nav-link" href="<?= HTML::url('/problems') ?>"><span class="glyphicon glyphicon-list-alt"></span> <?= UOJLocale::get('problems') ?></a></li>
<li class="nav-item"><a class="nav-link" href="<?= HTML::url('/groups') ?>"><span class="glyphicon glyphicon-education"></span> <?= UOJLocale::get('groups') ?></a></li>
<li class="nav-item"><a class="nav-link" href="<?= HTML::url('/submissions') ?>"><span class="glyphicon glyphicon-tasks"></span> <?= UOJLocale::get('submissions') ?></a></li> <li class="nav-item"><a class="nav-link" href="<?= HTML::url('/submissions') ?>"><span class="glyphicon glyphicon-tasks"></span> <?= UOJLocale::get('submissions') ?></a></li>
<li class="nav-item"><a class="nav-link" href="<?= HTML::url('/hacks') ?>"><span class="glyphicon glyphicon-flag"></span> <?= UOJLocale::get('hacks') ?></a></li> <li class="nav-item"><a class="nav-link" href="<?= HTML::url('/hacks') ?>"><span class="glyphicon glyphicon-flag"></span> <?= UOJLocale::get('hacks') ?></a></li>
<li class="nav-item"><a class="nav-link" href="<?= HTML::blog_list_url() ?>"><span class="glyphicon glyphicon-edit"></span> <?= UOJLocale::get('blogs') ?></a></li> <li class="nav-item"><a class="nav-link" href="<?= HTML::blog_list_url() ?>"><span class="glyphicon glyphicon-edit"></span> <?= UOJLocale::get('blogs') ?></a></li>