diff --git a/db/Dockerfile b/db/Dockerfile
index eb4c744..2ca033c 100644
--- a/db/Dockerfile
+++ b/db/Dockerfile
@@ -1,4 +1,4 @@
-FROM mysql:latest
+FROM mysql:5.7
ADD . /opt/uoj_db
WORKDIR /opt/uoj_db
diff --git a/db/app_uoj233.sql b/db/app_uoj233.sql
index 5e53a20..8d75f70 100644
--- a/db/app_uoj233.sql
+++ b/db/app_uoj233.sql
@@ -361,6 +361,33 @@ LOCK TABLES `custom_test_submissions` WRITE;
/*!40000 ALTER TABLE `custom_test_submissions` ENABLE KEYS */;
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`
--
diff --git a/web/app/controllers/contest_members.php b/web/app/controllers/contest_members.php
index eab9785..3daf9bd 100644
--- a/web/app/controllers/contest_members.php
+++ b/web/app/controllers/contest_members.php
@@ -10,6 +10,68 @@
}
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);
$show_ip = $has_contest_permission;
@@ -86,6 +148,14 @@
array('page_len' => 100,
'get_row_index' => '',
'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();
+ }
}
)
);
diff --git a/web/app/controllers/contest_registration.php b/web/app/controllers/contest_registration.php
index 462e5c1..34b2098 100644
--- a/web/app/controllers/contest_registration.php
+++ b/web/app/controllers/contest_registration.php
@@ -15,7 +15,7 @@
$register_form = new UOJForm('register');
$register_form->handle = function() {
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);
};
$register_form->submit_button_config['class_str'] = 'btn btn-primary';
@@ -28,11 +28,11 @@
- 比赛报名后不算正式参赛,报名后进了比赛页面也不算参赛,看了题目才算正式参赛。
- - 比赛中途可以提交,若同一题有多次提交按最后一次不是Compile Error的提交算成绩。(其实UOJ会自动无视你所有Compile Error的提交当作没看见)
+ - 比赛中途可以提交,若同一题有多次提交按最后一次不是 Compile Error 的提交算成绩。(其实UOJ会自动无视你所有 Compile Error 的提交)
- 比赛中途提交后,可以看到测样例的结果。(若为提交答案题则对于每个测试点,该测试点有分则该测试点为满分)
- 比赛结束后会进行最终测试,最终测试后的排名为最终排名。
- 比赛排名按分数为第一关键字,完成题目的总时间为第二关键字。完成题目的总时间等于完成每道题所花时间之和(无视掉爆零的题目)。
- - 请遵守比赛规则,一位选手在一场比赛内不得报名多个账号,选手之间不能交流或者抄袭代码,如果被检测到将以0分处理或者封禁。
+ - 请遵守比赛规则,一位选手在一场比赛内不得报名多个账号,选手之间不能交流或者抄袭代码,如果被检测到将以 0 分处理或者封禁。
printHTML(); ?>
diff --git a/web/app/controllers/group.php b/web/app/controllers/group.php
new file mode 100644
index 0000000..48f5817
--- /dev/null
+++ b/web/app/controllers/group.php
@@ -0,0 +1,128 @@
+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();
+ }
+?>
+
+
+
+= $group['title'] ?>
+(小组 ID: = $group['id'] ?>)
+
+
+
+
= UOJLocale::get('news') ?>
+
+ ';
+ echo getUserLink($ac['submitter']);
+ echo ' 解决了问题 ';
+ echo '', $ac['problem_title'], ' ';
+ echo '';
+ echo '';
+ }
+ if (!$current_ac) {
+ echo '(无)';
+ }
+ ?>
+
+
+
+
+
+编辑小组信息
+
+ printHTML(); ?>
+
+
+添加用户到小组
+printHTML(); ?>
+
+从小组中删除用户
+printHTML(); ?>
+
+
+
diff --git a/web/app/controllers/groups.php b/web/app/controllers/groups.php
new file mode 100644
index 0000000..2ae493c
--- /dev/null
+++ b/web/app/controllers/groups.php
@@ -0,0 +1,78 @@
+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 '';
+ echo '';
+ echo '#', $group['group_id'], ' | ';
+
+ echo '';
+ if ($group['is_hidden']) {
+ echo ' [隐藏] ';
+ }
+ echo '', $group['title'], '';
+ echo ' | ';
+
+ echo "{$group['user_count']} | ";
+
+ echo '
';
+ }
+?>
+
+
+
+
+ ID |
+ {$groups_caption} |
+ {$users_caption} |
+
+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
+ )
+ );
+?>
+
+
diff --git a/web/app/libs/uoj-query-lib.php b/web/app/libs/uoj-query-lib.php
index ddc8462..3364df8 100644
--- a/web/app/libs/uoj-query-lib.php
+++ b/web/app/libs/uoj-query-lib.php
@@ -82,6 +82,22 @@ function queryContestProblem($id) {
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) {
if ($user == null) {
return 0;
diff --git a/web/app/locale/basic/en.php b/web/app/locale/basic/en.php
index 582a1d6..5da1d8b 100644
--- a/web/app/locale/basic/en.php
+++ b/web/app/locale/basic/en.php
@@ -11,6 +11,9 @@ return [
'system manage' => 'System Manage',
'contests' => 'Contests',
'problems' => 'Problems',
+ 'groups' => 'Groups',
+ 'add new group' => 'Add new group',
+ 'users count' => 'Users',
'submissions' => 'Submissions',
'hacks' => 'Hack!',
'blogs' => 'Blogs',
@@ -18,6 +21,7 @@ return [
'all the announcements' => 'All the Announcements……',
'help' => 'Help',
'search' => 'Search',
+ 'news' => 'News',
'username' => 'Username',
'password' => 'Password',
'new password' => 'New password',
diff --git a/web/app/locale/basic/zh-cn.php b/web/app/locale/basic/zh-cn.php
index 7c8ce30..7b349fe 100644
--- a/web/app/locale/basic/zh-cn.php
+++ b/web/app/locale/basic/zh-cn.php
@@ -11,6 +11,9 @@ return [
'system manage' => '系统管理',
'contests' => '比赛',
'problems' => '题库',
+ 'groups' => '小组',
+ 'add new group' => '添加新小组',
+ 'users count' => '用户数量',
'submissions' => '提交记录',
'hacks' => 'Hack!',
'blogs' => '博客',
@@ -18,6 +21,7 @@ return [
'all the announcements' => '所有公告……',
'help' => '帮助',
'search' => '搜索',
+ 'news' => '最新动态',
'username' => '用户名',
'password' => '密码',
'new password' => '新密码',
diff --git a/web/app/route.php b/web/app/route.php
index 06fdb99..a71638a 100644
--- a/web/app/route.php
+++ b/web/app/route.php
@@ -37,6 +37,9 @@ Route::group([
Route::any('/hacks', '/hack_list.php');
Route::any('/hack/{id}', '/hack.php');
+
+ Route::any('/groups', '/groups.php');
+ Route::any('/group/{id}', '/group.php');
Route::any('/blogs', '/blogs.php');
if (UOJConfig::$data['switch']['blog-domain-mode'] != 3) {
diff --git a/web/app/views/blog-editor.php b/web/app/views/blog-editor.php
index 04f8b1b..44d889d 100644
--- a/web/app/views/blog-editor.php
+++ b/web/app/views/blog-editor.php
@@ -4,11 +4,15 @@
= HTML::div_vinput("{$editor->name}_title", 'text', $editor->label_text['title'], html_entity_decode($editor->cur_data['title'])) ?>
+ show_tags): ?>
= HTML::div_vinput("{$editor->name}_tags", 'text', $editor->label_text['tags'], join(', ', $editor->cur_data['tags'])) ?>
+
+show_editor): ?>
= HTML::div_vtextarea("{$editor->name}_content_md", $editor->label_text['content'], $editor->cur_data['content_md']) ?>
+
blog_url): ?>
diff --git a/web/app/views/main-nav.php b/web/app/views/main-nav.php
index 9edf904..06fde86 100644
--- a/web/app/views/main-nav.php
+++ b/web/app/views/main-nav.php
@@ -7,6 +7,7 @@